alinea 2.0.0-preview.13 → 2.0.0-preview.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.init/content/pages/welcome.json +1 -1
- package/dist/.mcp/later/dev.json +1 -1
- package/dist/.mcp/online/dev.json +1 -1
- package/dist/adapter/next/cms.js +6 -2
- package/dist/adapter/next/context.js +1 -4
- package/dist/adapter/next/handler.js +12 -1
- package/dist/backend/CommitEntries.d.ts +17 -0
- package/dist/backend/CommitEntries.js +39 -0
- package/dist/backend/Handler.d.ts +9 -0
- package/dist/backend/Handler.js +80 -20
- package/dist/backend/api/BasicAuth.js +5 -1
- package/dist/backend/api/CreateBackend.js +2 -2
- package/dist/backend/api/DatabaseApi.js +18 -5
- package/dist/backend/api/GithubApi.d.ts +13 -0
- package/dist/backend/api/GithubApi.js +170 -30
- package/dist/backend/api/database.js +2 -2
- package/dist/backend/util/Syncable.d.ts +6 -0
- package/dist/backend/util/Syncable.js +3 -1
- package/dist/bundled.d.ts +911 -151
- package/dist/chunks/{chunk-5H4D5JQL.js → chunk-2PP7WJUJ.js} +1 -11
- package/dist/chunks/{chunk-QHHMKJUO.js → chunk-7DDPSWRY.js} +465 -2
- package/dist/chunks/{chunk-HOETF6HN.js → chunk-PMGIIYDD.js} +1 -0
- package/dist/chunks/{chunk-3QFSGTGF.js → chunk-SCV3TEXM.js} +1 -1
- package/dist/chunks/{chunk-UPY2RUZF.js → chunk-TWBREZWC.js} +30 -0
- package/dist/cli/Generate.js +20 -7
- package/dist/cli/Mcp.js +1 -1
- package/dist/cli/Serve.js +1 -1
- package/dist/cli/bin.js +1 -1
- package/dist/cli/build/BuildEmitter.d.ts +5 -1
- package/dist/cli/build/BuildEmitter.js +8 -3
- package/dist/cli/generate/CompileConfig.js +10 -6
- package/dist/cli/generate/DevDB.d.ts +4 -1
- package/dist/cli/generate/DevDB.js +15 -7
- package/dist/cli/generate/GenerateDashboard.js +1 -0
- package/dist/cli/generate/MediaLocations.d.ts +18 -0
- package/dist/cli/generate/MediaLocations.js +69 -0
- package/dist/cli/serve/CreateLocalServer.js +1 -0
- package/dist/cli/serve/mcp/DevMcp.js +1 -1
- package/dist/cloud/CloudRemote.js +21 -7
- package/dist/cms.d.ts +4 -2
- package/dist/components/Alert.js +2 -2
- package/dist/components/AppShell.js +2 -2
- package/dist/components/AsyncList.d.ts +52 -0
- package/dist/components/AsyncList.js +69 -0
- package/dist/components/Breadcrumbs.js +6 -6
- package/dist/components/Button.js +4 -4
- package/dist/components/Calendar.js +18 -18
- package/dist/components/Checkbox.js +10 -10
- package/dist/components/CheckboxGroup.js +2 -2
- package/dist/components/Collapsible.js +5 -5
- package/dist/components/ColorSwatch.js +2 -2
- package/dist/components/ColorSwatchPicker.js +4 -4
- package/dist/components/ComboBox.d.ts +27 -9
- package/dist/components/ComboBox.js +40 -9
- package/dist/components/Command.js +13 -13
- package/dist/components/ContentCard.js +28 -28
- package/dist/components/ContentGrid.js +11 -11
- package/dist/components/DataList.js +1 -1
- package/dist/components/DateField.d.ts +4 -0
- package/dist/components/DateField.js +2 -2
- package/dist/components/DatePicker.js +2 -2
- package/dist/components/DateRangePicker.js +2 -2
- package/dist/components/Dialog.d.ts +14 -3
- package/dist/components/Dialog.js +38 -11
- package/dist/components/DropZone.js +6 -6
- package/dist/components/DropdownMenu.js +8 -8
- package/dist/components/Empty.js +4 -4
- package/dist/components/Field.d.ts +1 -1
- package/dist/components/Field.js +18 -12
- package/dist/components/Form.d.ts +23 -0
- package/dist/components/Form.js +64 -0
- package/dist/components/Link.d.ts +8 -1
- package/dist/components/List.js +27 -27
- package/dist/components/MediaPreview.js +5 -5
- package/dist/components/MultipleSelect.d.ts +13 -8
- package/dist/components/MultipleSelect.js +25 -25
- package/dist/components/NavRail.js +7 -7
- package/dist/components/NumberField.d.ts +5 -1
- package/dist/components/NumberField.js +6 -4
- package/dist/components/Page.js +6 -6
- package/dist/components/Popover.js +2 -2
- package/dist/components/PreviewFrame.js +8 -8
- package/dist/components/RadioGroup.js +10 -10
- package/dist/components/Resizable.js +1 -1
- package/dist/components/SearchField.js +8 -8
- package/dist/components/Select.d.ts +13 -10
- package/dist/components/Select.js +18 -32
- package/dist/components/Sheet.js +6 -6
- package/dist/components/Sidebar.js +5 -5
- package/dist/components/SortableList.js +12 -12
- package/dist/components/Spinner.js +4 -4
- package/dist/components/Stack.d.ts +20 -0
- package/dist/components/Stack.js +45 -0
- package/dist/components/Surface.js +1 -1
- package/dist/components/Switch.js +5 -5
- package/dist/components/Table.d.ts +11 -1
- package/dist/components/Table.js +81 -53
- package/dist/components/Tabs.js +6 -6
- package/dist/components/TagGroup.js +10 -10
- package/dist/components/TextField.d.ts +7 -1
- package/dist/components/TextField.js +20 -11
- package/dist/components/TimeField.d.ts +6 -1
- package/dist/components/TimeField.js +51 -26
- package/dist/components/Timestamp.js +1 -1
- package/dist/components/ToggleGroup.js +2 -2
- package/dist/components/Toolbar.js +3 -3
- package/dist/components/Tooltip.js +5 -4
- package/dist/components/Tree.js +27 -27
- package/dist/components/internal/DateControl.js +11 -11
- package/dist/components/internal/FormScope.d.ts +2 -0
- package/dist/components/internal/FormScope.js +8 -0
- package/dist/components/internal/ListBoxGroup.d.ts +12 -0
- package/dist/components/internal/ListBoxGroup.js +46 -0
- package/dist/components/internal/ListBoxOption.d.ts +2 -2
- package/dist/components/internal/ListBoxOption.js +5 -5
- package/dist/components/internal/Locale.js +2 -2
- package/dist/components/internal/PopoverSurface.js +5 -4
- package/dist/components/internal/SelectionCheckbox.js +5 -5
- package/dist/components/internal/Slot.js +1 -1
- package/dist/components/internal/ToggleButton.js +4 -4
- package/dist/components/internal/Trigger.js +2 -2
- package/dist/components/internal/useDragDrop.js +2 -2
- package/dist/components/types.d.ts +4 -0
- package/dist/components.d.ts +3 -0
- package/dist/components.js +3 -0
- package/dist/core/Client.js +12 -1
- package/dist/core/Config.d.ts +6 -0
- package/dist/core/Config.js +12 -0
- package/dist/core/DashboardEntry.d.ts +19 -0
- package/dist/core/DashboardEntry.js +0 -0
- package/dist/core/Document.d.ts +2 -2
- package/dist/core/Document.js +10 -3
- package/dist/core/EntryFields.d.ts +8 -2
- package/dist/core/Field.d.ts +5 -0
- package/dist/core/Filter.d.ts +19 -2
- package/dist/core/Filter.js +5 -0
- package/dist/core/Graph.d.ts +10 -2
- package/dist/core/Overview.d.ts +74 -5
- package/dist/core/Overview.js +1 -0
- package/dist/core/Root.js +1 -1
- package/dist/core/Scope.d.ts +5 -0
- package/dist/core/Scope.js +22 -5
- package/dist/core/Type.d.ts +25 -6
- package/dist/core/Type.js +25 -7
- package/dist/core/db/LocalStore.d.ts +7 -0
- package/dist/core/field/CreateField.d.ts +1 -2
- package/dist/core/source/Source.d.ts +2 -0
- package/dist/core/source/Source.js +11 -0
- package/dist/core/util/Async.d.ts +2 -0
- package/dist/core/util/Async.js +12 -1
- package/dist/core/util/Checks.js +1 -1
- package/dist/core/util/Slugs.js +4 -3
- package/dist/dashboard/App.css +13 -11
- package/dist/dashboard/App.d.ts +1 -0
- package/dist/dashboard/App.js +22 -6
- package/dist/dashboard/Href.d.ts +37 -0
- package/dist/dashboard/Href.js +46 -0
- package/dist/dashboard/app/ActivityStatus.js +34 -12
- package/dist/dashboard/app/AuthView.js +2 -2
- package/dist/dashboard/app/BlockSettings.d.ts +9 -0
- package/dist/dashboard/app/BlockSettings.js +16 -0
- package/dist/dashboard/app/BlockSheet.js +3 -3
- package/dist/dashboard/app/BlockSheetStoryFrame.js +2 -2
- package/dist/dashboard/app/CompactField.js +15 -15
- package/dist/dashboard/app/CroppedImage.js +2 -2
- package/dist/dashboard/app/DashboardLayout.d.ts +1 -1
- package/dist/dashboard/app/DashboardLayout.js +12 -0
- package/dist/dashboard/app/EntryActions.d.ts +11 -0
- package/dist/dashboard/app/EntryActions.js +30 -0
- package/dist/dashboard/app/EntryFields.d.ts +15 -2
- package/dist/dashboard/app/EntryFields.js +26 -5
- package/dist/dashboard/app/EntryHeader.js +17 -7
- package/dist/dashboard/app/EntryReferences.js +2 -2
- package/dist/dashboard/app/EntrySidebar.js +25 -23
- package/dist/dashboard/app/EntrySidebarPreview.js +2 -2
- package/dist/dashboard/app/EntryValidationModal.js +3 -3
- package/dist/dashboard/app/Explorer.js +84 -45
- package/dist/dashboard/app/ExplorerBatchActions.js +4 -4
- package/dist/dashboard/app/ExplorerControls.js +5 -5
- package/dist/dashboard/app/ExplorerList.js +2 -2
- package/dist/dashboard/app/ExplorerModal.js +3 -3
- package/dist/dashboard/app/ExplorerOverviewHeader.d.ts +6 -0
- package/dist/dashboard/app/ExplorerOverviewHeader.js +28 -0
- package/dist/dashboard/app/ExplorerTable.js +7 -7
- package/dist/dashboard/app/ExplorerTabs.d.ts +12 -0
- package/dist/dashboard/app/ExplorerTabs.js +42 -0
- package/dist/dashboard/app/ImageEditor.js +31 -31
- package/dist/dashboard/app/LinkPicker.js +3 -3
- package/dist/dashboard/app/LocaleMenu.js +9 -9
- package/dist/dashboard/app/MediaPreviewCell.js +5 -5
- package/dist/dashboard/app/OverviewCell.js +12 -10
- package/dist/dashboard/app/RemoveDialog.js +5 -0
- package/dist/dashboard/app/SearchBar.js +4 -4
- package/dist/dashboard/app/SettingsTab.d.ts +6 -0
- package/dist/dashboard/app/SettingsTab.js +27 -0
- package/dist/dashboard/app/SidebarTree.js +19 -19
- package/dist/dashboard/app/WorkspaceMenu.js +6 -6
- package/dist/dashboard/app/WorkspaceRoots.js +102 -98
- package/dist/dashboard/app/editor/FileEditor.js +18 -18
- package/dist/dashboard/app/pages/EntryPage.js +42 -39
- package/dist/dashboard/app/pages/RootPage.js +3 -3
- package/dist/dashboard/app/pages/SplashPage.js +94 -79
- package/dist/dashboard/app/pages/UsersPage.js +9 -9
- package/dist/dashboard/app/ui/DashboardModal.js +14 -14
- package/dist/dashboard/atoms/activity.d.ts +1 -0
- package/dist/dashboard/atoms/activity.js +8 -5
- package/dist/dashboard/atoms/config.d.ts +2 -0
- package/dist/dashboard/atoms/dashboard.d.ts +11 -0
- package/dist/dashboard/atoms/dashboard.js +8 -0
- package/dist/dashboard/atoms/editor.d.ts +7 -1
- package/dist/dashboard/atoms/editor.js +14 -2
- package/dist/dashboard/atoms/explorer.d.ts +15 -0
- package/dist/dashboard/atoms/explorer.js +43 -14
- package/dist/dashboard/atoms/graph.d.ts +5 -0
- package/dist/dashboard/atoms/graph.js +26 -1
- package/dist/dashboard/atoms/nav.d.ts +16 -10
- package/dist/dashboard/atoms/nav.js +96 -36
- package/dist/dashboard/atoms/overview.d.ts +26 -4
- package/dist/dashboard/atoms/overview.js +41 -12
- package/dist/dashboard/atoms/root.js +7 -1
- package/dist/dashboard/boot/ActivityEvent.d.ts +1 -1
- package/dist/dashboard/boot/Boot.js +6 -0
- package/dist/dashboard/boot/DashboardWorker.d.ts +6 -0
- package/dist/dashboard/boot/DashboardWorker.js +89 -8
- package/dist/dashboard/boot/LoadWorker.js +3 -0
- package/dist/dashboard/boot/SyncEvent.d.ts +7 -0
- package/dist/dashboard/boot/SyncEvent.js +14 -0
- package/dist/dashboard/boot/WorkerDB.d.ts +1 -0
- package/dist/dashboard/boot/WorkerDB.js +3 -0
- package/dist/dashboard/cms/EntryTable.d.ts +4 -3
- package/dist/dashboard/cms/EntryTable.js +2 -2
- package/dist/dashboard/cms/ViewProps.d.ts +3 -2
- package/dist/dashboard/cms/hooks.d.ts +34 -18
- package/dist/dashboard/cms/hooks.js +43 -18
- package/dist/dashboard/contexts.d.ts +3 -2
- package/dist/dashboard/contexts.js +3 -2
- package/dist/dashboard/hook/UseEntryEditor.d.ts +1 -1
- package/dist/dashboard/hook/UseEntryEditor.js +7 -3
- package/dist/dashboard/icons.d.ts +1 -0
- package/dist/dashboard/icons.js +15 -0
- package/dist/database/BrowserEntryStore.js +35 -13
- package/dist/database/DatabaseTables.js +1 -1
- package/dist/database/EntryDatabase.d.ts +10 -1
- package/dist/database/EntryDatabase.js +31 -10
- package/dist/database/EntryStore.js +1 -11
- package/dist/database/entry/EntryTable.d.ts +5 -3
- package/dist/database/entry/EntryTable.js +25 -8
- package/dist/database/query/Condition.d.ts +4 -1
- package/dist/database/query/Condition.js +16 -5
- package/dist/database/query/EntryQuery.js +59 -7
- package/dist/database/query/Relation.d.ts +13 -1
- package/dist/database/query/Relation.js +19 -1
- package/dist/database/query/Search.d.ts +7 -1
- package/dist/database/query/Search.js +54 -9
- package/dist/database/sync/EntrySyncer.d.ts +2 -0
- package/dist/database/sync/EntrySyncer.js +4 -2
- package/dist/database/sync/Ingest.d.ts +2 -1
- package/dist/database/sync/Ingest.js +20 -6
- package/dist/field/code/CodeField.view.js +12 -12
- package/dist/field/link/EntryLink.d.ts +4 -5
- package/dist/field/link/FileLink.d.ts +2 -2
- package/dist/field/link/ImageLink.d.ts +2 -5
- package/dist/field/link/ImageLink.js +0 -1
- package/dist/field/link/LinkField.view.js +18 -15
- package/dist/field/list/ListField.view.js +50 -35
- package/dist/field/metadata/MetadataField.view.js +14 -14
- package/dist/field/richtext/RichTextBlock.js +39 -35
- package/dist/field/richtext/RichTextExtensions.js +23 -23
- package/dist/field/richtext/RichTextField.view.js +6 -6
- package/dist/field/richtext/RichTextInsertMenu.js +5 -5
- package/dist/field/richtext/RichTextToolbar.js +26 -1
- package/dist/field/richtext/Toolbar.d.ts +9 -1
- package/dist/field/richtext/Toolbar.js +12 -2
- package/dist/field/select/SelectField.view.js +1 -0
- package/dist/index.css +174 -23
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/next.d.ts +2 -0
- package/dist/next.edge.d.ts +2 -0
- package/dist/picker/entry/EntryPicker.d.ts +3 -1
- package/dist/picker/entry/EntryPicker.js +15 -1
- package/dist/preview/react.js +3 -0
- package/dist/query.d.ts +26 -14
- package/dist/query.js +4 -0
- package/dist/types.d.ts +8 -5
- package/dist/ui/Stack.d.ts +4 -2
- package/docs/ai-setup.md +3 -3
- package/docs/cli.md +4 -2
- package/docs/components/combo-box.md +105 -16
- package/docs/components/date-field.md +1 -1
- package/docs/components/dialog.md +51 -0
- package/docs/components/form.md +67 -0
- package/docs/components/link.md +10 -0
- package/docs/components/multiple-select.md +9 -17
- package/docs/components/number-field.md +5 -1
- package/docs/components/select.md +9 -20
- package/docs/components/stack.md +42 -0
- package/docs/components/table.md +83 -0
- package/docs/components/text-field.md +9 -1
- package/docs/components/time-field.md +5 -1
- package/docs/components.md +39 -1
- package/docs/configuration.md +3 -1
- package/docs/custom-fields.md +135 -7
- package/docs/deploy/self-host.md +2 -0
- package/docs/deploy.md +7 -4
- package/docs/fields/image.md +0 -1
- package/docs/fields/list.md +19 -0
- package/docs/fields/object.md +5 -3
- package/docs/fields/rich-text.md +31 -0
- package/docs/index.md +8 -6
- package/docs/instant-publishing.md +23 -2
- package/docs/overviews.md +128 -4
- package/docs/query/filtering.md +2 -0
- package/docs/query/related.md +58 -9
- package/docs/query.md +52 -1
- package/docs/quickstart.md +5 -5
- package/docs/schema/document.md +2 -0
- package/docs/schema/type.md +3 -1
- package/docs/upgrading.md +100 -19
- package/llms-full.txt +1022 -125
- package/package.json +1 -1
- package/readme.md +2 -2
- package/dist/chunks/{chunk-4ZA6KZHS.js → chunk-6FQ5IMNA.js} +3 -3
package/docs/components.md
CHANGED
|
@@ -34,6 +34,7 @@ Components are the React components the dashboard is built from, import them fro
|
|
|
34
34
|
- [ColorSwatchPicker](components/color-swatch-picker.md): Picks a color from a set of swatches.
|
|
35
35
|
- [DropZone](components/drop-zone.md): A target to drop files or other content on.
|
|
36
36
|
- [Field](components/field.md): The label, help text and error around your own input.
|
|
37
|
+
- [Form](components/form.md): Submits its fields and shows their validation errors.
|
|
37
38
|
|
|
38
39
|
### Collections
|
|
39
40
|
- [Table](components/table.md): Rows and columns with sorting, selection and dragging.
|
|
@@ -67,6 +68,7 @@ Components are the React components the dashboard is built from, import them fro
|
|
|
67
68
|
- [FoldIcon](components/fold-icon.md): A chevron that turns when its section opens.
|
|
68
69
|
|
|
69
70
|
### Layout
|
|
71
|
+
- [Stack](components/stack.md): Lays out its children in a row or column with a gap.
|
|
70
72
|
- [Surface](components/surface.md): A bordered panel for a group of content.
|
|
71
73
|
- [Collapsible](components/collapsible.md): A section that opens and closes.
|
|
72
74
|
- [Page](components/page.md): The header, content and footer of a dashboard page.
|
|
@@ -111,7 +113,7 @@ export function PriorityView({field}: FieldViewProps<PriorityField>) {
|
|
|
111
113
|
}
|
|
112
114
|
```
|
|
113
115
|
|
|
114
|
-
The components are styled by the dashboard's stylesheet and theme
|
|
116
|
+
The components are styled by the dashboard's stylesheet and theme. They are meant for code that runs inside the dashboard, but work on other pages too (see Theme tokens below).
|
|
115
117
|
|
|
116
118
|
## Conventions
|
|
117
119
|
|
|
@@ -129,3 +131,39 @@ The props of every component are typed, so your editor shows what each one accep
|
|
|
129
131
|
## Icons
|
|
130
132
|
|
|
131
133
|
Components with an `icon` prop take an icon component (any component that renders an `<svg>`, such as those from [react-icons](https://react-icons.github.io/react-icons) or copied from [Icones](https://icones.js.org)) or an element. The icons of the dashboard itself are exported from `alinea/dashboard/icons`.
|
|
134
|
+
|
|
135
|
+
## Theme tokens
|
|
136
|
+
|
|
137
|
+
The components are styled with CSS custom properties. `alinea/css` declares them, and its resets, only on elements with a `data-alinea-theme` attribute, so loading the stylesheet doesn't restyle the rest of a page. The dashboard sets the attribute on `<html>`, and popovers, dialogs and tooltips set it on themselves since they render outside the page. To use the components on a page of your site, import `alinea/css` and render them inside an element with `data-alinea-theme`. Add `data-theme="light"` or `data-theme="dark"` to that element to fix the color scheme, otherwise it follows the page.
|
|
138
|
+
|
|
139
|
+
A theme only sets the inputs, as `light-dark()` pairs where a value differs per color scheme. Every other token is derived from them. Set them on the element with `data-alinea-theme`, for example `<div data-alinea-theme className="brand-theme">`:
|
|
140
|
+
|
|
141
|
+
File: theme.css
|
|
142
|
+
```css
|
|
143
|
+
.brand-theme {
|
|
144
|
+
--alinea-radius: 6px;
|
|
145
|
+
--alinea-bg: light-dark(#ffffff, #18181b);
|
|
146
|
+
--alinea-fg: light-dark(#27272a, #e4e4e7);
|
|
147
|
+
--alinea-primary: light-dark(#2f6b45, #5fbf82);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Popovers, dialogs and tooltips render outside that element and carry the attribute themselves, so a class on the page doesn't reach them: use `[data-alinea-theme]` as the selector to theme every part of the components on a page.
|
|
152
|
+
|
|
153
|
+
Inputs:
|
|
154
|
+
|
|
155
|
+
- `--alinea-font`, `--alinea-mono-font`, `--alinea-font-size-base` and `--alinea-radius`.
|
|
156
|
+
- `--alinea-backdrop` (behind panels), `--alinea-bg` (panels), `--alinea-fg` (text) and `--alinea-border`.
|
|
157
|
+
- `--alinea-primary`, `--alinea-danger`, `--alinea-warning` and `--alinea-success`, with `--alinea-primary-fg`, `--alinea-danger-fg` and `--alinea-warning-fg` for text on them.
|
|
158
|
+
- `--alinea-ring`: the focus ring.
|
|
159
|
+
- `--alinea-workspace-color`: the `color` option of the current workspace, set by the dashboard. It falls back to `--alinea-primary`.
|
|
160
|
+
|
|
161
|
+
Derived tokens, to read in your own styles:
|
|
162
|
+
|
|
163
|
+
- Text: `--alinea-fg-strong`, `--alinea-fg-muted`, `--alinea-fg-disabled` and `--alinea-field-label`.
|
|
164
|
+
- Surfaces: `--alinea-bg-muted`, `--alinea-muted` (hover backgrounds), `--alinea-input`, `--alinea-overlay`, `--alinea-scrim` (behind dialogs) and `--alinea-border-strong`.
|
|
165
|
+
- Tones: `--alinea-selected` and `--alinea-selected-fg`, and a `-subtle` background with a `-subtle-fg` text color for `primary`, `danger`, `warning` and `success`, eg. `--alinea-danger-subtle`.
|
|
166
|
+
- Entry statuses: `--alinea-status-published`, `--alinea-status-draft` and `--alinea-status-unpublished`, each with a `-fg` color.
|
|
167
|
+
- Shape and motion: `--alinea-radius-sm`, `--alinea-radius-lg`, `--alinea-radius-xl`, `--alinea-shadow-raised`, `--alinea-shadow-muted`, `--alinea-shadow-tooltip`, `--alinea-shadow-modal` and `--alinea-transition`.
|
|
168
|
+
|
|
169
|
+
The color palette the default theme is built from (`--alinea-gray-500`, `--alinea-blue-800`, ...) is not part of this list and can change.
|
package/docs/configuration.md
CHANGED
|
@@ -56,9 +56,11 @@ The URL of the API route that serves the dashboard and media files: the route th
|
|
|
56
56
|
|
|
57
57
|
The URL of your website, either one string or one per environment: `{development, production}`, picked by `NODE_ENV`. A value without a protocol gets `https://`.
|
|
58
58
|
|
|
59
|
-
- `alinea build` exits with `
|
|
59
|
+
- It is required for production builds when `handlerUrl` is relative: `alinea build` exits, and queries on your site throw, with `Missing baseUrl in Alinea config: set baseUrl.production to the url of your site, …`. An absolute `handlerUrl` doesn't need it.
|
|
60
60
|
- In production your site reaches the handler at `handlerUrl` resolved against this URL, to check for new content. That includes `next start` on your machine, which talks to the production URL.
|
|
61
61
|
|
|
62
|
+
Server renders have no request to take the origin from (static pages and pages from `generateStaticParams` are rendered without one, and reading the request headers would make every route dynamic), so Alinea doesn't guess it. Set `baseUrl.production`, or make `handlerUrl` absolute.
|
|
63
|
+
|
|
62
64
|
Most projects read the production URL from an environment variable, see [Environment variables](#environment-variables) below.
|
|
63
65
|
|
|
64
66
|
## Dashboard options
|
package/docs/custom-fields.md
CHANGED
|
@@ -28,7 +28,28 @@ export function range(
|
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
`Field.Create<Value, Options>` is the type of a field that stores `Value` (anything JSON can hold) and accepts your `Options` next to the standard ones: `initialValue`, `required`, `readOnly`, `hidden`, `shared` and `
|
|
31
|
+
`Field.Create<Value, Options>` is the type of a field that stores `Value` (anything JSON can hold) and accepts your `Options` next to the standard ones: `initialValue`, `required`, `readOnly`, `hidden`, `shared`, `validate` and `width`, the width of the field in the entry form. Queries return the stored value as it is.
|
|
32
|
+
|
|
33
|
+
To describe the options as an interface, extend `FieldOptions` from `alinea/core` and type the options argument with `WithoutLabel`:
|
|
34
|
+
|
|
35
|
+
File: fields/Rating.ts
|
|
36
|
+
```tsx
|
|
37
|
+
import {Field} from 'alinea'
|
|
38
|
+
import type {FieldOptions, WithoutLabel} from 'alinea'
|
|
39
|
+
|
|
40
|
+
export interface RatingOptions extends FieldOptions<number> {
|
|
41
|
+
max?: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type RatingField = Field.Create<number, RatingOptions>
|
|
45
|
+
|
|
46
|
+
export function rating(
|
|
47
|
+
label: string,
|
|
48
|
+
options: WithoutLabel<RatingOptions> = {}
|
|
49
|
+
): RatingField {
|
|
50
|
+
return Field.create({label, options, view: '@/fields/Rating.view'})
|
|
51
|
+
}
|
|
52
|
+
```
|
|
32
53
|
|
|
33
54
|
`Field.create` takes:
|
|
34
55
|
|
|
@@ -154,9 +175,13 @@ export function priority(
|
|
|
154
175
|
}
|
|
155
176
|
```
|
|
156
177
|
|
|
178
|
+
The entry editor sets `--alinea-header-height` to the height of its header. A view that fills the visible editor can size itself with it, for example `height: calc(100dvh - var(--alinea-header-height))`.
|
|
179
|
+
|
|
180
|
+
Style your own markup with the [theme tokens](components.md#theme-tokens), such as `--alinea-fg-muted`, `--alinea-border` and `--alinea-radius`, so it follows light and dark mode. `--alinea-workspace-color` holds the `color` of the current workspace.
|
|
181
|
+
|
|
157
182
|
## The alinea/cms API
|
|
158
183
|
|
|
159
|
-
Everything in `alinea/cms` works in field views and in the other components the dashboard renders: `Field.view` sections and
|
|
184
|
+
Everything in `alinea/cms` works in field views and in the other components the dashboard renders for an entry: `Field.view` sections, entry `actions` and a type's `view`. A root's `view` is not part of an entry: the hooks that read the dashboard, such as `useGraph`, `useNavigate`, `useLocale` and `useUser`, and `EntryTable` work there, the field hooks, `EditField` and `EditFields` don't, and `useEntry()` returns `null`.
|
|
160
185
|
|
|
161
186
|
Field hooks, for the field passed to a view or another field of the same entry, list row or object:
|
|
162
187
|
|
|
@@ -165,24 +190,55 @@ Field hooks, for the field passed to a view or another field of the same entry,
|
|
|
165
190
|
- `useFieldOptions(field)`: the field's options, including `label` and the resolved `readOnly` and `hidden`.
|
|
166
191
|
- `useFieldError(field)`: the validation message from `required` or `validate`, if any.
|
|
167
192
|
- `useFieldKey(field)`: the key the field is stored under, such as `'title'`.
|
|
168
|
-
-
|
|
193
|
+
- Other fields: pass any field of the same list row or object, of a parent row or of the entry to these hooks, it resolves in the nearest scope that holds it. Import the type from your schema (`useFieldValue(BlogPost.title)`) or pass the field through your field's options. A view shared by types that each define their own field passes its key instead: `useFieldValue('path')` reads that field of the same row, untyped. `useSiblingFieldValue(key)` is deprecated.
|
|
194
|
+
|
|
195
|
+
A field in the rows of a list can read a field of the entry, here to suggest the post title as a caption:
|
|
196
|
+
|
|
197
|
+
File: fields/Caption.view.tsx
|
|
198
|
+
```tsx
|
|
199
|
+
import {
|
|
200
|
+
type FieldViewProps,
|
|
201
|
+
useField,
|
|
202
|
+
useFieldOptions,
|
|
203
|
+
useFieldValue
|
|
204
|
+
} from 'alinea/cms'
|
|
205
|
+
import {TextField} from 'alinea/components'
|
|
206
|
+
import {BlogPost} from '@/schema'
|
|
207
|
+
import type {CaptionField} from './Caption'
|
|
208
|
+
|
|
209
|
+
export function CaptionView({field}: FieldViewProps<CaptionField>) {
|
|
210
|
+
const [value, setValue] = useField(field)
|
|
211
|
+
const {label} = useFieldOptions(field)
|
|
212
|
+
// A field of the entry, read from inside a list row
|
|
213
|
+
const title = useFieldValue(BlogPost.title)
|
|
214
|
+
return (
|
|
215
|
+
<TextField
|
|
216
|
+
label={label}
|
|
217
|
+
value={value}
|
|
218
|
+
placeholder={title}
|
|
219
|
+
onValueChange={setValue}
|
|
220
|
+
/>
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
```
|
|
169
224
|
|
|
170
225
|
Entry and dashboard hooks:
|
|
171
226
|
|
|
172
|
-
- `useEntry()`: the entry being edited, with its `id`, `
|
|
227
|
+
- `useEntry()`: the entry being edited, with its `id`, `type`, `title`, `path`, `url`, `locale`, `status`, `workspace`, `root`, `parentId` and `data`. `data` holds the field values as saved, read the live value of a field while it is edited with `useFieldValue(field)`. `null` outside an entry.
|
|
173
228
|
- `useLocale()`: the locale being edited, or `null` for content without translations.
|
|
174
229
|
- `useGraph()`: query content from the dashboard with the same API as `cms`, for example to list entries in a picker.
|
|
175
230
|
- `useUser()`, `usePolicy()`: the signed-in user and their [permissions](roles-permissions.md).
|
|
176
|
-
- `useNavigate()`: returns a function that opens an entry or root in the dashboard: `navigate({
|
|
231
|
+
- `useNavigate()`: returns a function that opens an entry or root in the dashboard: `navigate({entryId, locale})` looks up the entry's workspace and root, `navigate({workspace, root})` opens a root. Pass `view: 'edit'` or `view: 'overview'` to pick the form or the list of children. The editor is asked to confirm first when there are unsaved changes.
|
|
232
|
+
- `href(location)` from `alinea`: the href of the same locations, for a link that also opens in a new tab: `<a href={href({entryId: post.id})}>`. It is a plain function, so it also works in table rows, in the `help` of a field and on the server, where you prefix the dashboard's url: `${baseUrl}/admin${href({entryId})}`.
|
|
177
233
|
- `usePreviewMetadata()`: the title, description and other metadata the live preview reports for the current page.
|
|
178
234
|
|
|
179
235
|
Components and types:
|
|
180
236
|
|
|
181
237
|
- `FieldChrome`: the label, help text and error of a field around your own control.
|
|
182
|
-
- `EditField`, `EditFields`: render a field, or a record of fields, with the view it is configured with, for example to lay out existing fields in a `Field.view` section.
|
|
238
|
+
- `EditField`, `EditFields`: render a field, or a record of fields, with the view it is configured with, for example to lay out existing fields in a `Field.view` section. Pass an object field to `EditFields` to render its fields, or some of them, in the scope of the object: `<EditFields field={Product.dimensions} fields={{width: Product.dimensions.width}} />`.
|
|
183
239
|
- `EntryTable`: a table of entries with the columns of an [overview](overviews.md#entry-tables-in-your-own-views), for example to list related entries in a `Field.view` section.
|
|
184
240
|
- `FieldViewProps<F>`, `SectionViewProps`, `TypeViewProps`, `RootViewProps`: the props the dashboard passes to a field view, a `Field.view` component, a type's `view` and a root's `view`.
|
|
185
|
-
- `OverviewCellProps`, `OverviewActionProps`, `OverviewEntry`: the props of an overview column's `view` and of an overview action, and the entry they receive.
|
|
241
|
+
- `OverviewCellProps`, `OverviewActionProps`, `OverviewEntry`: the props of an overview column's `view` and of an overview action, and the entry they receive. `EntryActionProps` and `DashboardEntry`: the props of an entry action and the entry `useEntry()` returns.
|
|
186
242
|
|
|
187
243
|
## Show a component between fields
|
|
188
244
|
|
|
@@ -218,6 +274,77 @@ export function ProductUrls() {
|
|
|
218
274
|
}
|
|
219
275
|
```
|
|
220
276
|
|
|
277
|
+
## Replace the form of a type
|
|
278
|
+
|
|
279
|
+
A type's `view` replaces the form of its entries with your own component, for example to edit the fields in a layout of your own. The entry header stays above it, with the title, the status, the publish and draft buttons, the type's `actions` and the sidebar with history and references. The view fills the rest of the editor, without padding.
|
|
280
|
+
|
|
281
|
+
File: schema/Product.ts
|
|
282
|
+
```tsx
|
|
283
|
+
import {Config, Field} from 'alinea'
|
|
284
|
+
|
|
285
|
+
export const Product = Config.document('Product', {
|
|
286
|
+
view: '@/views/ProductView.view#ProductView',
|
|
287
|
+
fields: {
|
|
288
|
+
summary: Field.text('Summary', {multiline: true}),
|
|
289
|
+
price: Field.number('Price')
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
File: views/ProductView.view.tsx
|
|
295
|
+
```tsx
|
|
296
|
+
import {EditField, useField} from 'alinea/cms'
|
|
297
|
+
import {TextField} from 'alinea/components'
|
|
298
|
+
import {Product} from '@/schema/Product'
|
|
299
|
+
|
|
300
|
+
export function ProductView() {
|
|
301
|
+
const [summary = '', setSummary] = useField(Product.summary)
|
|
302
|
+
return (
|
|
303
|
+
<div style={{display: 'grid', gap: 16, padding: 24}}>
|
|
304
|
+
<TextField
|
|
305
|
+
label="Summary"
|
|
306
|
+
multiline
|
|
307
|
+
value={summary}
|
|
308
|
+
onValueChange={setSummary}
|
|
309
|
+
/>
|
|
310
|
+
<EditField field={Product.price} />
|
|
311
|
+
</div>
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
The view receives `TypeViewProps`: the `type` of the entry. It edits the entry like the form does: the field hooks, `EditField` and `EditFields` read and write the fields of the entry, the changes show up as unsaved edits in the header and are kept until the editor publishes or saves a draft, and leaving the entry with unsaved changes asks to confirm first. To fill the editor, size the view with `height: 100%`.
|
|
317
|
+
|
|
318
|
+
## Add actions to the entry header
|
|
319
|
+
|
|
320
|
+
Buttons that act on the entry, such as an export or a button that creates a related entry, go in the `actions` of its type. They render in the entry header next to the publish button and receive `EntryActionProps`: the `entry` and the `locale` it is edited in. The hooks of `alinea/cms` read and write its fields.
|
|
321
|
+
|
|
322
|
+
File: schema/Product.ts
|
|
323
|
+
```tsx
|
|
324
|
+
import {Config, Field} from 'alinea'
|
|
325
|
+
|
|
326
|
+
export const Product = Config.document('Product', {
|
|
327
|
+
fields: {title: Field.text('Title')},
|
|
328
|
+
actions: ['@/views/ExportProduct.view#ExportProduct']
|
|
329
|
+
})
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
File: views/ExportProduct.view.tsx
|
|
333
|
+
```tsx
|
|
334
|
+
import {type EntryActionProps, useFieldValue} from 'alinea/cms'
|
|
335
|
+
import {Button} from 'alinea/components'
|
|
336
|
+
import {Product} from '@/schema/Product'
|
|
337
|
+
|
|
338
|
+
export function ExportProduct({entry}: EntryActionProps) {
|
|
339
|
+
const title = useFieldValue(Product.title)
|
|
340
|
+
return (
|
|
341
|
+
<Button onClick={() => window.open(`/api/export/${entry.id}`)}>
|
|
342
|
+
Export {title}
|
|
343
|
+
</Button>
|
|
344
|
+
)
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
221
348
|
## Upgrading from Alinea 1.x
|
|
222
349
|
|
|
223
350
|
Field views from 1.x keep working, with deprecated APIs:
|
|
@@ -225,6 +352,7 @@ Field views from 1.x keep working, with deprecated APIs:
|
|
|
225
352
|
- `useField` from `alinea/dashboard` returns `{value, mutator, options, label, error}`. Replace it with the tuple `useField` from `alinea/cms` and the separate `useFieldOptions` and `useFieldError`. `useFieldValue`, `useFieldOptions`, `useFieldError` and `useFieldKey` also move to `alinea/cms`, and `useFieldMutator` becomes `useFieldSetter`.
|
|
226
353
|
- `InputLabel` from `alinea/dashboard` is now the `Field` component. It no longer shows `help` from `{...options}`: use `FieldChrome` from `alinea/cms`, which reads it from the field, or pass the help text to `Field` from `alinea/components` as `description`.
|
|
227
354
|
- `useLocale`, `useGraph` and `useEntryEditor` from `alinea/dashboard/hook/*` become `useLocale`, `useGraph` and `useEntry` from `alinea/cms`.
|
|
355
|
+
- `useEntryEditor` still works as a shim for views that only read the entry: `activeVersion` has the `id`, `locale`, `path`, `status`, `title` and `type` of the entry, and `activeVersion.data` only has `path`. Reading another field of it throws, read field values with `useFieldValue(field)` instead. `useEntry()` returns the same entry as a `DashboardEntry`, with its saved field values in `data`.
|
|
228
356
|
- Hooks imported from `alinea/dashboard/hooks` still work, but that module is internal: import them from `alinea/cms`.
|
|
229
357
|
- Components from `alinea/ui` were removed. Use [`alinea/components`](components.md).
|
|
230
358
|
|
package/docs/deploy/self-host.md
CHANGED
|
@@ -61,6 +61,8 @@ When two parts implement the same thing, the one listed last wins. That is how a
|
|
|
61
61
|
|
|
62
62
|
Commits are made by the owner of the token. The editor who published is added as a `Co-authored-by` trailer with their name and email. When the branch moved since the dashboard last synced, the handler syncs and retries the commit up to three times.
|
|
63
63
|
|
|
64
|
+
To commit under another name, such as when your host only deploys commits of its members, pass `author: {name, email}`. GitHub links the commit to the account that owns the email. These commits are written through the REST API: GitHub does not mark them as verified, and a save usually spends three REST requests, plus one per uploaded file, where commits by the token owner spend none. Removing a file that is already gone costs one more, and when the branch moved in the meantime the save is repeated.
|
|
65
|
+
|
|
64
66
|
## Authentication
|
|
65
67
|
|
|
66
68
|
### Basic authentication
|
package/docs/deploy.md
CHANGED
|
@@ -78,7 +78,7 @@ Without a `backend` option the handler connects to Alinea Cloud. Pass `backend`
|
|
|
78
78
|
|
|
79
79
|
### Commit hooks
|
|
80
80
|
|
|
81
|
-
Two hooks on the handler run for every commit, whatever it contains. `beforeCommit` receives the changes before they are committed: inspect or rewrite them and return the mutations to commit, or return nothing to keep them as they are. `afterCommit` runs once the commit is made, with the mutations
|
|
81
|
+
Two hooks on the handler run for every commit, whatever it contains. `beforeCommit` receives the changes before they are committed: inspect or rewrite them and return the mutations to commit, or return nothing to keep them as they are. `afterCommit` runs once the commit is made, with the mutations, the `sha` of the new commit, the changed `entries` as they are after the commit and the `removed` entries with the url they had: revalidate caches of your own, notify your team or trigger a deploy.
|
|
82
82
|
|
|
83
83
|
File: app/(alinea)/api/cms/route.ts
|
|
84
84
|
```tsx
|
|
@@ -92,9 +92,12 @@ const handler = createHandler({
|
|
|
92
92
|
// Inspect or rewrite the changes, return the mutations to commit
|
|
93
93
|
return mutations
|
|
94
94
|
},
|
|
95
|
-
afterCommit({
|
|
96
|
-
// Pages that query content refresh on their own, revalidate your own
|
|
97
|
-
|
|
95
|
+
afterCommit({entries, removed}) {
|
|
96
|
+
// Pages that query content refresh on their own, revalidate your own
|
|
97
|
+
// caches, eg. a feed that lists products
|
|
98
|
+
const changed = [...entries, ...removed]
|
|
99
|
+
if (changed.some(entry => entry.type === 'Product'))
|
|
100
|
+
revalidateTag('products', 'max')
|
|
98
101
|
}
|
|
99
102
|
})
|
|
100
103
|
|
package/docs/fields/image.md
CHANGED
|
@@ -36,7 +36,6 @@ The picker only offers images: files with an extension such as `.jpg`, `.png`, `
|
|
|
36
36
|
Each image is resolved to an `ImageLink`:
|
|
37
37
|
|
|
38
38
|
- `src`: the url of the image on your site, such as `/admin/file/landscape.jpg?v=3f2a…`. It's served through your Alinea handler, and the `v` parameter changes when the file is replaced, so caches never serve an outdated copy.
|
|
39
|
-
- `url`: the same as `src`.
|
|
40
39
|
- `width` and `height`: the dimensions in pixels.
|
|
41
40
|
- `alt`: the alt text editors enter in the media library, in the locale of the entry you queried when the media root is [translated](../workspaces/media.md).
|
|
42
41
|
- `focus`: the focal point as `{x, y}`, each between 0 and 1. Use it as `object-position` when you crop the image.
|
package/docs/fields/list.md
CHANGED
|
@@ -73,6 +73,25 @@ export default Config.type("List field", {
|
|
|
73
73
|
|
|
74
74
|
Row types are ordinary [Types](../schema/type.md): they can have tabs, an `icon` that is shown in the add menu and nested lists. They don't need a title or path and don't have to be in the schema.
|
|
75
75
|
|
|
76
|
+
Options that change how a block looks rather than what it says, such as a background or a layout variant, can go in the type's `settings`. Editors find them in the block's settings sheet, below the label and anchor, which keeps the block form short. They're stored on the row next to its other fields:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const HeroBlock = Config.type('Hero', {
|
|
80
|
+
fields: {
|
|
81
|
+
title: Field.text('Title'),
|
|
82
|
+
text: Field.richText('Text')
|
|
83
|
+
},
|
|
84
|
+
settings: {
|
|
85
|
+
background: Field.select('Background', {
|
|
86
|
+
options: {light: 'Light', dark: 'Dark'},
|
|
87
|
+
initialValue: 'light'
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// A queried row: {_type: 'HeroBlock', title, text, background, ...}
|
|
93
|
+
```
|
|
94
|
+
|
|
76
95
|
## Value
|
|
77
96
|
|
|
78
97
|
The value is an array of rows. Next to its fields, every row has:
|
package/docs/fields/object.md
CHANGED
|
@@ -41,11 +41,11 @@ An object with a key per field: `{street: '', zip: '', city: ''}`. New entries s
|
|
|
41
41
|
|
|
42
42
|
## Querying
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Without a `select` the object is returned as a whole. Select or sort on a field inside it through the object, `Venue.address.city`, and filter on inner values with `has`:
|
|
45
45
|
|
|
46
46
|
```tsx
|
|
47
47
|
import {cms} from '@/cms'
|
|
48
|
-
import {Config, Field} from 'alinea'
|
|
48
|
+
import {Config, Field, Query} from 'alinea'
|
|
49
49
|
|
|
50
50
|
export const Venue = Config.document('Venue', {
|
|
51
51
|
fields: {
|
|
@@ -61,8 +61,10 @@ export const Venue = Config.document('Venue', {
|
|
|
61
61
|
const venuesInGhent = await cms.find({
|
|
62
62
|
type: Venue,
|
|
63
63
|
filter: {address: {has: {city: 'Ghent'}}},
|
|
64
|
-
select: {
|
|
64
|
+
select: {title: Query.title, street: Venue.address.street},
|
|
65
|
+
orderBy: {asc: Venue.address.street}
|
|
65
66
|
})
|
|
67
|
+
// Array<{title: string, street: string}>
|
|
66
68
|
```
|
|
67
69
|
|
|
68
70
|
## Reusing a group of fields
|
package/docs/fields/rich-text.md
CHANGED
|
@@ -160,6 +160,37 @@ export function Prose({doc}: {doc: TextDoc}) {
|
|
|
160
160
|
|
|
161
161
|
The overridable tags are `h1`–`h6`, `p`, `b`, `i`, `ul`, `ol`, `li`, `blockquote`, `hr`, `img`, `br`, `small`, `sub`, `sup`, `a`, `table`, `tr`, `td` and `th`. Pass `text` to wrap every piece of text in a component. Headings get an `id` (the anchor an editor set, or a slug of the heading text), so you can link to sections.
|
|
162
162
|
|
|
163
|
+
### Custom attributes
|
|
164
|
+
|
|
165
|
+
Attributes that a custom extension adds to a node are stored with the node, but only the `data-*` attributes its `renderHTML` produces reach the rendered elements and your tag overrides. Render an attribute as `data-*` to use it on your site:
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import {Field} from 'alinea'
|
|
169
|
+
import {extensions} from 'alinea/field/richtext'
|
|
170
|
+
|
|
171
|
+
const Checklist = extensions.BulletList.extend({
|
|
172
|
+
addAttributes() {
|
|
173
|
+
return {
|
|
174
|
+
...this.parent?.(),
|
|
175
|
+
checklist: {
|
|
176
|
+
default: null,
|
|
177
|
+
renderHTML: attributes =>
|
|
178
|
+
attributes.checklist ? {'data-checklist': 'true'} : {}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
export const body = Field.richText('Body', {
|
|
185
|
+
extensions: defaults => ({...defaults, BulletList: Checklist})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// <RichText doc={body} ul={<ul className="list" />} /> renders
|
|
189
|
+
// <ul data-checklist="true" class="list">
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Content saved by Alinea 1.x only has the raw attribute (`checklist: true`). Its `data-*` attribute is added when the entry is saved again in the dashboard, until then views don't receive it.
|
|
193
|
+
|
|
163
194
|
### Blocks
|
|
164
195
|
|
|
165
196
|
Blocks are rendered only when you pass a component for their type name. The component receives the block's fields as props:
|
package/docs/index.md
CHANGED
|
@@ -45,7 +45,7 @@ The documentation of this version of Alinea, one Markdown file per page of https
|
|
|
45
45
|
- [Querying content](query.md): Read content with the methods on your CMS instance.
|
|
46
46
|
- [Structural](query/structural.md): Most queries start by saying which entries you want: of a type, at a url, in a root, in a locale. Then you choose what to get back and in which order.
|
|
47
47
|
- [Filtering](query/filtering.md): Use filter to match entries on the values of their fields, and search to find entries by the words they contain.
|
|
48
|
-
- [Related](query/related.md): A single query can also fetch entries related to the ones it finds: their parents, children and siblings, their translations,
|
|
48
|
+
- [Related](query/related.md): A single query can also fetch entries related to the ones it finds: their parents, children and siblings, their translations, the entries they link to and the…
|
|
49
49
|
- [Advanced](query/advanced.md): Patterns for larger sites: nesting relations, querying several types at once, reusing parts of queries and controlling how fresh the content is.
|
|
50
50
|
- [Examples](query/examples.md): Complete queries for pages most sites have. They use the example schema and the Next.js App Router, and assume a pages root without i18n unless noted.
|
|
51
51
|
|
|
@@ -80,7 +80,7 @@ The documentation of this version of Alinea, one Markdown file per page of https
|
|
|
80
80
|
- [Code](components/code.md)
|
|
81
81
|
- [Collapsible](components/collapsible.md)
|
|
82
82
|
- [ColorSwatchPicker](components/color-swatch-picker.md)
|
|
83
|
-
- [ComboBox](components/combo-box.md)
|
|
83
|
+
- [ComboBox](components/combo-box.md): Group items under a label with ComboBoxGroup. Like SelectItem, a ComboBoxItem can show an icon and a description below its label.
|
|
84
84
|
- [Command](components/command.md): A searchable list of commands, like a command palette.
|
|
85
85
|
- [ContentCard](components/content-card.md)
|
|
86
86
|
- [ContentGrid](components/content-grid.md): A selectable grid of cards, like the media library.
|
|
@@ -95,13 +95,14 @@ The documentation of this version of Alinea, one Markdown file per page of https
|
|
|
95
95
|
- [Field](components/field.md): The label, help text and error around your own input.
|
|
96
96
|
- [FileTrigger](components/file-trigger.md)
|
|
97
97
|
- [FoldIcon](components/fold-icon.md)
|
|
98
|
+
- [Form](components/form.md): A form whose fields show their validation errors. onSubmit receives the FormData of the named fields instead of the browser submitting the form.
|
|
98
99
|
- [Heading](components/heading.md)
|
|
99
100
|
- [Icon](components/icon.md)
|
|
100
101
|
- [Kbd](components/kbd.md)
|
|
101
|
-
- [Link](components/link.md)
|
|
102
|
+
- [Link](components/link.md): Besides href, the link passes target, rel, download, title, tabIndex, lang, hrefLang, referrerPolicy and aria-current to its <a>.
|
|
102
103
|
- [List](components/list.md)
|
|
103
104
|
- [MediaPreview](components/media-preview.md): A thumbnail of an image or an icon for other files.
|
|
104
|
-
- [MultipleSelect](components/multiple-select.md)
|
|
105
|
+
- [MultipleSelect](components/multiple-select.md): The value is an array of the values of the picked items, strings or numbers.
|
|
105
106
|
- [NavRail](components/nav-rail.md)
|
|
106
107
|
- [NumberField](components/number-field.md): A number input with increment and decrement buttons.
|
|
107
108
|
- [Page](components/page.md): The header, content and footer of a dashboard page.
|
|
@@ -110,10 +111,11 @@ The documentation of this version of Alinea, one Markdown file per page of https
|
|
|
110
111
|
- [RadioGroup](components/radio-group.md)
|
|
111
112
|
- [ResizablePanelGroup](components/resizable-panel-group.md)
|
|
112
113
|
- [SearchField](components/search-field.md): A text input for search queries, with a clear button.
|
|
113
|
-
- [Select](components/select.md): Picks a single value from a list of options. The value is the value of the chosen SelectItem, and
|
|
114
|
+
- [Select](components/select.md): Picks a single value from a list of options. The value is the value of the chosen SelectItem, and onValueChange receives it, typed like the value you pass:…
|
|
114
115
|
- [Sidebar](components/sidebar.md)
|
|
115
116
|
- [SortableList](components/sortable-list.md): Rows that editors reorder by dragging, like list fields.
|
|
116
117
|
- [Spinner](components/spinner.md)
|
|
118
|
+
- [Stack](components/stack.md): Lays out its children in a column, or a row with direction="row", with a gap between them: a number is in pixels, a string any CSS length.
|
|
117
119
|
- [Surface](components/surface.md)
|
|
118
120
|
- [Switch](components/switch.md): Turns a setting on or off, with an immediate effect. For choices that are saved later, as part of a form, use a Checkbox.
|
|
119
121
|
- [Table](components/table.md): Rows and columns of data with sorting, selection, nested rows and drag and drop. Describe the columns up front, then render a TableRow for every item.
|
|
@@ -121,7 +123,7 @@ The documentation of this version of Alinea, one Markdown file per page of https
|
|
|
121
123
|
- [TagGroup](components/tag-group.md)
|
|
122
124
|
- [Text](components/text.md)
|
|
123
125
|
- [TextField](components/text-field.md): A text input with a label, help text and a validation message. Set multiline for a text area that grows with its content.
|
|
124
|
-
- [TimeField](components/time-field.md)
|
|
126
|
+
- [TimeField](components/time-field.md): The value is an ISO time string: 14:30, or 14:30:15 with granularity="second".
|
|
125
127
|
- [Timestamp](components/timestamp.md): A date shown relative to now, with the full date on hover.
|
|
126
128
|
- [Toggle](components/toggle.md): A button that stays pressed until it is pressed again.
|
|
127
129
|
- [ToggleGroup](components/toggle-group.md): A row of toggles where one or several can be pressed.
|
|
@@ -10,6 +10,8 @@ Diagram: Publish flow: the dashboard publishes to your site's handler, which com
|
|
|
10
10
|
|
|
11
11
|
## Skip builds for content-only commits
|
|
12
12
|
|
|
13
|
+
Only set this up once your deployed site has a backend that syncs content at runtime: Alinea Cloud or a self-hosted backend. Without one, a deployment is how new content reaches your site, so skipping content-only builds would leave published changes offline.
|
|
14
|
+
|
|
13
15
|
Because content is synced at runtime, a commit that only changes content does not need a new deployment. On Vercel you can skip those builds with an `ignoreCommand` in `vercel.json`. The command exits successfully, which tells Vercel to skip the build, when nothing outside the `content` directory changed since the last deployment. Comparing against `VERCEL_GIT_PREVIOUS_SHA` covers every commit in a push, not only the last one; on the first deployment of a branch it falls back to the previous commit:
|
|
14
16
|
|
|
15
17
|
File: vercel.json
|
|
@@ -23,6 +25,25 @@ Note (info): Adjust `content` if your content lives in a different directory.
|
|
|
23
25
|
|
|
24
26
|
## Static pages
|
|
25
27
|
|
|
26
|
-
Static pages are revalidated for you. Every page that queries content is tagged, and after each commit the handler marks those pages as stale. The next visit is still answered from the cache while Next.js rebuilds the page in the background, and the visit after that gets the new content. No visitor waits for a render, and no `revalidatePath` call is needed.
|
|
28
|
+
Static pages are revalidated for you. Every page that queries content is tagged, and after each commit the handler marks those pages as stale. The next visit is still answered from the cache while Next.js rebuilds the page in the background, and the visit after that gets the new content. No visitor waits for a render, and no `revalidatePath` call is needed. This includes pages generated with `generateStaticParams`: revalidation code in `afterCommit` that refreshes them by hand, such as `revalidateTag('_N_T_/layout')` or `revalidatePath('/', 'layout')`, can be removed.
|
|
29
|
+
|
|
30
|
+
Pages rendered at request time sync on their next render. The first one after a commit can still show the previous version while the shared content sha refreshes. Content pushed to your repository without the dashboard, for example a local edit, skips the handler: pages pick it up within five minutes, or within `syncInterval` seconds when your config sets it. Use the handler's `afterCommit` hook if other caches in your app depend on content, for example your own `fetch` tags. It receives the changed `entries` and the `removed` ones, see [Commit hooks](page:guides/deploy#commit-hooks):
|
|
31
|
+
|
|
32
|
+
File: app/(alinea)/api/cms/route.ts
|
|
33
|
+
```tsx
|
|
34
|
+
import {cms} from '@/cms'
|
|
35
|
+
import {createHandler} from 'alinea/next'
|
|
36
|
+
import {revalidateTag} from 'next/cache'
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
const handler = createHandler({
|
|
39
|
+
cms,
|
|
40
|
+
afterCommit({entries, removed}) {
|
|
41
|
+
const changed = [...entries, ...removed]
|
|
42
|
+
if (changed.some(entry => entry.type === 'Product'))
|
|
43
|
+
revalidateTag('products', 'max')
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export const GET = handler
|
|
48
|
+
export const POST = handler
|
|
49
|
+
```
|