@coffer-org/web-ui 4.0.0
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/README.md +87 -0
- package/dist/Markdown.d.ts +4 -0
- package/dist/Markdown.js +13 -0
- package/dist/MarkdownEditor.d.ts +6 -0
- package/dist/MarkdownEditor.js +83 -0
- package/dist/actions/ActionButton.d.ts +7 -0
- package/dist/actions/ActionButton.js +54 -0
- package/dist/actions/PopupBody.d.ts +4 -0
- package/dist/actions/PopupBody.js +8 -0
- package/dist/actions/TemplateHost.d.ts +4 -0
- package/dist/actions/TemplateHost.js +36 -0
- package/dist/actions/registry.d.ts +20 -0
- package/dist/actions/registry.js +9 -0
- package/dist/components/ExtendReasonInfo.d.ts +6 -0
- package/dist/components/ExtendReasonInfo.js +12 -0
- package/dist/components/FavoriteButton.d.ts +8 -0
- package/dist/components/FavoriteButton.js +7 -0
- package/dist/components/Icon.d.ts +6 -0
- package/dist/components/Icon.js +12 -0
- package/dist/components/PageBody.d.ts +4 -0
- package/dist/components/PageBody.js +4 -0
- package/dist/components/RecordInline.d.ts +8 -0
- package/dist/components/RecordInline.js +29 -0
- package/dist/components/SaveStatus.d.ts +1 -0
- package/dist/components/SaveStatus.js +11 -0
- package/dist/components/Toolbar.d.ts +5 -0
- package/dist/components/Toolbar.js +4 -0
- package/dist/components/icons/color.d.ts +2 -0
- package/dist/components/icons/color.js +10 -0
- package/dist/components/icons/flag.d.ts +2 -0
- package/dist/components/icons/flag.js +6 -0
- package/dist/components/icons/lucide.d.ts +2 -0
- package/dist/components/icons/lucide.js +111 -0
- package/dist/components/icons/registry.d.ts +4 -0
- package/dist/components/icons/registry.js +22 -0
- package/dist/components/icons/simple.d.ts +2 -0
- package/dist/components/icons/simple.js +15 -0
- package/dist/components/icons/tabler.d.ts +2 -0
- package/dist/components/icons/tabler.js +11 -0
- package/dist/components/ui/FieldLabel.d.ts +9 -0
- package/dist/components/ui/FieldLabel.js +4 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/badge.js +19 -0
- package/dist/components/ui/button.d.ts +11 -0
- package/dist/components/ui/button.js +33 -0
- package/dist/components/ui/calendar.d.ts +8 -0
- package/dist/components/ui/calendar.js +72 -0
- package/dist/components/ui/card.d.ts +8 -0
- package/dist/components/ui/card.js +16 -0
- package/dist/components/ui/checkbox.d.ts +4 -0
- package/dist/components/ui/checkbox.js +8 -0
- package/dist/components/ui/chip-cls.d.ts +1 -0
- package/dist/components/ui/chip-cls.js +1 -0
- package/dist/components/ui/combobox.d.ts +24 -0
- package/dist/components/ui/combobox.js +58 -0
- package/dist/components/ui/confirm.d.ts +10 -0
- package/dist/components/ui/confirm.js +37 -0
- package/dist/components/ui/date-picker.d.ts +5 -0
- package/dist/components/ui/date-picker.js +16 -0
- package/dist/components/ui/dialog.d.ts +16 -0
- package/dist/components/ui/dialog.js +27 -0
- package/dist/components/ui/dropdown-menu.d.ts +15 -0
- package/dist/components/ui/dropdown-menu.js +18 -0
- package/dist/components/ui/input-group.d.ts +16 -0
- package/dist/components/ui/input-group.js +56 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/input.js +6 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/label.js +7 -0
- package/dist/components/ui/mask-input.d.ts +61 -0
- package/dist/components/ui/mask-input.js +1052 -0
- package/dist/components/ui/month-year-picker.d.ts +10 -0
- package/dist/components/ui/month-year-picker.js +46 -0
- package/dist/components/ui/option-icon.d.ts +15 -0
- package/dist/components/ui/option-icon.js +19 -0
- package/dist/components/ui/phone-input.d.ts +13 -0
- package/dist/components/ui/phone-input.js +53 -0
- package/dist/components/ui/popover.d.ts +11 -0
- package/dist/components/ui/popover.js +28 -0
- package/dist/components/ui/rating.d.ts +44 -0
- package/dist/components/ui/rating.js +501 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/scroll-area.js +11 -0
- package/dist/components/ui/select-combobox.d.ts +25 -0
- package/dist/components/ui/select-combobox.js +36 -0
- package/dist/components/ui/select.d.ts +13 -0
- package/dist/components/ui/select.js +24 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/separator.js +7 -0
- package/dist/components/ui/switch.d.ts +4 -0
- package/dist/components/ui/switch.js +7 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/components/ui/textarea.js +6 -0
- package/dist/components/ui/time-picker.d.ts +84 -0
- package/dist/components/ui/time-picker.js +1258 -0
- package/dist/components/visually-hidden-input.d.ts +10 -0
- package/dist/components/visually-hidden-input.js +96 -0
- package/dist/hooks/use-as-ref.d.ts +3 -0
- package/dist/hooks/use-as-ref.js +10 -0
- package/dist/hooks/use-isomorphic-layout-effect.d.ts +3 -0
- package/dist/hooks/use-isomorphic-layout-effect.js +3 -0
- package/dist/hooks/use-lazy-ref.d.ts +3 -0
- package/dist/hooks/use-lazy-ref.js +9 -0
- package/dist/hooks/use-media-query.d.ts +3 -0
- package/dist/hooks/use-media-query.js +26 -0
- package/dist/host-services.d.ts +47 -0
- package/dist/host-services.js +35 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +69 -0
- package/dist/layout.d.ts +29 -0
- package/dist/layout.js +323 -0
- package/dist/lib/cat-color.d.ts +1 -0
- package/dist/lib/cat-color.js +7 -0
- package/dist/lib/compose-refs.d.ts +5 -0
- package/dist/lib/compose-refs.js +38 -0
- package/dist/lib/date-format.d.ts +9 -0
- package/dist/lib/date-format.js +31 -0
- package/dist/lib/format.d.ts +14 -0
- package/dist/lib/format.js +34 -0
- package/dist/lib/json.d.ts +1 -0
- package/dist/lib/json.js +12 -0
- package/dist/lib/multi-value.d.ts +3 -0
- package/dist/lib/multi-value.js +22 -0
- package/dist/lib/record-option.d.ts +7 -0
- package/dist/lib/record-option.js +52 -0
- package/dist/lib/use-record-labels.d.ts +14 -0
- package/dist/lib/use-record-labels.js +130 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/list-view.d.ts +26 -0
- package/dist/list-view.js +11 -0
- package/dist/markdown/blocks.d.ts +10 -0
- package/dist/markdown/blocks.js +57 -0
- package/dist/markdown/extensions.d.ts +6 -0
- package/dist/markdown/extensions.js +25 -0
- package/dist/markdown/slash.d.ts +23 -0
- package/dist/markdown/slash.js +27 -0
- package/dist/markdown/toolbar.d.ts +13 -0
- package/dist/markdown/toolbar.js +119 -0
- package/dist/plugin-ui.d.ts +43 -0
- package/dist/plugin-ui.js +37 -0
- package/dist/registry.d.ts +26 -0
- package/dist/registry.js +169 -0
- package/dist/render/blocks/accordion.d.ts +1 -0
- package/dist/render/blocks/accordion.js +15 -0
- package/dist/render/blocks/aside.d.ts +1 -0
- package/dist/render/blocks/aside.js +14 -0
- package/dist/render/blocks/balance.d.ts +1 -0
- package/dist/render/blocks/balance.js +14 -0
- package/dist/render/blocks/bento.d.ts +1 -0
- package/dist/render/blocks/bento.js +24 -0
- package/dist/render/blocks/block-shell.d.ts +15 -0
- package/dist/render/blocks/block-shell.js +20 -0
- package/dist/render/blocks/calendar.d.ts +1 -0
- package/dist/render/blocks/calendar.js +142 -0
- package/dist/render/blocks/callout.d.ts +1 -0
- package/dist/render/blocks/callout.js +21 -0
- package/dist/render/blocks/chart.d.ts +1 -0
- package/dist/render/blocks/chart.js +46 -0
- package/dist/render/blocks/compare.d.ts +1 -0
- package/dist/render/blocks/compare.js +16 -0
- package/dist/render/blocks/countdown.d.ts +1 -0
- package/dist/render/blocks/countdown.js +36 -0
- package/dist/render/blocks/deck.d.ts +1 -0
- package/dist/render/blocks/deck.js +29 -0
- package/dist/render/blocks/epigraph.d.ts +1 -0
- package/dist/render/blocks/epigraph.js +13 -0
- package/dist/render/blocks/facets.d.ts +1 -0
- package/dist/render/blocks/facets.js +23 -0
- package/dist/render/blocks/figure.d.ts +1 -0
- package/dist/render/blocks/figure.js +13 -0
- package/dist/render/blocks/grid.d.ts +1 -0
- package/dist/render/blocks/grid.js +21 -0
- package/dist/render/blocks/heatmap.d.ts +1 -0
- package/dist/render/blocks/heatmap.js +135 -0
- package/dist/render/blocks/idcard.d.ts +1 -0
- package/dist/render/blocks/idcard.js +30 -0
- package/dist/render/blocks/identity.d.ts +1 -0
- package/dist/render/blocks/identity.js +46 -0
- package/dist/render/blocks/index.d.ts +40 -0
- package/dist/render/blocks/index.js +40 -0
- package/dist/render/blocks/journal.d.ts +1 -0
- package/dist/render/blocks/journal.js +50 -0
- package/dist/render/blocks/ledger.d.ts +1 -0
- package/dist/render/blocks/ledger.js +23 -0
- package/dist/render/blocks/manifest.d.ts +1 -0
- package/dist/render/blocks/manifest.js +50 -0
- package/dist/render/blocks/masthead.d.ts +1 -0
- package/dist/render/blocks/masthead.js +15 -0
- package/dist/render/blocks/meter.d.ts +1 -0
- package/dist/render/blocks/meter.js +32 -0
- package/dist/render/blocks/nutrition.d.ts +1 -0
- package/dist/render/blocks/nutrition.js +27 -0
- package/dist/render/blocks/people.d.ts +1 -0
- package/dist/render/blocks/people.js +35 -0
- package/dist/render/blocks/plaque.d.ts +1 -0
- package/dist/render/blocks/plaque.js +15 -0
- package/dist/render/blocks/properties.d.ts +1 -0
- package/dist/render/blocks/properties.js +22 -0
- package/dist/render/blocks/prose.d.ts +1 -0
- package/dist/render/blocks/prose.js +17 -0
- package/dist/render/blocks/receipt.d.ts +1 -0
- package/dist/render/blocks/receipt.js +23 -0
- package/dist/render/blocks/registry.d.ts +16 -0
- package/dist/render/blocks/registry.js +7 -0
- package/dist/render/blocks/relation-source.d.ts +2 -0
- package/dist/render/blocks/relation-source.js +6 -0
- package/dist/render/blocks/route.d.ts +1 -0
- package/dist/render/blocks/route.js +23 -0
- package/dist/render/blocks/score.d.ts +1 -0
- package/dist/render/blocks/score.js +15 -0
- package/dist/render/blocks/series.d.ts +23 -0
- package/dist/render/blocks/series.js +57 -0
- package/dist/render/blocks/specimen.d.ts +1 -0
- package/dist/render/blocks/specimen.js +19 -0
- package/dist/render/blocks/split.d.ts +1 -0
- package/dist/render/blocks/split.js +15 -0
- package/dist/render/blocks/spread.d.ts +1 -0
- package/dist/render/blocks/spread.js +15 -0
- package/dist/render/blocks/stack.d.ts +1 -0
- package/dist/render/blocks/stack.js +15 -0
- package/dist/render/blocks/stats.d.ts +1 -0
- package/dist/render/blocks/stats.js +23 -0
- package/dist/render/blocks/status.d.ts +1 -0
- package/dist/render/blocks/status.js +18 -0
- package/dist/render/blocks/table.d.ts +11 -0
- package/dist/render/blocks/table.js +136 -0
- package/dist/render/blocks/tabs.d.ts +1 -0
- package/dist/render/blocks/tabs.js +17 -0
- package/dist/render/blocks/terminal.d.ts +1 -0
- package/dist/render/blocks/terminal.js +18 -0
- package/dist/render/blocks/timeline.d.ts +1 -0
- package/dist/render/blocks/timeline.js +31 -0
- package/dist/render/box.d.ts +5 -0
- package/dist/render/box.js +23 -0
- package/dist/render/chrome.d.ts +64 -0
- package/dist/render/chrome.js +92 -0
- package/dist/render/core/amount.d.ts +1 -0
- package/dist/render/core/amount.js +19 -0
- package/dist/render/core/attachment.d.ts +29 -0
- package/dist/render/core/attachment.js +102 -0
- package/dist/render/core/choice.d.ts +1 -0
- package/dist/render/core/choice.js +40 -0
- package/dist/render/core/composite.d.ts +1 -0
- package/dist/render/core/composite.js +132 -0
- package/dist/render/core/datetime.d.ts +2 -0
- package/dist/render/core/datetime.js +215 -0
- package/dist/render/core/dimensions.d.ts +1 -0
- package/dist/render/core/dimensions.js +44 -0
- package/dist/render/core/embed.d.ts +11 -0
- package/dist/render/core/embed.js +99 -0
- package/dist/render/core/file.d.ts +12 -0
- package/dist/render/core/file.js +238 -0
- package/dist/render/core/illustrated.d.ts +39 -0
- package/dist/render/core/illustrated.js +113 -0
- package/dist/render/core/index.d.ts +23 -0
- package/dist/render/core/index.js +23 -0
- package/dist/render/core/layout-elements.d.ts +1 -0
- package/dist/render/core/layout-elements.js +78 -0
- package/dist/render/core/localpath.d.ts +6 -0
- package/dist/render/core/localpath.js +73 -0
- package/dist/render/core/money.d.ts +1 -0
- package/dist/render/core/money.js +39 -0
- package/dist/render/core/number.d.ts +17 -0
- package/dist/render/core/number.js +104 -0
- package/dist/render/core/period.d.ts +1 -0
- package/dist/render/core/period.js +60 -0
- package/dist/render/core/range.d.ts +1 -0
- package/dist/render/core/range.js +55 -0
- package/dist/render/core/reminder.d.ts +2 -0
- package/dist/render/core/reminder.js +56 -0
- package/dist/render/core/source.d.ts +3 -0
- package/dist/render/core/source.js +65 -0
- package/dist/render/core/stepper.d.ts +1 -0
- package/dist/render/core/stepper.js +81 -0
- package/dist/render/core/storage-group.d.ts +1 -0
- package/dist/render/core/storage-group.js +30 -0
- package/dist/render/core/text.d.ts +15 -0
- package/dist/render/core/text.js +370 -0
- package/dist/render/core/tristate.d.ts +1 -0
- package/dist/render/core/tristate.js +42 -0
- package/dist/render/core/weight.d.ts +4 -0
- package/dist/render/core/weight.js +38 -0
- package/dist/render/dispatch.d.ts +12 -0
- package/dist/render/dispatch.js +53 -0
- package/dist/render/display-override.d.ts +25 -0
- package/dist/render/display-override.js +23 -0
- package/dist/render/editor-mode.d.ts +4 -0
- package/dist/render/editor-mode.js +11 -0
- package/dist/render/field-address.d.ts +14 -0
- package/dist/render/field-address.js +10 -0
- package/dist/render/field-errors.d.ts +8 -0
- package/dist/render/field-errors.js +25 -0
- package/dist/render/field-frame.d.ts +20 -0
- package/dist/render/field-frame.js +78 -0
- package/dist/render/field-scope.d.ts +8 -0
- package/dist/render/field-scope.js +25 -0
- package/dist/render/field-width.d.ts +7 -0
- package/dist/render/field-width.js +12 -0
- package/dist/render/form-types.d.ts +12 -0
- package/dist/render/form-types.js +1 -0
- package/dist/render/hybrid-scope.d.ts +5 -0
- package/dist/render/hybrid-scope.js +9 -0
- package/dist/render/image.d.ts +64 -0
- package/dist/render/image.js +322 -0
- package/dist/render/live-kind.d.ts +7 -0
- package/dist/render/live-kind.js +104 -0
- package/dist/render/markdown-view.d.ts +5 -0
- package/dist/render/markdown-view.js +17 -0
- package/dist/render/multi.d.ts +10 -0
- package/dist/render/multi.js +26 -0
- package/dist/render/multiple-chips.d.ts +24 -0
- package/dist/render/multiple-chips.js +18 -0
- package/dist/render/option-list.d.ts +15 -0
- package/dist/render/option-list.js +47 -0
- package/dist/render/option-value.d.ts +20 -0
- package/dist/render/option-value.js +41 -0
- package/dist/render/picker-scrim.d.ts +7 -0
- package/dist/render/picker-scrim.js +32 -0
- package/dist/render/picker-surface.d.ts +5 -0
- package/dist/render/picker-surface.js +9 -0
- package/dist/render/popover-field.d.ts +16 -0
- package/dist/render/popover-field.js +153 -0
- package/dist/render/record-save-ctx.d.ts +11 -0
- package/dist/render/record-save-ctx.js +41 -0
- package/dist/render/shared.d.ts +92 -0
- package/dist/render/shared.js +182 -0
- package/dist/render/slot.d.ts +45 -0
- package/dist/render/slot.js +194 -0
- package/dist/render/sort-rows.d.ts +3 -0
- package/dist/render/sort-rows.js +38 -0
- package/dist/render/use-collection-rows.d.ts +10 -0
- package/dist/render/use-collection-rows.js +11 -0
- package/dist/render/use-field-edit.d.ts +28 -0
- package/dist/render/use-field-edit.js +50 -0
- package/dist/render/use-file-upload.d.ts +10 -0
- package/dist/render/use-file-upload.js +35 -0
- package/dist/render/use-form-bridge.d.ts +21 -0
- package/dist/render/use-form-bridge.js +53 -0
- package/dist/render/use-hybrid.d.ts +2 -0
- package/dist/render/use-hybrid.js +5 -0
- package/dist/render/widgets/attachment-group.d.ts +1 -0
- package/dist/render/widgets/attachment-group.js +35 -0
- package/dist/render/widgets/checklist-content.d.ts +15 -0
- package/dist/render/widgets/checklist-content.js +12 -0
- package/dist/render/widgets/checklist-group.d.ts +11 -0
- package/dist/render/widgets/checklist-group.js +47 -0
- package/dist/render/widgets/checklist-row.d.ts +14 -0
- package/dist/render/widgets/checklist-row.js +14 -0
- package/dist/render/widgets/display.d.ts +33 -0
- package/dist/render/widgets/display.js +97 -0
- package/dist/render/widgets/edit.d.ts +63 -0
- package/dist/render/widgets/edit.js +283 -0
- package/dist/render/widgets/fixed-collection.d.ts +11 -0
- package/dist/render/widgets/fixed-collection.js +34 -0
- package/dist/render/widgets/flow-row.d.ts +14 -0
- package/dist/render/widgets/flow-row.js +27 -0
- package/dist/render/widgets/group-edit.d.ts +9 -0
- package/dist/render/widgets/group-edit.js +23 -0
- package/dist/render/widgets/group-field.d.ts +9 -0
- package/dist/render/widgets/group-field.js +42 -0
- package/dist/render/widgets/group-widgets.d.ts +2 -0
- package/dist/render/widgets/group-widgets.js +41 -0
- package/dist/render/widgets/group.d.ts +26 -0
- package/dist/render/widgets/group.js +139 -0
- package/dist/render/widgets/illustrated-group.d.ts +1 -0
- package/dist/render/widgets/illustrated-group.js +41 -0
- package/dist/render/widgets/table-group.d.ts +11 -0
- package/dist/render/widgets/table-group.js +58 -0
- package/dist/render/widgets/two-cell-group.d.ts +21 -0
- package/dist/render/widgets/two-cell-group.js +42 -0
- package/dist/render/widgets/url-widget.d.ts +7 -0
- package/dist/render/widgets/url-widget.js +15 -0
- package/dist/render/widgets/weekday-grid.d.ts +13 -0
- package/dist/render/widgets/weekday-grid.js +47 -0
- package/dist/render/write.d.ts +5 -0
- package/dist/render/write.js +14 -0
- package/dist/schema-registry.d.ts +6 -0
- package/dist/schema-registry.js +25 -0
- package/dist/site-links.d.ts +13 -0
- package/dist/site-links.js +18 -0
- package/dist/strip-markdown.d.ts +1 -0
- package/dist/strip-markdown.js +11 -0
- package/dist/theme.css +189 -0
- package/dist/types.d.ts +84 -0
- package/dist/types.js +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { inlineText } from '@coffer-org/sdk/shelf';
|
|
3
|
+
import { getShelf } from "../schema-registry.js";
|
|
4
|
+
import { listRecords } from "../host-services.js";
|
|
5
|
+
const inFlight = new Map();
|
|
6
|
+
const byIdCache = new Map();
|
|
7
|
+
const listCache = new Map();
|
|
8
|
+
function cacheRows(library, shelf, rows) {
|
|
9
|
+
for (const r of rows)
|
|
10
|
+
byIdCache.set(`${library}/${shelf}/${String(r['id'])}`, r);
|
|
11
|
+
}
|
|
12
|
+
const inFlightById = new Map();
|
|
13
|
+
function fetchByIds(library, shelf, idsCsv) {
|
|
14
|
+
const key = `${library}/${shelf}/${idsCsv}`;
|
|
15
|
+
const existing = inFlightById.get(key);
|
|
16
|
+
if (existing)
|
|
17
|
+
return existing;
|
|
18
|
+
const p = listRecords(library, shelf, { id: idsCsv })
|
|
19
|
+
.then((rows) => {
|
|
20
|
+
cacheRows(library, shelf, rows);
|
|
21
|
+
return rows;
|
|
22
|
+
})
|
|
23
|
+
.finally(() => inFlightById.delete(key));
|
|
24
|
+
inFlightById.set(key, p);
|
|
25
|
+
return p;
|
|
26
|
+
}
|
|
27
|
+
export function clearRecordByIdCache() {
|
|
28
|
+
byIdCache.clear();
|
|
29
|
+
listCache.clear();
|
|
30
|
+
}
|
|
31
|
+
function filterKey(filter) {
|
|
32
|
+
if (!filter)
|
|
33
|
+
return '';
|
|
34
|
+
const keys = Object.keys(filter).sort();
|
|
35
|
+
return keys.length ? '?' + keys.map((k) => `${k}=${filter[k]}`).join('&') : '';
|
|
36
|
+
}
|
|
37
|
+
function fetchRows(library, shelf, filter) {
|
|
38
|
+
const key = `${library}/${shelf}${filterKey(filter)}`;
|
|
39
|
+
const cached = listCache.get(key);
|
|
40
|
+
if (cached)
|
|
41
|
+
return Promise.resolve(cached);
|
|
42
|
+
const existing = inFlight.get(key);
|
|
43
|
+
if (existing)
|
|
44
|
+
return existing;
|
|
45
|
+
const p = listRecords(library, shelf, filter)
|
|
46
|
+
.then((rows) => {
|
|
47
|
+
cacheRows(library, shelf, rows);
|
|
48
|
+
listCache.set(key, rows);
|
|
49
|
+
return rows;
|
|
50
|
+
})
|
|
51
|
+
.finally(() => inFlight.delete(key));
|
|
52
|
+
inFlight.set(key, p);
|
|
53
|
+
return p;
|
|
54
|
+
}
|
|
55
|
+
export function useRecordLabels(library, shelf, displayKey = 'name', filter) {
|
|
56
|
+
const [records, setRecords] = useState([]);
|
|
57
|
+
const [loadState, setLoadState] = useState('loading');
|
|
58
|
+
const filterDeps = filterKey(filter);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!library || !shelf) {
|
|
61
|
+
setLoadState('idle');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let alive = true;
|
|
65
|
+
setLoadState('loading');
|
|
66
|
+
fetchRows(library, shelf, filter)
|
|
67
|
+
.then((r) => {
|
|
68
|
+
if (!alive)
|
|
69
|
+
return;
|
|
70
|
+
setRecords(r);
|
|
71
|
+
setLoadState('idle');
|
|
72
|
+
})
|
|
73
|
+
.catch(() => {
|
|
74
|
+
if (alive)
|
|
75
|
+
setLoadState('error');
|
|
76
|
+
});
|
|
77
|
+
return () => {
|
|
78
|
+
alive = false;
|
|
79
|
+
};
|
|
80
|
+
}, [library, shelf, filterDeps]);
|
|
81
|
+
const rows = useMemo(() => {
|
|
82
|
+
const m = getShelf(library, shelf);
|
|
83
|
+
return records.map((x) => ({
|
|
84
|
+
id: String(x['id']),
|
|
85
|
+
label: m ? inlineText(m, x) : String(x[displayKey] ?? x['name'] ?? x['id']),
|
|
86
|
+
}));
|
|
87
|
+
}, [records, library, shelf, displayKey]);
|
|
88
|
+
const labelMap = useMemo(() => new Map(rows.map((r) => [r.id, r.label])), [rows]);
|
|
89
|
+
const recordMap = useMemo(() => new Map(records.map((r) => [String(r['id']), r])), [records]);
|
|
90
|
+
return { rows, labelMap, records, recordMap, loadState };
|
|
91
|
+
}
|
|
92
|
+
export function useRecordsByIds(library, shelf, ids) {
|
|
93
|
+
const key = useMemo(() => [...new Set(ids)].sort().join(','), [ids]);
|
|
94
|
+
const [map, setMap] = useState(new Map());
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!library || !shelf || !key) {
|
|
97
|
+
setMap(new Map());
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const resolved = new Map();
|
|
101
|
+
const missing = [];
|
|
102
|
+
for (const id of key.split(',')) {
|
|
103
|
+
const hit = byIdCache.get(`${library}/${shelf}/${id}`);
|
|
104
|
+
if (hit)
|
|
105
|
+
resolved.set(id, hit);
|
|
106
|
+
else
|
|
107
|
+
missing.push(id);
|
|
108
|
+
}
|
|
109
|
+
if (!missing.length) {
|
|
110
|
+
setMap(resolved);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
let alive = true;
|
|
114
|
+
fetchByIds(library, shelf, missing.join(','))
|
|
115
|
+
.then((rows) => {
|
|
116
|
+
for (const r of rows)
|
|
117
|
+
resolved.set(String(r['id']), r);
|
|
118
|
+
if (alive)
|
|
119
|
+
setMap(new Map(resolved));
|
|
120
|
+
})
|
|
121
|
+
.catch(() => {
|
|
122
|
+
if (alive)
|
|
123
|
+
setMap(resolved);
|
|
124
|
+
});
|
|
125
|
+
return () => {
|
|
126
|
+
alive = false;
|
|
127
|
+
};
|
|
128
|
+
}, [library, shelf, key]);
|
|
129
|
+
return map;
|
|
130
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { ShelfDef } from '@coffer-org/sdk/shelf';
|
|
3
|
+
export interface ListViewProps {
|
|
4
|
+
rows: Record<string, unknown>[];
|
|
5
|
+
shelf: ShelfDef;
|
|
6
|
+
keys: string[];
|
|
7
|
+
hrefBase: string;
|
|
8
|
+
onDelete: (id: string) => void;
|
|
9
|
+
deletingId: string | null;
|
|
10
|
+
onToggleFavorite?: (id: string) => void;
|
|
11
|
+
favoriteBusyId?: string | null;
|
|
12
|
+
sort: {
|
|
13
|
+
key: string;
|
|
14
|
+
dir: 'asc' | 'desc';
|
|
15
|
+
} | null;
|
|
16
|
+
onToggleSort: (key: string) => void;
|
|
17
|
+
scrollKey?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ListViewDef {
|
|
20
|
+
kind: string;
|
|
21
|
+
View: FC<ListViewProps>;
|
|
22
|
+
check?: (shelf: ShelfDef, keys: string[]) => string | null;
|
|
23
|
+
}
|
|
24
|
+
export declare const listViewRegistry: Record<string, ListViewDef>;
|
|
25
|
+
export declare function registerListView(def: ListViewDef): void;
|
|
26
|
+
export declare function resolveListView(kind: string): ListViewDef | undefined;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const listViewRegistry = {};
|
|
2
|
+
export function registerListView(def) {
|
|
3
|
+
if (!def.kind)
|
|
4
|
+
throw new Error('[list-view] view without kind');
|
|
5
|
+
if (listViewRegistry[def.kind])
|
|
6
|
+
throw new Error(`[list-view] kind '${def.kind}' already registered`);
|
|
7
|
+
listViewRegistry[def.kind] = def;
|
|
8
|
+
}
|
|
9
|
+
export function resolveListView(kind) {
|
|
10
|
+
return listViewRegistry[kind];
|
|
11
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Editor } from '@tiptap/core';
|
|
2
|
+
import { type LucideIcon } from 'lucide-react';
|
|
3
|
+
import type { SlashRange } from './slash.tsx';
|
|
4
|
+
export type BlockAction = {
|
|
5
|
+
key: string;
|
|
6
|
+
icon: LucideIcon;
|
|
7
|
+
run: (editor: Editor, range?: SlashRange) => void;
|
|
8
|
+
active?: (editor: Editor) => boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare const BLOCKS: BlockAction[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Heading1, Heading2, Heading3, List, ListOrdered, ListChecks, Quote, Code2, Minus, Table as TableIcon, } from 'lucide-react';
|
|
2
|
+
const at = (editor, range) => {
|
|
3
|
+
const chain = editor.chain().focus();
|
|
4
|
+
return range ? chain.deleteRange(range) : chain;
|
|
5
|
+
};
|
|
6
|
+
const heading = (level, icon) => ({
|
|
7
|
+
key: `h${level}`,
|
|
8
|
+
icon,
|
|
9
|
+
run: (editor, range) => at(editor, range).toggleHeading({ level }).run(),
|
|
10
|
+
active: (editor) => editor.isActive('heading', { level }),
|
|
11
|
+
});
|
|
12
|
+
export const BLOCKS = [
|
|
13
|
+
heading(1, Heading1),
|
|
14
|
+
heading(2, Heading2),
|
|
15
|
+
heading(3, Heading3),
|
|
16
|
+
{
|
|
17
|
+
key: 'bulletList',
|
|
18
|
+
icon: List,
|
|
19
|
+
run: (editor, range) => at(editor, range).toggleBulletList().run(),
|
|
20
|
+
active: (editor) => editor.isActive('bulletList'),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: 'orderedList',
|
|
24
|
+
icon: ListOrdered,
|
|
25
|
+
run: (editor, range) => at(editor, range).toggleOrderedList().run(),
|
|
26
|
+
active: (editor) => editor.isActive('orderedList'),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: 'taskList',
|
|
30
|
+
icon: ListChecks,
|
|
31
|
+
run: (editor, range) => at(editor, range).toggleTaskList().run(),
|
|
32
|
+
active: (editor) => editor.isActive('taskList'),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: 'blockquote',
|
|
36
|
+
icon: Quote,
|
|
37
|
+
run: (editor, range) => at(editor, range).toggleBlockquote().run(),
|
|
38
|
+
active: (editor) => editor.isActive('blockquote'),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: 'codeBlock',
|
|
42
|
+
icon: Code2,
|
|
43
|
+
run: (editor, range) => at(editor, range).toggleCodeBlock().run(),
|
|
44
|
+
active: (editor) => editor.isActive('codeBlock'),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: 'horizontalRule',
|
|
48
|
+
icon: Minus,
|
|
49
|
+
run: (editor, range) => at(editor, range).setHorizontalRule().run(),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: 'table',
|
|
53
|
+
icon: TableIcon,
|
|
54
|
+
run: (editor, range) => at(editor, range).insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(),
|
|
55
|
+
active: (editor) => editor.isActive('table'),
|
|
56
|
+
},
|
|
57
|
+
];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
2
|
+
import { Placeholder } from '@tiptap/extensions';
|
|
3
|
+
import { TaskList, TaskItem } from '@tiptap/extension-list';
|
|
4
|
+
import { Table, TableRow, TableHeader, TableCell } from '@tiptap/extension-table';
|
|
5
|
+
import Image from '@tiptap/extension-image';
|
|
6
|
+
import { Markdown } from 'tiptap-markdown';
|
|
7
|
+
import { slashMenu } from "./slash.js";
|
|
8
|
+
export function markdownExtensions(opts) {
|
|
9
|
+
return [
|
|
10
|
+
StarterKit.configure({
|
|
11
|
+
link: { openOnClick: false },
|
|
12
|
+
underline: false,
|
|
13
|
+
}),
|
|
14
|
+
TaskList,
|
|
15
|
+
TaskItem.configure({ nested: true }),
|
|
16
|
+
Table,
|
|
17
|
+
TableRow,
|
|
18
|
+
TableHeader,
|
|
19
|
+
TableCell,
|
|
20
|
+
Image.configure({ inline: false }),
|
|
21
|
+
Markdown.configure({ transformPastedText: true, linkify: false }),
|
|
22
|
+
...(opts.placeholder ? [Placeholder.configure({ placeholder: opts.placeholder })] : []),
|
|
23
|
+
...(opts.slash ? [slashMenu(opts.slash)] : []),
|
|
24
|
+
];
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Extension, type Editor } from '@tiptap/core';
|
|
2
|
+
export type SlashRange = {
|
|
3
|
+
from: number;
|
|
4
|
+
to: number;
|
|
5
|
+
};
|
|
6
|
+
export type SlashItem = {
|
|
7
|
+
key: string;
|
|
8
|
+
run: (ctx: {
|
|
9
|
+
editor: Editor;
|
|
10
|
+
range: SlashRange;
|
|
11
|
+
}) => void;
|
|
12
|
+
};
|
|
13
|
+
export type SlashHandlers = {
|
|
14
|
+
filter: (query: string) => SlashItem[];
|
|
15
|
+
open: (state: {
|
|
16
|
+
rect: DOMRect | null;
|
|
17
|
+
items: SlashItem[];
|
|
18
|
+
pick: (item: SlashItem) => void;
|
|
19
|
+
}) => void;
|
|
20
|
+
close: () => void;
|
|
21
|
+
keyDown: (event: KeyboardEvent) => boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare function slashMenu(handlers: SlashHandlers): Extension;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import Suggestion from '@tiptap/suggestion';
|
|
3
|
+
export function slashMenu(handlers) {
|
|
4
|
+
return Extension.create({
|
|
5
|
+
name: 'slashMenu',
|
|
6
|
+
addProseMirrorPlugins() {
|
|
7
|
+
return [
|
|
8
|
+
Suggestion({
|
|
9
|
+
editor: this.editor,
|
|
10
|
+
char: '/',
|
|
11
|
+
allowedPrefixes: [' ', '\n'],
|
|
12
|
+
items: ({ query }) => handlers.filter(query),
|
|
13
|
+
command: ({ editor, range, props }) => props.run({ editor, range }),
|
|
14
|
+
render: () => {
|
|
15
|
+
const show = (props) => handlers.open({ rect: props.clientRect?.() ?? null, items: props.items, pick: props.command });
|
|
16
|
+
return {
|
|
17
|
+
onStart: show,
|
|
18
|
+
onUpdate: show,
|
|
19
|
+
onKeyDown: ({ event }) => handlers.keyDown(event),
|
|
20
|
+
onExit: () => handlers.close(),
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
];
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Editor } from '@tiptap/core';
|
|
2
|
+
import { type LucideIcon } from 'lucide-react';
|
|
3
|
+
export declare const ToolButton: import("react").ForwardRefExoticComponent<{
|
|
4
|
+
icon: LucideIcon;
|
|
5
|
+
label: string;
|
|
6
|
+
active?: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
tone?: "danger";
|
|
9
|
+
onClick?: () => void;
|
|
10
|
+
} & Omit<Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "onClick"> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
11
|
+
export declare function Toolbar({ editor }: {
|
|
12
|
+
editor: Editor;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, forwardRef } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { useEditorState } from '@tiptap/react';
|
|
5
|
+
import { Bold, Italic, Strikethrough, Code, Link as LinkIcon, Unlink, ImagePlus, Undo2, Redo2, Check, BetweenVerticalEnd, BetweenHorizontalEnd, Rows3, Columns3, Grid2x2X, } from 'lucide-react';
|
|
6
|
+
import { Input } from "../components/ui/input.js";
|
|
7
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../components/ui/popover.js";
|
|
8
|
+
import { BLOCKS } from "./blocks.js";
|
|
9
|
+
export const ToolButton = forwardRef(({ icon: Icon, label, active, disabled, tone, onClick, ...rest }, ref) => (_jsx("button", { ref: ref, type: "button", "aria-label": label, title: label, "aria-pressed": active ?? false, disabled: disabled, onClick: onClick, ...rest, className: `flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent disabled:opacity-40 ${tone === 'danger' ? 'hover:bg-danger/10 hover:text-danger' : 'hover:bg-elevated hover:text-text'} ${active ? 'bg-elevated text-accent' : ''}`, children: _jsx(Icon, { size: 15 }) })));
|
|
10
|
+
ToolButton.displayName = 'ToolButton';
|
|
11
|
+
const Divider = () => _jsx("span", { className: "mx-0.5 h-5 w-px shrink-0 bg-border", "aria-hidden": true });
|
|
12
|
+
const MARKS = [
|
|
13
|
+
{ key: 'bold', icon: Bold, run: (e) => e.chain().focus().toggleBold().run(), active: (e) => e.isActive('bold') },
|
|
14
|
+
{
|
|
15
|
+
key: 'italic',
|
|
16
|
+
icon: Italic,
|
|
17
|
+
run: (e) => e.chain().focus().toggleItalic().run(),
|
|
18
|
+
active: (e) => e.isActive('italic'),
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
key: 'strike',
|
|
22
|
+
icon: Strikethrough,
|
|
23
|
+
run: (e) => e.chain().focus().toggleStrike().run(),
|
|
24
|
+
active: (e) => e.isActive('strike'),
|
|
25
|
+
},
|
|
26
|
+
{ key: 'code', icon: Code, run: (e) => e.chain().focus().toggleCode().run(), active: (e) => e.isActive('code') },
|
|
27
|
+
];
|
|
28
|
+
function LinkForm({ editor, onDone }) {
|
|
29
|
+
const { t } = useTranslation();
|
|
30
|
+
const [href, setHref] = useState(editor.getAttributes('link').href ?? '');
|
|
31
|
+
const apply = () => {
|
|
32
|
+
const chain = editor.chain().focus().extendMarkRange('link');
|
|
33
|
+
if (href.trim() === '')
|
|
34
|
+
chain.unsetLink().run();
|
|
35
|
+
else
|
|
36
|
+
chain.setLink({ href: href.trim() }).run();
|
|
37
|
+
onDone();
|
|
38
|
+
};
|
|
39
|
+
return (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Input, { autoFocus: true, value: href, placeholder: "https://", "aria-label": t('form.mdLink'), onChange: (e) => setHref(e.target.value), onKeyDown: (e) => {
|
|
40
|
+
if (e.key === 'Enter') {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
apply();
|
|
43
|
+
}
|
|
44
|
+
if (e.key === 'Escape') {
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
onDone();
|
|
47
|
+
}
|
|
48
|
+
}, className: "h-7 flex-1 text-sm" }), _jsx(ToolButton, { icon: Check, label: t('actions.save'), onClick: apply }), _jsx(ToolButton, { icon: Unlink, label: t('form.md.unlink'), onClick: () => {
|
|
49
|
+
editor.chain().focus().extendMarkRange('link').unsetLink().run();
|
|
50
|
+
onDone();
|
|
51
|
+
} })] }));
|
|
52
|
+
}
|
|
53
|
+
function ImageForm({ editor, onDone }) {
|
|
54
|
+
const { t } = useTranslation();
|
|
55
|
+
const [src, setSrc] = useState('');
|
|
56
|
+
const [alt, setAlt] = useState('');
|
|
57
|
+
const apply = () => {
|
|
58
|
+
const address = src.trim();
|
|
59
|
+
if (address)
|
|
60
|
+
editor
|
|
61
|
+
.chain()
|
|
62
|
+
.focus()
|
|
63
|
+
.setImage({ src: address, alt: alt.trim() || undefined })
|
|
64
|
+
.run();
|
|
65
|
+
onDone();
|
|
66
|
+
};
|
|
67
|
+
return (_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx(Input, { autoFocus: true, value: src, placeholder: "https://\u2026 /uploads/\u2026", "aria-label": t('form.md.imageUrl'), onChange: (e) => setSrc(e.target.value), onKeyDown: (e) => {
|
|
68
|
+
if (e.key === 'Enter') {
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
apply();
|
|
71
|
+
}
|
|
72
|
+
if (e.key === 'Escape') {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
onDone();
|
|
75
|
+
}
|
|
76
|
+
}, className: "h-7 min-w-0 flex-1 text-sm" }), _jsx(Input, { value: alt, placeholder: t('form.md.imageAlt'), "aria-label": t('form.md.imageAlt'), onChange: (e) => setAlt(e.target.value), onKeyDown: (e) => {
|
|
77
|
+
if (e.key === 'Enter') {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
apply();
|
|
80
|
+
}
|
|
81
|
+
if (e.key === 'Escape') {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
onDone();
|
|
84
|
+
}
|
|
85
|
+
}, className: "h-7 w-32 text-sm" }), _jsx(ToolButton, { icon: Check, label: t('actions.save'), disabled: !src.trim(), onClick: apply })] }));
|
|
86
|
+
}
|
|
87
|
+
function TableTools({ editor }) {
|
|
88
|
+
const { t } = useTranslation();
|
|
89
|
+
return (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsx(ToolButton, { icon: BetweenVerticalEnd, label: t('form.md.addRow'), onClick: () => editor.chain().focus().addRowAfter().run() }), _jsx(ToolButton, { icon: BetweenHorizontalEnd, label: t('form.md.addColumn'), onClick: () => editor.chain().focus().addColumnAfter().run() }), _jsx(ToolButton, { icon: Rows3, tone: "danger", label: t('form.md.deleteRow'), onClick: () => editor.chain().focus().deleteRow().run() }), _jsx(ToolButton, { icon: Columns3, tone: "danger", label: t('form.md.deleteColumn'), onClick: () => editor.chain().focus().deleteColumn().run() }), _jsx(ToolButton, { icon: Grid2x2X, tone: "danger", label: t('form.md.deleteTable'), onClick: () => editor.chain().focus().deleteTable().run() })] }));
|
|
90
|
+
}
|
|
91
|
+
function AddressButton({ icon, label, active, open, onOpenChange, children, }) {
|
|
92
|
+
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(ToolButton, { icon: icon, label: label, active: active || open }) }), _jsx(PopoverContent, { align: "start", className: "w-80 p-2", children: children })] }));
|
|
93
|
+
}
|
|
94
|
+
export function Toolbar({ editor }) {
|
|
95
|
+
const { t } = useTranslation();
|
|
96
|
+
const [form, setForm] = useState(null);
|
|
97
|
+
const state = useEditorState({
|
|
98
|
+
editor,
|
|
99
|
+
selector: ({ editor: e }) => e && !e.isDestroyed
|
|
100
|
+
? {
|
|
101
|
+
marks: MARKS.map((m) => m.active(e)),
|
|
102
|
+
blocks: BLOCKS.map((b) => b.active?.(e) ?? false),
|
|
103
|
+
link: e.isActive('link'),
|
|
104
|
+
table: e.isActive('table'),
|
|
105
|
+
canUndo: e.can().undo(),
|
|
106
|
+
canRedo: e.can().redo(),
|
|
107
|
+
}
|
|
108
|
+
: {
|
|
109
|
+
marks: MARKS.map(() => false),
|
|
110
|
+
blocks: BLOCKS.map(() => false),
|
|
111
|
+
link: false,
|
|
112
|
+
table: false,
|
|
113
|
+
canUndo: false,
|
|
114
|
+
canRedo: false,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
const group = (children) => _jsx("div", { className: "flex items-center gap-0.5", children: children });
|
|
118
|
+
return (_jsx("div", { className: "flex flex-col gap-1.5", children: _jsxs("div", { className: "-mx-0.5 flex items-center gap-0.5 overflow-x-auto overscroll-x-contain border-b border-border px-0.5 pb-1.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [group(_jsxs(_Fragment, { children: [_jsx(ToolButton, { icon: Undo2, label: t('form.md.undo'), disabled: !state.canUndo, onClick: () => editor.chain().focus().undo().run() }), _jsx(ToolButton, { icon: Redo2, label: t('form.md.redo'), disabled: !state.canRedo, onClick: () => editor.chain().focus().redo().run() })] })), _jsx(Divider, {}), group(MARKS.map((m, i) => (_jsx(ToolButton, { icon: m.icon, label: t(`form.md.${m.key}`), active: state.marks[i], onClick: () => m.run(editor) }, m.key)))), _jsx(Divider, {}), group(BLOCKS.map((b, i) => (_jsx(ToolButton, { icon: b.icon, label: t(`form.md.${b.key}`), active: state.blocks[i], onClick: () => b.run(editor) }, b.key)))), _jsx(Divider, {}), group(_jsxs(_Fragment, { children: [_jsx(AddressButton, { icon: LinkIcon, label: t('form.md.link'), active: state.link, open: form === 'link', onOpenChange: (o) => setForm(o ? 'link' : null), children: _jsx(LinkForm, { editor: editor, onDone: () => setForm(null) }) }), _jsx(AddressButton, { icon: ImagePlus, label: t('form.md.image'), open: form === 'image', onOpenChange: (o) => setForm(o ? 'image' : null), children: _jsx(ImageForm, { editor: editor, onDone: () => setForm(null) }) })] })), state.table && _jsx(TableTools, { editor: editor })] }) }));
|
|
119
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { FC, ComponentType } from 'react';
|
|
2
|
+
import type { FieldRenderer, TemplateImporter, RecordViewProps } from './types.ts';
|
|
3
|
+
import type { ListViewDef } from './list-view.ts';
|
|
4
|
+
export interface SlotEntry {
|
|
5
|
+
slot: string;
|
|
6
|
+
id: string;
|
|
7
|
+
load: () => Promise<{
|
|
8
|
+
default: ComponentType;
|
|
9
|
+
}>;
|
|
10
|
+
order?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface RouteEntry {
|
|
13
|
+
path: string;
|
|
14
|
+
load: () => Promise<{
|
|
15
|
+
default: ComponentType;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
export interface NavEntry {
|
|
19
|
+
to: string;
|
|
20
|
+
labelKey: string;
|
|
21
|
+
icon?: string;
|
|
22
|
+
order?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface PluginUIBundle {
|
|
25
|
+
shelf?: {
|
|
26
|
+
library: string;
|
|
27
|
+
shelf: string;
|
|
28
|
+
RecordView?: FC<RecordViewProps>;
|
|
29
|
+
};
|
|
30
|
+
renderers?: FieldRenderer[];
|
|
31
|
+
listViews?: ListViewDef[];
|
|
32
|
+
templates?: Record<string, TemplateImporter<any>>;
|
|
33
|
+
translations?: Partial<Record<'uk' | 'ru' | 'en', Record<string, unknown>>>;
|
|
34
|
+
slots?: SlotEntry[];
|
|
35
|
+
routes?: RouteEntry[];
|
|
36
|
+
nav?: NavEntry[];
|
|
37
|
+
}
|
|
38
|
+
export declare function definePluginUI(b: PluginUIBundle): PluginUIBundle;
|
|
39
|
+
export declare function applyAppUI(b: PluginUIBundle): void;
|
|
40
|
+
export declare function getSlotEntries(slot: string): SlotEntry[];
|
|
41
|
+
export declare function getPluginRoutes(): RouteEntry[];
|
|
42
|
+
export declare function getPluginNav(): NavEntry[];
|
|
43
|
+
export declare function resetAppUI(): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function definePluginUI(b) {
|
|
2
|
+
return b;
|
|
3
|
+
}
|
|
4
|
+
const slotRegistry = new Map();
|
|
5
|
+
const routeRegistry = new Map();
|
|
6
|
+
const navRegistry = new Map();
|
|
7
|
+
export function applyAppUI(b) {
|
|
8
|
+
for (const s of b.slots ?? []) {
|
|
9
|
+
let bucket = slotRegistry.get(s.slot);
|
|
10
|
+
if (!bucket) {
|
|
11
|
+
bucket = new Map();
|
|
12
|
+
slotRegistry.set(s.slot, bucket);
|
|
13
|
+
}
|
|
14
|
+
bucket.set(s.id, s);
|
|
15
|
+
}
|
|
16
|
+
for (const r of b.routes ?? [])
|
|
17
|
+
routeRegistry.set(r.path, r);
|
|
18
|
+
for (const n of b.nav ?? [])
|
|
19
|
+
navRegistry.set(n.to, n);
|
|
20
|
+
}
|
|
21
|
+
function byOrder(key) {
|
|
22
|
+
return (a, b) => (a.order ?? 0) - (b.order ?? 0) || key(a).localeCompare(key(b));
|
|
23
|
+
}
|
|
24
|
+
export function getSlotEntries(slot) {
|
|
25
|
+
return [...(slotRegistry.get(slot)?.values() ?? [])].sort(byOrder((e) => e.id));
|
|
26
|
+
}
|
|
27
|
+
export function getPluginRoutes() {
|
|
28
|
+
return [...routeRegistry.values()];
|
|
29
|
+
}
|
|
30
|
+
export function getPluginNav() {
|
|
31
|
+
return [...navRegistry.values()].sort(byOrder((n) => n.to));
|
|
32
|
+
}
|
|
33
|
+
export function resetAppUI() {
|
|
34
|
+
slotRegistry.clear();
|
|
35
|
+
routeRegistry.clear();
|
|
36
|
+
navRegistry.clear();
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FieldClient, FieldMeta, LayoutEl } from '@coffer-org/sdk/fields';
|
|
2
|
+
import type { FieldRenderer, ElementRenderProps, FieldInlineProps, FieldValueProps, FieldPickerProps, PickerSize, EditorMode, BoxGeom, TemplateImporter } from './types.ts';
|
|
3
|
+
import type { FC } from 'react';
|
|
4
|
+
export declare const rendererRegistry: Record<string, FieldRenderer>;
|
|
5
|
+
type NoWidenedProps<P> = Exclude<keyof P, keyof ElementRenderProps> extends never ? unknown : never;
|
|
6
|
+
export declare function registerRenderer<P extends ElementRenderProps>(r: {
|
|
7
|
+
kinds: string[];
|
|
8
|
+
layout?: 'block' | 'scalar' | ((field: FieldMeta) => 'block' | 'scalar');
|
|
9
|
+
childWidth?: string;
|
|
10
|
+
box?: BoxGeom;
|
|
11
|
+
Page: FC<P>;
|
|
12
|
+
Inline?: FC<FieldInlineProps>;
|
|
13
|
+
Value?: FC<FieldValueProps>;
|
|
14
|
+
pickerSize?: PickerSize | ((field: FieldMeta) => PickerSize);
|
|
15
|
+
Picker?: FC<FieldPickerProps>;
|
|
16
|
+
editor?: EditorMode;
|
|
17
|
+
keys?: FieldRenderer['keys'];
|
|
18
|
+
} & NoWidenedProps<P>): void;
|
|
19
|
+
export declare function resolveRenderer(field: Pick<FieldClient, 'kind'>): FieldRenderer | undefined;
|
|
20
|
+
export declare function elementKind(el: LayoutEl): string;
|
|
21
|
+
export declare function fieldLayout(field: FieldMeta): 'block' | 'scalar';
|
|
22
|
+
export declare const multiBlock: (field: FieldMeta) => "block" | "scalar";
|
|
23
|
+
export declare function resolveFieldSpan(field: FieldMeta): number;
|
|
24
|
+
export declare function registerTemplate(name: string, importer: TemplateImporter<any>): void;
|
|
25
|
+
export declare function resolveTemplate(name: string): TemplateImporter | undefined;
|
|
26
|
+
export {};
|