mirror of
https://github.com/zadam/trilium.git
synced 2025-12-29 03:29:58 +01:00
docs(user): improve documentation on custom widgets & Preact
This commit is contained in:
@@ -9,7 +9,7 @@ Render Note is used in <a class="reference-link" href="../Scripting.md">Scripti
|
||||
2. Create a <a class="reference-link" href="Render%20Note.md">Render Note</a>.
|
||||
3. Assign the `renderNote` [relation](../Advanced%20Usage/Attributes.md) to point at the previously created code note.
|
||||
|
||||
## Dynamic content
|
||||
## Legacy scripting using jQuery
|
||||
|
||||
A static HTML is generally not enough for <a class="reference-link" href="../Scripting.md">Scripting</a>. The next step is to automatically change parts of the note using JavaScript.
|
||||
|
||||
@@ -34,6 +34,28 @@ Now create a render note at any place and set its `~renderNote` relation to poin
|
||||
> **Current date & time**
|
||||
> The current date & time is Sun Apr 06 2025 15:26:29 GMT+0300 (Eastern European Summer Time)
|
||||
|
||||
## Dynamic content using Preact & JSX
|
||||
|
||||
As a more modern alternative to jQuery, it's possible to use Preact & JSX to render pages. Since JSX is a superset of JavaScript, there's no need to provide a HTML anymore.
|
||||
|
||||
Here are the steps to creating a simple render note:
|
||||
|
||||
1. Create a note of type <a class="reference-link" href="Render%20Note.md">Render Note</a>.
|
||||
2. Create a child <a class="reference-link" href="Code.md">Code</a> note with JSX as the language.
|
||||
As an example, use the following content:
|
||||
|
||||
```jsx
|
||||
export default function() {
|
||||
return (
|
||||
<>
|
||||
<p>Hello world.</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
3. In the parent render note, define a `~renderNote` relation pointing to the newly created child.
|
||||
4. Refresh the render note and it should display a “Hello world” message.
|
||||
|
||||
## Refreshing the note
|
||||
|
||||
It's possible to refresh the note via:
|
||||
|
||||
48
docs/User Guide/User Guide/Scripting/Common concepts/Script bundles.md
vendored
Normal file
48
docs/User Guide/User Guide/Scripting/Common concepts/Script bundles.md
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# Script bundles
|
||||
For both <a class="reference-link" href="../../Note%20Types/Render%20Note.md">Render Note</a> and more complicated scripts, it's generally useful to split the code into multiple <a class="reference-link" href="../../Note%20Types/Code.md">Code</a> notes.
|
||||
|
||||
When a script is run, the sub-children of the script being run (or the <a class="reference-link" href="../../Note%20Types/Render%20Note.md">Render Note</a>) are checked for children. If the children are Code notes of the corresponding type (front-end or backend) as the code being run, they will be evaluated as well.
|
||||
|
||||
The collection of a script and its child notes is called a _bundle_. A child note inside a bundle is called a _module_.
|
||||
|
||||
As a basic example of dependencies, consider the following note structure:
|
||||
|
||||
* _Script with dependency_
|
||||
|
||||
```javascript
|
||||
api.log(MyMath.sum(2, 2));
|
||||
```
|
||||
|
||||
* _MyMath_
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
When _Script with dependency_ is run, it will detect _MyMath_ as a submodule and provide the result of its `module.exports` object into a global object with the same name as the note.
|
||||
|
||||
> [!NOTE]
|
||||
> If the note contains spaces or special characters, they will be stripped. For example `My Nice Note!` becomes `MyNiceNote`.
|
||||
|
||||
## Alternative syntax
|
||||
|
||||
Instead of providing an object to `module.exports`, it's also possible to add fields individually:
|
||||
|
||||
```javascript
|
||||
module.exports.sum = (a, b) => a + b;
|
||||
module.exports.subtract = (a, b) => a - b;
|
||||
```
|
||||
|
||||
## Ignoring a code script from a bundle
|
||||
|
||||
To ignore a script from being included in a bundle (e.g. if it's unrelated to the parent script note), apply the `#disableInclusion` label.
|
||||
|
||||
## Sharing a module across multiple bundles
|
||||
|
||||
Modules can be reused across multiple scripts by simply cloning the shared module between two modules (see <a class="reference-link" href="../../Basic%20Concepts%20and%20Features/Notes/Cloning%20Notes.md">Cloning Notes</a>).
|
||||
|
||||
Optionally, a separate note can be used to contain all the different reusable modules for an easy way to discover them.
|
||||
@@ -1,9 +1,62 @@
|
||||
# Custom Widgets
|
||||
It's possible to create custom widget in three possible locations where you can display your custom content.
|
||||
Custom widgets are a special subset of scripts that render graphical elements in certain parts of the application. These can be used to add new functionality to the Trilium application.
|
||||
|
||||
Positions are:
|
||||
## Preact with JSX vs. vanilla jQuery
|
||||
|
||||
* `left-pane`
|
||||
* `center-pane`
|
||||
* `note-detail-pane` - located within `center-pane`, but specific to note (split)
|
||||
* `right-pane`
|
||||
In older versions of Trilium, custom widgets were exclusively written in a combination of jQuery with Trilium's internal widget architecture (e.g., `BasicWidget`, `NoteContextAwareWidget`).
|
||||
|
||||
Starting with v0.101.0, custom widgets can also be written in JSX using the <a class="reference-link" href="Preact.md">Preact</a> framework. Both legacy and Preact widgets have the same capabilities, with a single difference:
|
||||
|
||||
* Preact widgets are content-sized by default whereas legacy widgets need `this.contentSized()` applied in the constructor. For more information, see the corresponding section in <a class="reference-link" href="Custom%20Widgets/Troubleshooting.md">Troubleshooting</a>.
|
||||
|
||||
Wherever possible, widget examples will be both in the legacy and Preact format.
|
||||
|
||||
## Creating a custom widget
|
||||
|
||||
1. Create a <a class="reference-link" href="../../Note%20Types/Code.md">Code</a> note.
|
||||
2. Set the language to:
|
||||
1. JavaScript (frontend) for legacy widgets using jQuery.
|
||||
2. JSX for Preact widgets. You might need to go to Options → Code to enable the language first.
|
||||
3. Apply the `#widget` [label](../../Advanced%20Usage/Attributes/Labels.md).
|
||||
|
||||
## Getting started with a simple example
|
||||
|
||||
Let's start by creating a widget that shows a message near the content area. Follow the previous section to create a code note, and use the following content.
|
||||
|
||||
<table><thead><tr><th>Legacy</th><th style="width:50%;">Preact (v0.101.0+)</th></tr></thead><tbody><tr><td><pre><code class="language-text-x-trilium-auto">class HelloNoteDetail extends api.BasicWidget {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.contentSized();
|
||||
}
|
||||
|
||||
get parentWidget() { return "center-pane" }
|
||||
|
||||
doRender() {
|
||||
this.$widget = $("<span>Center pane</span>");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = new HelloNoteDetail();</code></pre></td><td style="vertical-align:top;"><pre><code class="language-text-x-trilium-auto">import { defineWidget } from "trilium:preact";
|
||||
|
||||
export default defineWidget({
|
||||
parent: "center-pane",
|
||||
render: () => <span>Center pane from Preact.</span>
|
||||
});</code></pre></td></tr></tbody></table>
|
||||
|
||||
[Refresh the application](../../Troubleshooting/Refreshing%20the%20application.md) and the widget should appear underneath the content area.
|
||||
|
||||
## Widget location (parent widget)
|
||||
|
||||
A widget can be placed in one of the following sections of the applications:
|
||||
|
||||
<table class="ck-table-resized"><colgroup><col style="width:15.59%;"><col style="width:30.42%;"><col style="width:16.68%;"><col style="width:37.31%;"></colgroup><thead><tr><th>Value for <code spellcheck="false">parentWidget</code></th><th>Description</th><th>Sample widget</th><th>Special requirements</th></tr></thead><tbody><tr><th><code spellcheck="false">left-pane</code></th><td>Appears within the same pane that holds the <a class="reference-link" href="../../Basic%20Concepts%20and%20Features/UI%20Elements/Note%20Tree.md">Note Tree</a>.</td><td>Same as above, with only a different <code spellcheck="false">parentWidget</code>.</td><td>None.</td></tr><tr><th><code spellcheck="false">center-pane</code></th><td>In the content area. If a split is open, the widget will span all of the splits.</td><td>See example above.</td><td>None.</td></tr><tr><th><code spellcheck="false">note-detail-pane</code></th><td><p>In the content area, inside the note detail area. If a split is open, the widget will be contained inside the split.</p><p>This is ideal if the widget is note-specific.</p></td><td><a class="reference-link" href="Custom%20Widgets/Note%20context%20aware%20widget.md">Note context aware widget</a></td><td><ul><li data-list-item-id="ec06332efcc3039721606c052f0d913fa">The widget must export a <code spellcheck="false">class</code> and not an instance of the class (e.g. <code spellcheck="false">no new</code>) because it needs to be multiplied for each note, so that splits work correctly.</li><li data-list-item-id="e8da690a2a8df148f6b5fc04ba1611688">Since the <code spellcheck="false">class</code> is exported instead of an instance, the <code spellcheck="false">parentWidget</code> getter must be <code spellcheck="false">static</code>, otherwise the widget is ignored.</li></ul></td></tr><tr><th><code spellcheck="false">right-pane</code></th><td>In the <a class="reference-link" href="../../Basic%20Concepts%20and%20Features/UI%20Elements/Right%20Sidebar.md">Right Sidebar</a>, as a dedicated section.</td><td><a class="reference-link" href="Custom%20Widgets/Right%20pane%20widget.md">Right pane widget</a></td><td><ul><li data-list-item-id="efe008d361e224f422582552648e1afe7">Although not mandatory, it's best to use a <code spellcheck="false">RightPanelWidget</code> instead of a <code spellcheck="false">BasicWidget</code> or a <code spellcheck="false">NoteContextAwareWidget</code>.</li></ul></td></tr></tbody></table>
|
||||
|
||||
To position the widget somewhere else, just change the value passed to `get parentWidget()` for legacy widgets or the `parent` field for Preact. Do note that some positions such as `note-detail-pane` and `right-pane` have special requirements that need to be accounted for (see the table above).
|
||||
|
||||
## Launch bar widgets
|
||||
|
||||
Launch bar widgets are similar to _Custom widgets_ but are specific to the <a class="reference-link" href="../../Basic%20Concepts%20and%20Features/UI%20Elements/Launch%20Bar.md">Launch Bar</a>. See <a class="reference-link" href="Launch%20Bar%20Widgets.md">Launch Bar Widgets</a> for more information.
|
||||
|
||||
## Custom position
|
||||
@@ -1,5 +1,7 @@
|
||||
# CSS
|
||||
In `doRender()`:
|
||||
## Classic widgets
|
||||
|
||||
In `doRender()`:<sup><a href="#fn1saoftmefpp">[1]</a></sup>
|
||||
|
||||
```
|
||||
this.cssBlock(`#my-widget {
|
||||
@@ -7,9 +9,13 @@ this.cssBlock(`#my-widget {
|
||||
bottom: 40px;
|
||||
left: 60px;
|
||||
z-index: 1;
|
||||
}`)
|
||||
}`);
|
||||
```
|
||||
|
||||
* * *
|
||||
## Preact widgets
|
||||
|
||||
Reference: [https://trilium.rocks/X7pxYpiu0lgU](https://trilium.rocks/X7pxYpiu0lgU)
|
||||
See the dedicated page: <a class="reference-link" href="../Preact/CSS.md">CSS</a>.
|
||||
|
||||
1. <sup><strong><a href="#fnref1saoftmefpp">^</a></strong></sup>
|
||||
|
||||
Reference: [https://trilium.rocks/X7pxYpiu0lgU](https://trilium.rocks/X7pxYpiu0lgU)
|
||||
53
docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget.md
vendored
Normal file
53
docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget.md
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# Note context aware widget
|
||||
Note context-aware widgets are a different type of widget which automatically react to changes in the current note.
|
||||
|
||||
Important aspects:
|
||||
|
||||
* The widget must export a `class` and not an instance of the class (e.g. `no new`) because it needs to be multiplied for each note, so that splits work correctly.
|
||||
* Since the `class` is exported instead of an instance, the `parentWidget` getter must be `static`, otherwise the widget is ignored.
|
||||
|
||||
## Example displaying the current note title
|
||||
|
||||
This is a note context-aware widget that simply displays the name the current note.
|
||||
|
||||
### Classic example
|
||||
|
||||
```
|
||||
class HelloNoteDetail extends api.NoteContextAwareWidget {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.contentSized();
|
||||
}
|
||||
|
||||
doRender() {
|
||||
this.$widget = $("<div>");
|
||||
}
|
||||
|
||||
async refreshWithNote(note) {
|
||||
this.$widget.text("Current note: " + note.title);
|
||||
}
|
||||
|
||||
static get parentWidget() { return "note-detail-pane" }
|
||||
get position() { return 10 }
|
||||
|
||||
}
|
||||
|
||||
module.exports = HelloNoteDetail;
|
||||
```
|
||||
|
||||
### Preact (v0.101.0+)
|
||||
|
||||
```
|
||||
import { defineWidget, useNoteContext, useNoteProperty } from "trilium:preact";
|
||||
|
||||
export default defineWidget({
|
||||
parent: "note-detail-pane",
|
||||
position: 10,
|
||||
render: () => {
|
||||
const { note } = useNoteContext();
|
||||
const title = useNoteProperty(note, "title");
|
||||
return <span>Current note JSX: {title}</span>;
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -40,6 +40,8 @@ module.exports = new NoteTitleWidget();
|
||||
|
||||
A simple widget which will show the current time, as an example on how to dynamically change the content of the widget periodically.
|
||||
|
||||
### Legacy widget
|
||||
|
||||
```
|
||||
const template = `<div></div>`;
|
||||
|
||||
@@ -62,6 +64,31 @@ class ToDoListWidget extends api.RightPanelWidget {
|
||||
module.exports = new ToDoListWidget();
|
||||
```
|
||||
|
||||
### Preact widget
|
||||
|
||||
```
|
||||
import { defineWidget, RightPanelWidget, useEffect, useState } from "trilium:preact";
|
||||
|
||||
export default defineWidget({
|
||||
parent: "right-pane",
|
||||
position: 1,
|
||||
render() {
|
||||
const [ time, setTime ] = useState();
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setTime(new Date().toLocaleString());
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
return (
|
||||
<RightPanelWidget id="clock-jsx" title="Clock (JSX)">
|
||||
<p>The time is: {time}</p>
|
||||
</RightPanelWidget>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Example for old layout
|
||||
|
||||
Here's a widget that displays a basic message ("Hi"):
|
||||
@@ -105,7 +132,7 @@ By default, the sidebar items are displayed in the order they are found by the a
|
||||
|
||||
It is possible to make a widget appear higher or lower up, by adjusting its `position` property:
|
||||
|
||||
```diff
|
||||
```
|
||||
class MyWidget extends api.RightPanelWidget {
|
||||
|
||||
+ get position() { return 20 };
|
||||
|
||||
@@ -108,11 +108,6 @@ class MyWidget extends api.BasicWidget {
|
||||
module.exports = new MyWidget();
|
||||
```
|
||||
|
||||
`parentWidget()` can be given the following values:
|
||||
|
||||
* `left-pane` - This renders the widget on the left side of the screen where the note tree lives.
|
||||
* `center-pane` - This renders the widget in the center of the layout in the same location that notes and splits appear.
|
||||
* `note-detail-pane` - This renders the widget _with_ the note in the center pane. This means it can appear multiple times with splits.
|
||||
* `right-pane` - This renders the widget to the right of any opened notes.
|
||||
For the list of possible values for `parentWidget()`, see <a class="reference-link" href="../Custom%20Widgets.md">Custom Widgets</a>.
|
||||
|
||||
[Reload](../../../Troubleshooting/Refreshing%20the%20application.md) the application one last time. When you click the button, a "Hello World!" message should appear, confirming that your widget is fully functional.
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
This is an example of a note context-aware widget, which reacts to the currently opened note and refreshes automatically as the user navigates through the notes.
|
||||
|
||||
## Legacy widget
|
||||
|
||||
In this example, the title of the note is displayed. It works best on the [horizontal layout](../../../Basic%20Concepts%20and%20Features/UI%20Elements/Vertical%20and%20horizontal%20layout.md).
|
||||
|
||||
```javascript
|
||||
@@ -29,4 +31,24 @@ class NoteTitleWidget extends api.NoteContextAwareWidget {
|
||||
}
|
||||
|
||||
module.exports = new NoteTitleWidget();
|
||||
```
|
||||
|
||||
## Preact widget (v0.101.0+)
|
||||
|
||||
```jsx
|
||||
import { defineLauncherWidget, useActiveNoteContext } from "trilium:preact";
|
||||
|
||||
export default defineLauncherWidget({
|
||||
render: () => {
|
||||
const { note } = useActiveNoteContext();
|
||||
return <div style={{
|
||||
display: "flex",
|
||||
height: "53px",
|
||||
width: "fit-content",
|
||||
fontSize: "0.75em",
|
||||
alignItems: "center",
|
||||
flexShrink: 0
|
||||
}}>{note?.title}</div>;
|
||||
}
|
||||
});
|
||||
```
|
||||
65
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md
vendored
Normal file
65
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
# Preact
|
||||
Since v0.101.0, Trilium integrates Preact for front-end scripting, including support for JSX.
|
||||
|
||||
Preact can be used for:
|
||||
|
||||
* <a class="reference-link" href="../../Note%20Types/Render%20Note.md">Render Note</a>, where a JSX code note is used instead of a HTML one.
|
||||
* <a class="reference-link" href="Custom%20Widgets.md">Custom Widgets</a>, where JSX can be used to replace the old, jQuery-based mechanism.
|
||||
|
||||
To get started, the first step is to enable JSX in the list of Code languages. Go to Options → Code Notes and check the “JSX” language. Afterwards, proceed with the documentation in either <a class="reference-link" href="../../Note%20Types/Render%20Note.md">Render Note</a> or <a class="reference-link" href="Custom%20Widgets.md">Custom Widgets</a>, which will both have a section on how to use the new Preact integration.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The documentation assumes prior knowledge with React or Preact. As a starting point, consider the [FreeCodeCamp course on Front End Development Libraries](https://www.freecodecamp.org/learn/front-end-development-libraries-v9/) or the [Preact Tutorial](https://preactjs.com/tutorial/).
|
||||
|
||||
## Import/exports
|
||||
|
||||
When using Preact with JSX, there is a special syntax which provides ES-like imports. This `import` syntax makes way for a more intuitive that doesn't make use of global objects and paves the way for better auto-completion support that might be introduced in the future.
|
||||
|
||||
### API imports
|
||||
|
||||
Instead of:
|
||||
|
||||
```jsx
|
||||
api.showMessage("Hello");
|
||||
```
|
||||
|
||||
the JSX version looks like this:
|
||||
|
||||
```jsx
|
||||
import { showMessage } from "trilium:api";
|
||||
showMessage("hello");
|
||||
```
|
||||
|
||||
### Preact API imports (hooks, components)
|
||||
|
||||
There's a new <a class="reference-link" href="../Script%20API.md">Script API</a> dedicated to Preact, which provides shared components that are also used by Trilium internally as well as hooks, for example.
|
||||
|
||||
```jsx
|
||||
import { useState } from "trilium:preact";
|
||||
const [ myState, setMyState ] = useState("Hi");
|
||||
```
|
||||
|
||||
### Exporting
|
||||
|
||||
JSX notes can export a component for use in <a class="reference-link" href="../../Note%20Types/Render%20Note.md">Render Note</a> or for <a class="reference-link" href="Preact/Component%20libraries.md">Component libraries</a>:
|
||||
|
||||
```jsx
|
||||
export default function() {
|
||||
return (
|
||||
<>
|
||||
<p>Hello world.</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Import/export are not required
|
||||
|
||||
These imports are syntactic sugar meant to replace the usage for the `api` global object (see <a class="reference-link" href="../Script%20API.md">Script API</a>).
|
||||
|
||||
> [!NOTE]
|
||||
> The `import` and `export` syntax work only for JSX notes. Standard/jQuery code notes still need to use the `api` global and `module.exports`.
|
||||
|
||||
## Under the hood
|
||||
|
||||
Unlike JavaScript, JSX requires pre-processing to turn it into JavaScript (just like TypeScript). To do so, Trilium uses [Sucrase](https://github.com/alangpierce/sucrase), a JavaScript library which processes the JSX to pure JavaScript. The processing is done each time a script is run (for widgets this happens at every program startup). If you notice any performance degradation due to long compilation, consider [reporting the issue](../../Troubleshooting/Reporting%20issues.md) to us.
|
||||
43
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md
vendored
Normal file
43
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Built-in components
|
||||
<figure class="image image_resized" style="width:54.58%;"><img style="aspect-ratio:896/712;" src="Built-in components_image.png" width="896" height="712"><figcaption>A partial screenshot from the Widget showcase example (see below).</figcaption></figure>
|
||||
|
||||
Trilium comes with its own set of Preact components, some of which are also available to <a class="reference-link" href="../Custom%20Widgets.md">Custom Widgets</a> and <a class="reference-link" href="../../../Note%20Types/Render%20Note.md">Render Note</a>.
|
||||
|
||||
To use these components, simply import them from `trilium:preact`:
|
||||
|
||||
```jsx
|
||||
import { ActionButton, Button, LinkButton } from "trilium:preact";
|
||||
```
|
||||
|
||||
and then use them:
|
||||
|
||||
```jsx
|
||||
export default function MyRenderNote() {
|
||||
const onClick = () => showMessage("A button was pressed");
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Buttons</h2>
|
||||
<div style={{ display: "flex", gap: "1em", alignItems: "center" }}>
|
||||
<ActionButton icon="bx bx-rocket" text="Action button" onClick={onClick} />
|
||||
<Button icon="bx bx-rocket" text="Simple button" onClick={onClick} />
|
||||
<LinkButton text="Link button" onClick={onClick} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Widget showcase
|
||||
|
||||
> [!TIP]
|
||||
> Starting with v0.101.0, the widget showcase is also available in the <a class="reference-link" href="../../../Advanced%20Usage/Database/Demo%20Notes.md">Demo Notes</a>.
|
||||
|
||||
This is a <a class="reference-link" href="../../../Note%20Types/Render%20Note.md">Render Note</a> example with JSX that shows most of the built-in components that are accessible to custom widgets and JSX render notes.
|
||||
|
||||
To use it, simply:
|
||||
|
||||
1. Create a render note.
|
||||
2. Create a child code note of JSX type with the content of this file: <a class="reference-link" href="Built-in%20components/Widget%20showcase.jsx">Widget showcase</a>
|
||||
3. Set the `~renderNote` relation of the parent note to the child note.
|
||||
4. Refresh the render note to see the results.
|
||||
189
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components/Widget showcase.jsx
vendored
Normal file
189
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components/Widget showcase.jsx
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
import {
|
||||
ActionButton, Button, LinkButton,
|
||||
Admonition, Collapsible,
|
||||
FormCheckbox, FormDropdownList, FormFileUploadButton, FormGroup, FormRadioGroup, FormTextArea,
|
||||
FormTextBox, FormToggle, Slider, RawHtml, LoadingSpinner, Icon,
|
||||
Dropdown, FormListItem, FormDropdownDivider, FormDropdownSubmenu,
|
||||
NoteAutocomplete, NoteLink, Modal,
|
||||
CKEditor,
|
||||
useEffect, useState
|
||||
} from "trilium:preact";
|
||||
import { showMessage } from "trilium:api";
|
||||
|
||||
export default function() {
|
||||
const [ time, setTime ] = useState();
|
||||
const lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam accumsan eu odio non gravida. Pellentesque ornare, arcu condimentum molestie dignissim, nibh turpis ultrices elit, eget elementum nunc erat at erat. Maecenas vehicula consectetur elit, nec fermentum elit venenatis eu.";
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => setTime(new Date().toLocaleString()), 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ padding: 20, display: "flex", flexDirection: "column", gap: "1em" }}>
|
||||
<h1>Widget showcase</h1>
|
||||
|
||||
<Buttons />
|
||||
<Admonition type="note">
|
||||
<strong>Admonition</strong><br />
|
||||
{lorem}
|
||||
</Admonition>
|
||||
|
||||
<Collapsible title="Collapsible" initiallyExpanded>
|
||||
{lorem}
|
||||
</Collapsible>
|
||||
|
||||
<FormElements />
|
||||
<NoteElements />
|
||||
<ModalSample />
|
||||
<DropdownSample />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Buttons() {
|
||||
const onClick = () => showMessage("A button was pressed");
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Buttons</h2>
|
||||
<div style={{ display: "flex", gap: "1em", alignItems: "center" }}>
|
||||
<ActionButton icon="bx bx-rocket" text="Action button" onClick={onClick} />
|
||||
<Button icon="bx bx-rocket" text="Simple button" onClick={onClick} />
|
||||
<LinkButton text="Link button" onClick={onClick} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function FormElements() {
|
||||
const [ checkboxChecked, setCheckboxChecked ] = useState(false);
|
||||
const [ dropdownValue, setDropdownValue ] = useState("key-1");
|
||||
const [ radioGroupValue, setRadioGroupValue ] = useState("key-1");
|
||||
const [ sliderValue, setSliderValue ] = useState(50);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Form elements</h2>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: "1em" }}>
|
||||
<FormGroup name="checkbox" label="Checkbox">
|
||||
<FormCheckbox label="Checkbox" currentValue={checkboxChecked} onChange={setCheckboxChecked} />
|
||||
</FormGroup>
|
||||
<FormGroup name="toggle" label="Toggle">
|
||||
<FormToggle switchOnName="Off" switchOffName="On" currentValue={checkboxChecked} onChange={setCheckboxChecked} />
|
||||
</FormGroup>
|
||||
<FormGroup name="dropdown" label="Dropdown">
|
||||
<FormDropdownList
|
||||
values={[
|
||||
{ key: "key-1", name: "First item" },
|
||||
{ key: "key-2", name: "Second item" },
|
||||
{ key: "key-3", name: "Third item" },
|
||||
]}
|
||||
currentValue={dropdownValue} onChange={setDropdownValue}
|
||||
keyProperty="key" titleProperty="name"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup name="radio-group" label="Radio group">
|
||||
<FormRadioGroup
|
||||
values={[
|
||||
{ value: "key-1", label: "First item" },
|
||||
{ value: "key-2", label: "Second item" },
|
||||
{ value: "key-3", label: "Third item" },
|
||||
]}
|
||||
currentValue={radioGroupValue} onChange={setRadioGroupValue}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup name="text-box" label="Text box">
|
||||
<FormTextBox
|
||||
placeholder="Type something..."
|
||||
currentValue="" onChange={(newValue) => {}}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup name="text-area" label="Text area">
|
||||
<FormTextArea
|
||||
placeholder="Type something bigger..."
|
||||
currentValue="" onChange={(newValue) => {}}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup name="slider" label="Slider">
|
||||
<Slider
|
||||
min={1} max={100}
|
||||
value={sliderValue} onChange={setSliderValue}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup name="file-upload" label="File upload">
|
||||
<FormFileUploadButton
|
||||
text="Upload"
|
||||
onChange={(files) => {
|
||||
const file = files?.[0];
|
||||
if (!file) return;
|
||||
showMessage(`Got file "${file.name}" of size ${file.size} B and type ${file.type}.`);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup name="icon" label="Icon">
|
||||
<Icon icon="bx bx-smile" />
|
||||
</FormGroup>
|
||||
<FormGroup name="loading-spinner" label="Loading spinner">
|
||||
<LoadingSpinner />
|
||||
</FormGroup>
|
||||
<FormGroup name="raw-html" label="Raw HTML">
|
||||
<RawHtml html="<strong>Hi</strong> <em>there</em>" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function NoteElements() {
|
||||
const [ noteId, setNoteId ] = useState("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Note elements</h2>
|
||||
|
||||
<FormGroup name="note-autocomplete" label="Note autocomplete">
|
||||
<NoteAutocomplete
|
||||
placeholder="Select a note"
|
||||
noteId={noteId} noteIdChanged={setNoteId}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup name="note-link" label="Note link">
|
||||
{noteId
|
||||
? <NoteLink notePath={noteId} showNoteIcon />
|
||||
: <span>Select a note first</span>}
|
||||
</FormGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ModalSample() {
|
||||
const [ shown, setShown ] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Modal</h2>
|
||||
<Button text="Open modal" onClick={() => setShown(true)} />
|
||||
<Modal title="Modal title" size="md" show={shown} onHidden={() => setShown(false)}>
|
||||
Modal goes here.
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownSample() {
|
||||
return (
|
||||
<>
|
||||
<h2>Dropdown menu</h2>
|
||||
<Dropdown text="Dropdown" hideToggleArrow>
|
||||
<FormListItem icon="bx bx-cut">Cut</FormListItem>
|
||||
<FormListItem icon="bx bx-copy">Copy</FormListItem>
|
||||
<FormListItem icon="bx bx-paste">Paste</FormListItem>
|
||||
<FormDropdownDivider />
|
||||
<FormDropdownSubmenu title="Submenu">
|
||||
<FormListItem>More items</FormListItem>
|
||||
</FormDropdownSubmenu>
|
||||
</Dropdown>
|
||||
</>
|
||||
)
|
||||
}
|
||||
BIN
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components_image.png
vendored
Normal file
BIN
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components_image.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
17
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md
vendored
Normal file
17
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# CSS
|
||||
## Inline styles
|
||||
|
||||
```jsx
|
||||
<div style={{
|
||||
display: "flex",
|
||||
height: "53px",
|
||||
width: "fit-content",
|
||||
fontSize: "0.75em",
|
||||
alignItems: "center",
|
||||
flexShrink: 0
|
||||
}}>/* [...] */</div>
|
||||
```
|
||||
|
||||
## Custom CSS file
|
||||
|
||||
Simply create a <a class="reference-link" href="../../../Theme%20development/Custom%20app-wide%20CSS.md">Custom app-wide CSS</a>. Make sure the class names are unique enough to not intersect with other UI elements, consider adding a prefix (e.g. `x-mywidget-`).
|
||||
69
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries.md
vendored
Normal file
69
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries.md
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# Component libraries
|
||||
Using the concept of <a class="reference-link" href="../../Common%20concepts/Script%20bundles.md">Script bundles</a>, it's possible to create components that are shared for multiple widgets or <a class="reference-link" href="../../../Note%20Types/Render%20Note.md">Render Note</a>.
|
||||
|
||||
## Exporting a single component
|
||||
|
||||
This is generally useful for big components.
|
||||
|
||||
Here's an example child hierarchy using <a class="reference-link" href="../../../Note%20Types/Render%20Note.md">Render Note</a>:
|
||||
|
||||
* _My render note_
|
||||
Note type: Render Note
|
||||
Link `~renderNote` to the child note (_Render note with subcomponents_)
|
||||
* _Render note with subcomponents_
|
||||
Type: JSX
|
||||
|
||||
```javascript
|
||||
export default function() {
|
||||
return (
|
||||
<MyComponent />
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
* _MyComponent_
|
||||
Type: Code / JSX
|
||||
|
||||
```javascript
|
||||
export default function MyComponent() {
|
||||
return <p>Hi</p>;
|
||||
}
|
||||
```
|
||||
|
||||
## Multiple components per note
|
||||
|
||||
To export multiple components, use the `export` keyword next to each of the function components.
|
||||
|
||||
Here's how a sub-note called `MyComponents` would look like:
|
||||
|
||||
```javascript
|
||||
export function MyFirstComponent() {
|
||||
return <p>First</p>;
|
||||
}
|
||||
|
||||
export function MySecondComponent() {
|
||||
return <p>Bar</p>;
|
||||
}
|
||||
```
|
||||
|
||||
Then in its parent note:
|
||||
|
||||
```javascript
|
||||
const { MyFirstComponent, MySecondComponent } = MyComponents;
|
||||
|
||||
export default function() {
|
||||
return (
|
||||
<>
|
||||
<MyFirstComponent />
|
||||
<MySecondComponent />
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, it's also possible to use the components directly without assigning them to a `const` first:
|
||||
|
||||
```javascript
|
||||
<MyComponents.MyFirstComponent />
|
||||
<MyComponents.MySecondComponent />
|
||||
```
|
||||
43
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks.md
vendored
Normal file
43
docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Hooks
|
||||
## Standard Preact hooks
|
||||
|
||||
All standard Preact hooks are available as an import in `trilium:api`.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
import { useState } from "trilium:preact";
|
||||
const [ myState, setMyState ] = useState("Hi");
|
||||
```
|
||||
|
||||
## Custom hooks
|
||||
|
||||
Trilium comes with a large set of custom hooks for Preact, all of which are also available to custom widgets and <a class="reference-link" href="../../../Note%20Types/Render%20Note.md">Render Note</a>.
|
||||
|
||||
### `useNoteContext`
|
||||
|
||||
As a replacement to <a class="reference-link" href="../Custom%20Widgets/Note%20context%20aware%20widget.md">Note context aware widget</a>, Preact exposes the current note context in the `useNoteContext` hook:
|
||||
|
||||
```
|
||||
import { defineWidget, useNoteContext, useNoteProperty } from "trilium:preact";
|
||||
|
||||
export default defineWidget({
|
||||
parent: "note-detail-pane",
|
||||
position: 10,
|
||||
render: () => {
|
||||
const { note } = useNoteContext();
|
||||
const title = useNoteProperty(note, "title");
|
||||
return <span>Current note JSX: {title}</span>;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Note that the custom widget must be inside the content area (so note detail widget) for this to work properly, especially when dealing with splits.
|
||||
|
||||
### `useActiveNoteContext`
|
||||
|
||||
`useActiveNoteContext` is an alternative to `useNoteContext` which works even if the widget is not within the note detail section and it automatically switches the note context as the user is navigating around between tabs and splits.
|
||||
|
||||
### `useNoteProperty`
|
||||
|
||||
This hook allows “listening” for changes to a particular property of a `FNote`, such as the `title` or `type` of a note. The benefit from using the hook is that it actually reacts to changes, for example if the note title or type is changed.
|
||||
Reference in New Issue
Block a user