@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
package/dist/registry.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { hasValue, hasChildren, isPseudo, isCollectionGroup, isEmbeddedGroup } from '@coffer-org/sdk/fields';
|
|
2
|
+
export const rendererRegistry = {};
|
|
3
|
+
export function registerRenderer(r) {
|
|
4
|
+
if (!r.kinds.length)
|
|
5
|
+
throw new Error('[render] renderer without kinds');
|
|
6
|
+
const stored = {
|
|
7
|
+
kinds: r.kinds,
|
|
8
|
+
layout: r.layout,
|
|
9
|
+
childWidth: r.childWidth,
|
|
10
|
+
box: r.box,
|
|
11
|
+
Page: r.Page,
|
|
12
|
+
Inline: r.Inline,
|
|
13
|
+
Value: r.Value,
|
|
14
|
+
pickerSize: r.pickerSize,
|
|
15
|
+
Picker: r.Picker,
|
|
16
|
+
editor: r.editor,
|
|
17
|
+
keys: r.keys,
|
|
18
|
+
};
|
|
19
|
+
for (const kind of r.kinds) {
|
|
20
|
+
if (rendererRegistry[kind])
|
|
21
|
+
throw new Error(`[render] kind '${kind}' already registered`);
|
|
22
|
+
rendererRegistry[kind] = stored;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function resolveRenderer(field) {
|
|
26
|
+
return rendererRegistry[field.kind];
|
|
27
|
+
}
|
|
28
|
+
export function elementKind(el) {
|
|
29
|
+
if (hasValue(el) && !hasChildren(el))
|
|
30
|
+
return el.type.kind;
|
|
31
|
+
if (hasChildren(el))
|
|
32
|
+
return el.kind ?? (isCollectionGroup(el) ? 'collection' : isEmbeddedGroup(el) ? 'embedded' : 'group');
|
|
33
|
+
if (isPseudo(el)) {
|
|
34
|
+
if (el.el === 'button')
|
|
35
|
+
return 'button';
|
|
36
|
+
if (el.el === 'divider')
|
|
37
|
+
return 'divider';
|
|
38
|
+
if (el.el === 'info')
|
|
39
|
+
return 'info';
|
|
40
|
+
}
|
|
41
|
+
throw new Error('[render] unknown layout element');
|
|
42
|
+
}
|
|
43
|
+
export function fieldLayout(field) {
|
|
44
|
+
const layout = resolveRenderer(field)?.layout;
|
|
45
|
+
if (typeof layout === 'function')
|
|
46
|
+
return layout(field);
|
|
47
|
+
return layout ?? 'scalar';
|
|
48
|
+
}
|
|
49
|
+
export const multiBlock = (field) => field.hints['multi'] === true || field.hints['multiple'] === true ? 'block' : 'scalar';
|
|
50
|
+
export function resolveFieldSpan(field) {
|
|
51
|
+
const explicit = field.hints?.['span'];
|
|
52
|
+
if (typeof explicit === 'number' && explicit >= 1 && explicit <= 12) {
|
|
53
|
+
return Math.round(explicit);
|
|
54
|
+
}
|
|
55
|
+
if (typeof explicit === 'string') {
|
|
56
|
+
if (explicit === 'full')
|
|
57
|
+
return 12;
|
|
58
|
+
if (explicit === 'half')
|
|
59
|
+
return 6;
|
|
60
|
+
if (explicit === 'third')
|
|
61
|
+
return 4;
|
|
62
|
+
if (explicit === 'quarter')
|
|
63
|
+
return 3;
|
|
64
|
+
const parsed = parseInt(explicit, 10);
|
|
65
|
+
if (!Number.isNaN(parsed) && parsed >= 1 && parsed <= 12)
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
if (field.hints['multiple'] === true || field.hints['multi'] === true) {
|
|
69
|
+
if (field.kind === 'color' || field.kind === 'country')
|
|
70
|
+
return 6;
|
|
71
|
+
return 12;
|
|
72
|
+
}
|
|
73
|
+
const layout = fieldLayout(field);
|
|
74
|
+
if (layout === 'block')
|
|
75
|
+
return 12;
|
|
76
|
+
switch (field.kind) {
|
|
77
|
+
case 'boolean':
|
|
78
|
+
case 'tristate':
|
|
79
|
+
case 'triState':
|
|
80
|
+
case 'rating':
|
|
81
|
+
case 'percent':
|
|
82
|
+
case 'year':
|
|
83
|
+
case 'color':
|
|
84
|
+
case 'colorname':
|
|
85
|
+
case 'unit':
|
|
86
|
+
case 'amount':
|
|
87
|
+
case 'status':
|
|
88
|
+
case 'score':
|
|
89
|
+
case 'tag':
|
|
90
|
+
return 3;
|
|
91
|
+
case 'email':
|
|
92
|
+
case 'tel':
|
|
93
|
+
case 'url':
|
|
94
|
+
case 'password':
|
|
95
|
+
case 'measured':
|
|
96
|
+
case 'money':
|
|
97
|
+
case 'period':
|
|
98
|
+
case 'numberRange':
|
|
99
|
+
case 'realRange':
|
|
100
|
+
case 'dimensions':
|
|
101
|
+
case 'weight':
|
|
102
|
+
case 'cidr':
|
|
103
|
+
case 'file':
|
|
104
|
+
case 'document':
|
|
105
|
+
case 'audio':
|
|
106
|
+
case 'video':
|
|
107
|
+
case 'image':
|
|
108
|
+
case 'avatar':
|
|
109
|
+
case 'poster':
|
|
110
|
+
case 'cover':
|
|
111
|
+
case 'media':
|
|
112
|
+
case 'tags':
|
|
113
|
+
case 'keyValue':
|
|
114
|
+
case 'key_value':
|
|
115
|
+
return 6;
|
|
116
|
+
case 'markdown':
|
|
117
|
+
case 'textarea':
|
|
118
|
+
case 'snippet':
|
|
119
|
+
case 'source':
|
|
120
|
+
case 'json':
|
|
121
|
+
case 'embed':
|
|
122
|
+
case 'table':
|
|
123
|
+
case 'sheet':
|
|
124
|
+
case 'calendar':
|
|
125
|
+
case 'journal':
|
|
126
|
+
case 'manifest':
|
|
127
|
+
case 'prose':
|
|
128
|
+
case 'idcard':
|
|
129
|
+
case 'bento':
|
|
130
|
+
case 'grid':
|
|
131
|
+
case 'split':
|
|
132
|
+
return 12;
|
|
133
|
+
case 'text':
|
|
134
|
+
case 'string':
|
|
135
|
+
case 'int':
|
|
136
|
+
case 'number':
|
|
137
|
+
case 'real':
|
|
138
|
+
case 'date':
|
|
139
|
+
case 'time':
|
|
140
|
+
case 'datetime':
|
|
141
|
+
case 'reminder':
|
|
142
|
+
case 'select':
|
|
143
|
+
case 'country':
|
|
144
|
+
case 'slug':
|
|
145
|
+
case 'link':
|
|
146
|
+
case 'localDir':
|
|
147
|
+
case 'localFile':
|
|
148
|
+
case 'i18n':
|
|
149
|
+
case 'smalltext':
|
|
150
|
+
case 'ip':
|
|
151
|
+
case 'mac':
|
|
152
|
+
case 'imei':
|
|
153
|
+
case 'hostname':
|
|
154
|
+
case 'dataSize':
|
|
155
|
+
case 'ram':
|
|
156
|
+
case 'diskSize':
|
|
157
|
+
default:
|
|
158
|
+
return 4;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const templateRegistry = {};
|
|
162
|
+
export function registerTemplate(name, importer) {
|
|
163
|
+
if (templateRegistry[name])
|
|
164
|
+
throw new Error(`[template] '${name}' already registered`);
|
|
165
|
+
templateRegistry[name] = importer;
|
|
166
|
+
}
|
|
167
|
+
export function resolveTemplate(name) {
|
|
168
|
+
return templateRegistry[name];
|
|
169
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Icon } from "../../components/Icon.js";
|
|
4
|
+
import { registerRenderer } from "../../registry.js";
|
|
5
|
+
import { Slot, SlotScope, useSlots } from "../slot.js";
|
|
6
|
+
function SectionFields() {
|
|
7
|
+
return (_jsx(_Fragment, { children: useSlots().map((s) => (_jsx(Slot, { name: s.name }, s.name))) }));
|
|
8
|
+
}
|
|
9
|
+
function AccordionPage({ el }) {
|
|
10
|
+
const g = el;
|
|
11
|
+
const { t } = useTranslation();
|
|
12
|
+
const sections = g.view?.sections ?? [];
|
|
13
|
+
return (_jsx("div", { className: "flex min-w-0 flex-col gap-2", children: sections.map((section) => (_jsxs("details", { className: "rounded-lg border border-border", open: section.open, children: [_jsxs("summary", { className: "flex cursor-pointer items-center gap-1.5 px-3 py-2 font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [section.icon && _jsx(Icon, { name: section.icon, size: 14, className: "shrink-0" }), t(section.label)] }), _jsx("div", { className: "space-y-3 px-3 pb-3", children: _jsx(SlotScope, { name: section.name, children: _jsx(SectionFields, {}) }) })] }, section.name))) }));
|
|
14
|
+
}
|
|
15
|
+
registerRenderer({ kinds: ['accordion'], layout: 'block', editor: 'custom', Page: AccordionPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot } from "../slot.js";
|
|
4
|
+
registerRenderer({
|
|
5
|
+
kinds: ['aside'],
|
|
6
|
+
layout: 'block',
|
|
7
|
+
editor: 'custom',
|
|
8
|
+
Page: ({ el }) => {
|
|
9
|
+
const g = el;
|
|
10
|
+
const main = g.view?.main ?? [];
|
|
11
|
+
const side = g.view?.aside ?? [];
|
|
12
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-[minmax(0,1fr)_14rem]", children: [_jsx("div", { className: "min-w-0 space-y-3", children: main.map((n) => (_jsx(Slot, { name: n }, n))) }), _jsx("aside", { className: "min-w-0 space-y-2 text-sm text-muted sm:border-l sm:border-border sm:pl-4", children: side.map((n) => (_jsx(Slot, { name: n }, n))) })] }));
|
|
13
|
+
},
|
|
14
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { registerRenderer } from "../../registry.js";
|
|
4
|
+
import { Slot } from "../slot.js";
|
|
5
|
+
import { BlockShell } from "./block-shell.js";
|
|
6
|
+
function BalancePage({ el }) {
|
|
7
|
+
const g = el;
|
|
8
|
+
const sourceName = g.view?.source;
|
|
9
|
+
const { t } = useTranslation();
|
|
10
|
+
const caption = g.view?.caption;
|
|
11
|
+
const body = (_jsxs("div", { className: "flex flex-col gap-1", "data-testid": "balance", children: [_jsx(Slot, { name: sourceName, render: (_handle, defaultDisplay) => (_jsx("span", { className: "text-3xl font-semibold tabular-nums", "data-testid": "balance-figure", children: defaultDisplay })) }), caption && (_jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-muted", "data-testid": "balance-caption", children: t(caption) }))] }));
|
|
12
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
13
|
+
}
|
|
14
|
+
registerRenderer({ kinds: ['balance'], layout: 'block', editor: 'custom', Page: BalancePage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const tileClass = "rounded-lg border border-border bg-surface p-3 min-w-0";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { BlockShell } from "./block-shell.js";
|
|
5
|
+
export const tileClass = 'rounded-lg border border-border bg-surface p-3 min-w-0';
|
|
6
|
+
const SPAN = {
|
|
7
|
+
'2x1': 'sm:col-span-2',
|
|
8
|
+
'1x2': 'sm:row-span-2',
|
|
9
|
+
'2x2': 'sm:col-span-2 sm:row-span-2',
|
|
10
|
+
wide: 'col-span-full',
|
|
11
|
+
};
|
|
12
|
+
function BentoPage({ el }) {
|
|
13
|
+
const g = el;
|
|
14
|
+
const spans = g.view?.spans ?? {};
|
|
15
|
+
const slots = useSlots();
|
|
16
|
+
const body = (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-3 auto-rows-auto gap-3", children: slots.map((s, i) => {
|
|
17
|
+
if (s.handle.hidden)
|
|
18
|
+
return null;
|
|
19
|
+
const span = spans[s.name];
|
|
20
|
+
return (_jsx("div", { className: `${tileClass} ${span ? (SPAN[span] ?? '') : ''}`, "data-testid": `bento-tile-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
|
|
21
|
+
}) }));
|
|
22
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
23
|
+
}
|
|
24
|
+
registerRenderer({ kinds: ['bento'], layout: 'block', editor: 'custom', Page: BentoPage });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
2
|
+
import type { SlotHandle } from '../slot.tsx';
|
|
3
|
+
import type { TFn } from '../../types.ts';
|
|
4
|
+
export type NamedSlot = {
|
|
5
|
+
name: string;
|
|
6
|
+
handle: SlotHandle;
|
|
7
|
+
};
|
|
8
|
+
export declare function findSlot(slots: readonly NamedSlot[], name: string | undefined): NamedSlot | undefined;
|
|
9
|
+
export declare function findSlots(slots: readonly NamedSlot[], names: readonly string[]): NamedSlot[];
|
|
10
|
+
export declare function BlockShell({ label, icon, children, }: {
|
|
11
|
+
label?: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}): ReactElement;
|
|
15
|
+
export declare function fieldLabel(handle: SlotHandle, t: TFn): string | undefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { isNamedField } from '@coffer-org/sdk/fields';
|
|
4
|
+
import { SectionShell } from "../../layout.js";
|
|
5
|
+
export function findSlot(slots, name) {
|
|
6
|
+
return name !== undefined ? slots.find((s) => s.name === name) : undefined;
|
|
7
|
+
}
|
|
8
|
+
export function findSlots(slots, names) {
|
|
9
|
+
return names.map((n) => findSlot(slots, n)).filter((s) => s !== undefined);
|
|
10
|
+
}
|
|
11
|
+
export function BlockShell({ label, icon, children, }) {
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
if (!label)
|
|
14
|
+
return _jsx(_Fragment, { children: children });
|
|
15
|
+
return (_jsx(SectionShell, { label: t(label), icon: icon, children: children }));
|
|
16
|
+
}
|
|
17
|
+
export function fieldLabel(handle, t) {
|
|
18
|
+
const key = handle.field.label ?? (isNamedField(handle.field) ? handle.field.type.label : undefined);
|
|
19
|
+
return key ? t(key) : undefined;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { ChevronLeft, ChevronRight, Trash2, Plus } from 'lucide-react';
|
|
5
|
+
import { isNamedField, isStorageGroup } from '@coffer-org/sdk/fields';
|
|
6
|
+
import { registerRenderer } from "../../registry.js";
|
|
7
|
+
import { Popover, PopoverTrigger, PopoverContent, PopoverArrow } from "../../components/ui/popover.js";
|
|
8
|
+
import { Button } from "../../components/ui/button.js";
|
|
9
|
+
import { RemoveBtn } from "../multiple-chips.js";
|
|
10
|
+
import { groupScopeClass, useGroupDepth, GroupDepthProvider } from "../chrome.js";
|
|
11
|
+
import { useGroupEdit } from "../widgets/group-edit.js";
|
|
12
|
+
import { useHybridScope } from "../hybrid-scope.js";
|
|
13
|
+
import { GroupField } from "../widgets/group-field.js";
|
|
14
|
+
import { useSlot } from "../slot.js";
|
|
15
|
+
import { ChecklistContent } from "../widgets/checklist-content.js";
|
|
16
|
+
import { useFieldErrors } from "../field-errors.js";
|
|
17
|
+
import { partField } from "../use-collection-rows.js";
|
|
18
|
+
function parseDay(v) {
|
|
19
|
+
const s = typeof v === 'string' ? v : typeof v === 'number' ? String(v) : '';
|
|
20
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(s);
|
|
21
|
+
if (!m)
|
|
22
|
+
return undefined;
|
|
23
|
+
return { year: Number(m[1]), month: Number(m[2]) - 1, day: Number(m[3]) };
|
|
24
|
+
}
|
|
25
|
+
function DayCell({ day, month, year, matchingRows, dateKey, dateFld, restFields, recordCtx, errorFor, onUpdateRow, onRemoveRow, onAddRow, rowTools, editing, }) {
|
|
26
|
+
const { t } = useTranslation();
|
|
27
|
+
const [open, setOpen] = useState(false);
|
|
28
|
+
if (day == null) {
|
|
29
|
+
return _jsx("div", { className: "aspect-square" });
|
|
30
|
+
}
|
|
31
|
+
const firstMatch = matchingRows[0];
|
|
32
|
+
const isMarked = firstMatch !== undefined;
|
|
33
|
+
const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
|
34
|
+
const fullDateLabel = new Date(year, month, day).toLocaleDateString(undefined, {
|
|
35
|
+
day: 'numeric',
|
|
36
|
+
month: 'long',
|
|
37
|
+
year: 'numeric',
|
|
38
|
+
});
|
|
39
|
+
const primaryError = matchingRows
|
|
40
|
+
.map(({ idx }) => errorFor(idx, dateKey) || restFields.map((f) => errorFor(idx, f.key)).find(Boolean))
|
|
41
|
+
.find(Boolean);
|
|
42
|
+
const cellButton = (_jsxs("button", { type: "button", role: "button", className: `relative flex aspect-square w-full items-center justify-center rounded-md text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${primaryError
|
|
43
|
+
? 'border border-danger/60 bg-danger/10 text-danger font-semibold'
|
|
44
|
+
: isMarked
|
|
45
|
+
? 'bg-accent/20 font-semibold text-accent hover:bg-accent/30 shadow-xs'
|
|
46
|
+
: 'text-text hover:bg-muted/20'}`, "data-testid": firstMatch ? `calendar-date-${firstMatch.idx}` : undefined, "aria-label": dateStr, children: [day, isMarked && (_jsx("span", { "data-testid": `calendar-marked-${day}`, className: `absolute bottom-1 h-1 w-1 rounded-full ${primaryError ? 'bg-danger' : 'bg-accent'}` })), primaryError && _jsx("span", { className: "sr-only text-danger", children: primaryError })] }));
|
|
47
|
+
if (!rowTools) {
|
|
48
|
+
return (_jsx("div", { "data-testid": firstMatch ? `calendar-entry-${firstMatch.idx}` : undefined, className: "w-full", children: cellButton }));
|
|
49
|
+
}
|
|
50
|
+
return (_jsx("div", { "data-testid": firstMatch ? `calendar-entry-${firstMatch.idx}` : undefined, className: "w-full", children: _jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: cellButton }), _jsxs(PopoverContent, { align: "center", className: "w-72 p-3 space-y-3 shadow-lg", sideOffset: 6, children: [_jsx(PopoverArrow, {}), _jsxs("div", { className: "flex items-center justify-between border-b border-border/60 pb-2", children: [_jsx("span", { className: "text-xs font-semibold text-text", children: fullDateLabel }), _jsx("span", { className: "text-[11px] font-mono text-muted", children: dateStr })] }), isMarked ? (_jsx("div", { className: "space-y-3", children: matchingRows.map(({ row, idx }) => {
|
|
51
|
+
const err = errorFor(idx, dateKey);
|
|
52
|
+
return (_jsxs("div", { className: "space-y-2 border-b border-border/40 pb-2 last:border-b-0 last:pb-0", children: [err && _jsx("div", { className: "text-xs text-danger font-medium", children: err }), restFields.length > 0 && (_jsxs("div", { className: "space-y-1", children: [_jsx(ChecklistContent, { fields: restFields, row: row, editing: editing, onChange: (key, v) => onUpdateRow(idx, key, v), recordCtx: recordCtx, error: (key) => errorFor(idx, key) }), restFields.map((f) => {
|
|
53
|
+
const fieldErr = errorFor(idx, f.key);
|
|
54
|
+
return fieldErr ? (_jsx("div", { className: "text-xs text-danger font-medium", children: fieldErr }, f.key)) : null;
|
|
55
|
+
})] })), _jsx("div", { className: "flex items-center justify-between pt-1", children: _jsxs("button", { type: "button", "aria-label": "remove", onClick: () => {
|
|
56
|
+
onRemoveRow(idx);
|
|
57
|
+
setOpen(false);
|
|
58
|
+
}, className: "inline-flex items-center gap-1 text-xs text-danger hover:text-danger/80 transition-colors", children: [_jsx(Trash2, { size: 12 }), " ", t('actions.delete', 'Delete')] }) })] }, idx));
|
|
59
|
+
}) })) : (_jsx("div", { className: "space-y-2 pt-1", children: _jsxs(Button, { size: "sm", className: "w-full h-8 text-xs flex items-center justify-center gap-1.5", onClick: () => {
|
|
60
|
+
onAddRow(dateStr);
|
|
61
|
+
setOpen(false);
|
|
62
|
+
}, children: [_jsx(Plus, { size: 13 }), " ", t('actions.add', 'Add')] }) }))] })] }) }));
|
|
63
|
+
}
|
|
64
|
+
function MonthGrid({ rows, dateKey, dateFld, restFields, recordCtx, errorFor, onUpdateRow, onRemoveRow, onAddRow, rowTools, editing, }) {
|
|
65
|
+
const [viewDate, setViewDate] = useState(() => {
|
|
66
|
+
for (const r of rows) {
|
|
67
|
+
const d = parseDay(r[dateKey]);
|
|
68
|
+
if (d)
|
|
69
|
+
return new Date(d.year, d.month, 1);
|
|
70
|
+
}
|
|
71
|
+
return new Date();
|
|
72
|
+
});
|
|
73
|
+
const month = viewDate.getMonth();
|
|
74
|
+
const year = viewDate.getFullYear();
|
|
75
|
+
const prevMonth = () => setViewDate(new Date(year, month - 1, 1));
|
|
76
|
+
const nextMonth = () => setViewDate(new Date(year, month + 1, 1));
|
|
77
|
+
const first = new Date(year, month, 1);
|
|
78
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
79
|
+
const lead = first.getDay();
|
|
80
|
+
const cells = [
|
|
81
|
+
...Array.from({ length: lead }, () => null),
|
|
82
|
+
...Array.from({ length: daysInMonth }, (_, i) => i + 1),
|
|
83
|
+
];
|
|
84
|
+
while (cells.length % 7 !== 0)
|
|
85
|
+
cells.push(null);
|
|
86
|
+
const weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
|
|
87
|
+
const monthName = new Date(year, month, 1).toLocaleDateString(undefined, { month: 'long', year: 'numeric' });
|
|
88
|
+
const rowsByDay = new Map();
|
|
89
|
+
const unassignedRows = [];
|
|
90
|
+
rows.forEach((row, idx) => {
|
|
91
|
+
const d = parseDay(row[dateKey]);
|
|
92
|
+
if (d && d.year === year && d.month === month) {
|
|
93
|
+
const list = rowsByDay.get(d.day) ?? [];
|
|
94
|
+
list.push({ row, idx });
|
|
95
|
+
rowsByDay.set(d.day, list);
|
|
96
|
+
}
|
|
97
|
+
else if (!d) {
|
|
98
|
+
unassignedRows.push({ row, idx });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return (_jsxs("div", { className: "w-full max-w-[280px] rounded-lg border border-border bg-surface p-3", "data-testid": "calendar-grid", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsx("span", { className: "text-xs font-semibold text-text capitalize", children: monthName }), _jsxs("div", { className: "flex items-center gap-0.5", children: [_jsx("button", { type: "button", onClick: prevMonth, "aria-label": "Previous month", className: "rounded p-1 text-muted hover:bg-muted/20 hover:text-text transition-colors", children: _jsx(ChevronLeft, { size: 13 }) }), _jsx("button", { type: "button", onClick: nextMonth, "aria-label": "Next month", className: "rounded p-1 text-muted hover:bg-muted/20 hover:text-text transition-colors", children: _jsx(ChevronRight, { size: 13 }) })] })] }), _jsx("div", { className: "mb-1 grid grid-cols-7 gap-1 text-center text-[10px] font-medium text-muted", children: weekdays.map((wd, i) => (_jsx("div", { className: "py-0.5", children: wd }, i))) }), _jsx("div", { className: "grid grid-cols-7 gap-1", children: cells.map((day, i) => (_jsx(DayCell, { day: day, month: month, year: year, matchingRows: day != null ? (rowsByDay.get(day) ?? []) : [], dateKey: dateKey, dateFld: dateFld, restFields: restFields, recordCtx: recordCtx, errorFor: errorFor, onUpdateRow: onUpdateRow, onRemoveRow: onRemoveRow, onAddRow: onAddRow, rowTools: rowTools, editing: editing }, i))) }), unassignedRows.length > 0 && (_jsx("div", { className: "mt-3 space-y-1.5 border-t border-border pt-2", children: unassignedRows.map(({ idx }) => {
|
|
102
|
+
const err = errorFor(idx, dateKey);
|
|
103
|
+
return (_jsxs("div", { "data-testid": `calendar-entry-${idx}`, className: "flex items-center justify-between text-xs", children: [_jsx("div", { "data-testid": `calendar-date-${idx}`, className: "text-danger font-medium", children: err || 'Required' }), rowTools && _jsx(RemoveBtn, { onClick: () => onRemoveRow(idx) })] }, idx));
|
|
104
|
+
}) }))] }));
|
|
105
|
+
}
|
|
106
|
+
function CalendarPage({ el, value, recordCtx, mode }) {
|
|
107
|
+
const { t } = useTranslation();
|
|
108
|
+
const depth = useGroupDepth();
|
|
109
|
+
const errorAt = useFieldErrors();
|
|
110
|
+
const { write } = useSlot();
|
|
111
|
+
const group = el;
|
|
112
|
+
const dateKey = group.view?.['date'];
|
|
113
|
+
const dateFld = partField(group, dateKey);
|
|
114
|
+
const g = useGroupEdit(mode, value, (v) => write(v));
|
|
115
|
+
const rows = Array.isArray(g.value) ? g.value : [];
|
|
116
|
+
const editing = g.mode !== 'view';
|
|
117
|
+
const live = useHybridScope() && mode === 'view';
|
|
118
|
+
const rowTools = editing || live;
|
|
119
|
+
if (!dateFld || !dateKey) {
|
|
120
|
+
return (_jsx(GroupField, { group: { ...group, kind: undefined }, value: value, onChange: (v) => void write(v), recordCtx: recordCtx, mode: mode }));
|
|
121
|
+
}
|
|
122
|
+
const restFields = group.fields
|
|
123
|
+
.filter((f) => isNamedField(f) && f.key !== dateKey)
|
|
124
|
+
.map((f) => ({ key: f.key, type: f.type }));
|
|
125
|
+
const errorFor = (idx, key) => isStorageGroup(group) ? errorAt([group.key, idx, key]) : undefined;
|
|
126
|
+
const updateRow = (idx, key, v) => {
|
|
127
|
+
const source = rows[idx];
|
|
128
|
+
const next = { ...source, [key]: v };
|
|
129
|
+
g.onChange(rows.map((r, i) => (i === idx ? next : r)));
|
|
130
|
+
};
|
|
131
|
+
const removeRow = (idx) => {
|
|
132
|
+
g.onChange(rows.filter((_, i) => i !== idx));
|
|
133
|
+
};
|
|
134
|
+
const addRow = (dateStr) => {
|
|
135
|
+
g.onChange([...rows, { [dateKey]: dateStr }]);
|
|
136
|
+
};
|
|
137
|
+
const content = (_jsx(MonthGrid, { rows: rows, dateKey: dateKey, dateFld: dateFld, restFields: restFields, recordCtx: recordCtx, errorFor: errorFor, onUpdateRow: updateRow, onRemoveRow: removeRow, onAddRow: addRow, rowTools: rowTools, editing: editing }));
|
|
138
|
+
if (mode !== 'view')
|
|
139
|
+
return content;
|
|
140
|
+
return (_jsxs("section", { className: `${groupScopeClass(depth)} flex flex-col gap-1 min-w-0`, "data-testid": "calendar", children: [group.label && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "text-[13px] font-semibold text-accent", children: t(group.label) }), !live && g.actions && _jsx("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: g.actions })] })), _jsx(GroupDepthProvider, { depth: depth + 1, children: content })] }));
|
|
141
|
+
}
|
|
142
|
+
registerRenderer({ kinds: ['calendar'], layout: 'block', Page: CalendarPage, editor: 'custom' });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { BlockShell } from "./block-shell.js";
|
|
5
|
+
const TONE = {
|
|
6
|
+
note: 'border-l-4 border-accent bg-accent/5',
|
|
7
|
+
warn: 'border-l-4 border-warning bg-warning/10 font-medium',
|
|
8
|
+
danger: 'border-l-[6px] border-danger bg-danger/10 font-semibold',
|
|
9
|
+
};
|
|
10
|
+
function CalloutPage({ el }) {
|
|
11
|
+
const g = el;
|
|
12
|
+
const tone = g.view?.tone ?? 'note';
|
|
13
|
+
const slots = useSlots();
|
|
14
|
+
const body = (_jsx("div", { className: `space-y-2 rounded-lg px-4 py-3 ${TONE[tone] ?? TONE.note}`, "data-testid": "callout", "data-tone": tone, children: slots.map((s, i) => {
|
|
15
|
+
if (s.handle.hidden)
|
|
16
|
+
return null;
|
|
17
|
+
return (_jsx("div", { className: "min-w-0", "data-testid": `callout-item-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
|
|
18
|
+
}) }));
|
|
19
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
20
|
+
}
|
|
21
|
+
registerRenderer({ kinds: ['callout'], layout: 'block', editor: 'custom', Page: CalloutPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { registerRenderer } from "../../registry.js";
|
|
4
|
+
import { groupScopeClass, useGroupDepth, GroupDepthProvider } from "../chrome.js";
|
|
5
|
+
import { useGroupEdit } from "../widgets/group-edit.js";
|
|
6
|
+
import { GroupField } from "../widgets/group-field.js";
|
|
7
|
+
import { FieldSlot } from "../dispatch.js";
|
|
8
|
+
import { useSlot } from "../slot.js";
|
|
9
|
+
import { toSeries, sparkline, seriesDelta } from "./series.js";
|
|
10
|
+
import { partField } from "../use-collection-rows.js";
|
|
11
|
+
const BOX = { width: 100, height: 28, pad: 2 };
|
|
12
|
+
function fmtDelta(n) {
|
|
13
|
+
const rounded = Math.round(n * 100) / 100;
|
|
14
|
+
return `${rounded > 0 ? '+' : ''}${rounded}`;
|
|
15
|
+
}
|
|
16
|
+
function ChartPage({ el, value, recordCtx, mode }) {
|
|
17
|
+
const { t } = useTranslation();
|
|
18
|
+
const depth = useGroupDepth();
|
|
19
|
+
const { write } = useSlot();
|
|
20
|
+
const group = el;
|
|
21
|
+
const valueKey = group.view?.['value'];
|
|
22
|
+
const dateKey = group.view?.['date'];
|
|
23
|
+
const showDelta = Boolean(group.view?.['delta']);
|
|
24
|
+
const valueFld = partField(group, valueKey);
|
|
25
|
+
const dateFld = partField(group, dateKey);
|
|
26
|
+
const g = useGroupEdit(mode, value, (v) => write(v));
|
|
27
|
+
const rows = Array.isArray(g.value) ? g.value : [];
|
|
28
|
+
const editing = g.mode !== 'view';
|
|
29
|
+
if (!valueKey || !dateKey || !valueFld || !dateFld) {
|
|
30
|
+
return (_jsx(GroupField, { group: { ...group, kind: undefined }, value: value, onChange: (v) => void write(v), recordCtx: recordCtx, mode: mode }));
|
|
31
|
+
}
|
|
32
|
+
const points = toSeries(rows, valueKey, dateKey);
|
|
33
|
+
const spark = sparkline(points, BOX);
|
|
34
|
+
const last = points[points.length - 1];
|
|
35
|
+
const delta = showDelta ? seriesDelta(points) : undefined;
|
|
36
|
+
const editor = (_jsx(GroupField, { group: { ...group, kind: undefined }, value: g.value, onChange: (v) => g.onChange(v), recordCtx: recordCtx, mode: "edit" }));
|
|
37
|
+
const reading = (p) => (_jsx(FieldSlot, { el: valueFld, field: valueFld.type, value: p.row?.[valueKey] ?? p.value, mode: "view", bare: true, recordCtx: recordCtx }));
|
|
38
|
+
const lowest = points.length ? points.reduce((a, b) => (b.value < a.value ? b : a)) : undefined;
|
|
39
|
+
const highest = points.length ? points.reduce((a, b) => (b.value > a.value ? b : a)) : undefined;
|
|
40
|
+
const chart = last === undefined ? (_jsx("p", { className: "py-1 text-sm text-muted", "data-testid": "chart-empty", children: t('form.noSeries', 'No readings yet') })) : (_jsxs("div", { className: "flex flex-col gap-1.5", "data-testid": "chart-body", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("div", { className: "min-w-0 text-2xl font-semibold tabular-nums text-text", "data-testid": "chart-last", children: reading(last) }), delta !== undefined && (_jsx("span", { className: `text-sm font-medium tabular-nums ${delta > 0 ? 'text-accent' : delta < 0 ? 'text-danger' : 'text-muted'}`, "data-testid": "chart-delta", children: fmtDelta(delta) }))] }), _jsx("svg", { className: "h-12 w-full text-accent", viewBox: `0 0 ${BOX.width} ${BOX.height}`, preserveAspectRatio: "none", "aria-hidden": "true", "data-testid": "chart-spark", children: _jsx("path", { d: spark.path, "data-testid": "chart-path", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", vectorEffect: "non-scaling-stroke" }) }), _jsxs("div", { className: "flex items-baseline gap-1 text-[10px] tabular-nums text-muted", "data-testid": "chart-range", children: [lowest && reading(lowest), _jsx("span", { "aria-hidden": "true", children: "\u2013" }), highest && reading(highest)] })] }));
|
|
41
|
+
const content = editing ? editor : chart;
|
|
42
|
+
if (mode !== 'view')
|
|
43
|
+
return content;
|
|
44
|
+
return (_jsxs("section", { className: `${groupScopeClass(depth)} flex flex-col gap-1 min-w-0`, "data-testid": "chart", children: [group.label && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "text-[13px] font-semibold text-accent", children: t(group.label) }), g.actions && _jsx("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: g.actions })] })), _jsx(GroupDepthProvider, { depth: depth + 1, children: content })] }));
|
|
45
|
+
}
|
|
46
|
+
registerRenderer({ kinds: ['chart'], layout: 'block', Page: ChartPage, editor: 'custom' });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { registerRenderer } from "../../registry.js";
|
|
4
|
+
import { Slot } from "../slot.js";
|
|
5
|
+
import { BlockShell } from "./block-shell.js";
|
|
6
|
+
function ComparePage({ el }) {
|
|
7
|
+
const g = el;
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
const left = g.view?.left ?? [];
|
|
10
|
+
const right = g.view?.right ?? [];
|
|
11
|
+
const leftLabel = g.view?.leftLabel;
|
|
12
|
+
const rightLabel = g.view?.rightLabel;
|
|
13
|
+
const body = (_jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", "data-testid": "compare", children: [_jsxs("div", { className: "min-w-0 space-y-3", "data-testid": "compare-left", children: [leftLabel && _jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-muted", children: t(leftLabel) }), left.map((n) => (_jsx(Slot, { name: n }, n)))] }), _jsxs("div", { className: "min-w-0 space-y-3 sm:border-l sm:border-border sm:pl-4", "data-testid": "compare-right", children: [rightLabel && _jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-muted", children: t(rightLabel) }), right.map((n) => (_jsx(Slot, { name: n }, n)))] })] }));
|
|
14
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
15
|
+
}
|
|
16
|
+
registerRenderer({ kinds: ['compare'], layout: 'block', editor: 'custom', Page: ComparePage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function countdownDays(value: unknown, today: string): number | null;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ArrowUp, ArrowDown, Minus } from 'lucide-react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { registerRenderer } from "../../registry.js";
|
|
5
|
+
import { Slot } from "../slot.js";
|
|
6
|
+
import { BlockShell } from "./block-shell.js";
|
|
7
|
+
const MS_PER_DAY = 86_400_000;
|
|
8
|
+
export function countdownDays(value, today) {
|
|
9
|
+
const s = typeof value === 'string' ? value.slice(0, 10) : '';
|
|
10
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(s))
|
|
11
|
+
return null;
|
|
12
|
+
const t = Date.parse(s);
|
|
13
|
+
const t0 = Date.parse(today);
|
|
14
|
+
if (Number.isNaN(t) || Number.isNaN(t0))
|
|
15
|
+
return null;
|
|
16
|
+
return Math.round((t - t0) / MS_PER_DAY);
|
|
17
|
+
}
|
|
18
|
+
function todayISO() {
|
|
19
|
+
return new Date().toISOString().slice(0, 10);
|
|
20
|
+
}
|
|
21
|
+
function CountdownPage({ el }) {
|
|
22
|
+
const g = el;
|
|
23
|
+
const sourceName = g.view?.source;
|
|
24
|
+
const { t } = useTranslation();
|
|
25
|
+
const caption = g.view?.caption;
|
|
26
|
+
const body = (_jsxs("div", { className: "flex flex-col gap-1", "data-testid": "countdown", children: [_jsx(Slot, { name: sourceName, render: (handle, defaultDisplay) => {
|
|
27
|
+
const days = countdownDays(handle.value, todayISO());
|
|
28
|
+
if (days === null)
|
|
29
|
+
return defaultDisplay;
|
|
30
|
+
const direction = days > 0 ? 'remaining' : days < 0 ? 'elapsed' : 'today';
|
|
31
|
+
const Ico = days > 0 ? ArrowUp : days < 0 ? ArrowDown : Minus;
|
|
32
|
+
return (_jsxs("span", { className: "inline-flex items-baseline gap-3", "data-testid": "countdown-figure", "data-direction": direction, children: [_jsxs("span", { className: "inline-flex items-baseline gap-1.5", children: [_jsx(Ico, { size: 16, className: "shrink-0 self-center text-muted", "aria-hidden": "true" }), _jsx("span", { className: "text-3xl font-bold tabular-nums", children: Math.abs(days) })] }), _jsx("span", { className: "text-sm text-muted", "data-testid": "countdown-date", children: defaultDisplay })] }));
|
|
33
|
+
} }), caption && (_jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-muted", "data-testid": "countdown-caption", children: t(caption) }))] }));
|
|
34
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
35
|
+
}
|
|
36
|
+
registerRenderer({ kinds: ['countdown'], layout: 'block', editor: 'custom', Page: CountdownPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Link } from 'react-router-dom';
|
|
3
|
+
import { getShelf } from "../../schema-registry.js";
|
|
4
|
+
import { RecordInline } from "../../components/RecordInline.js";
|
|
5
|
+
import { useRecordsByIds } from "../../lib/use-record-labels.js";
|
|
6
|
+
import { Dash, shortId } from "../shared.js";
|
|
7
|
+
import { registerRenderer } from "../../registry.js";
|
|
8
|
+
import { Slot } from "../slot.js";
|
|
9
|
+
import { BlockShell } from "./block-shell.js";
|
|
10
|
+
import { relationIds } from "./relation-source.js";
|
|
11
|
+
function DeckCards({ field, value }) {
|
|
12
|
+
const library = field.relation?.library ?? '';
|
|
13
|
+
const shelf = field.relation?.shelf ?? '';
|
|
14
|
+
const ids = relationIds(field, value);
|
|
15
|
+
const recordMap = useRecordsByIds(library, shelf, ids);
|
|
16
|
+
const m = getShelf(library, shelf);
|
|
17
|
+
if (!ids.length)
|
|
18
|
+
return _jsx(Dash, {});
|
|
19
|
+
return (_jsx("div", { className: "overflow-x-auto", "data-testid": "deck", children: _jsx("div", { className: "flex gap-3 pb-1", children: ids.map((id) => {
|
|
20
|
+
const row = recordMap.get(id);
|
|
21
|
+
return (_jsx("div", { className: "flex w-40 shrink-0 flex-col gap-2 rounded-lg border border-border p-3", "data-testid": "deck-card", children: _jsx(Link, { to: `/${library}/${shelf}/${id}`, className: "min-w-0 hover:text-accent", children: m && row ? _jsx(RecordInline, { shelf: m, row: row }) : shortId(id) }) }, id));
|
|
22
|
+
}) }) }));
|
|
23
|
+
}
|
|
24
|
+
function DeckPage({ el }) {
|
|
25
|
+
const g = el;
|
|
26
|
+
const sourceName = g.view?.source;
|
|
27
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: _jsx(Slot, { name: sourceName, render: (handle) => _jsx(DeckCards, { field: handle.field.type, value: handle.value }) }) }));
|
|
28
|
+
}
|
|
29
|
+
registerRenderer({ kinds: ['deck'], layout: 'block', editor: 'custom', Page: DeckPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { findSlot } from "./block-shell.js";
|
|
5
|
+
function EpigraphPage({ el }) {
|
|
6
|
+
const g = el;
|
|
7
|
+
const sourceName = g.view?.source;
|
|
8
|
+
const attributionName = g.view?.attribution;
|
|
9
|
+
const slots = useSlots();
|
|
10
|
+
const attribution = findSlot(slots, attributionName);
|
|
11
|
+
return (_jsxs("blockquote", { className: "border-l-2 border-accent pl-4", "data-testid": "epigraph", children: [_jsx("div", { className: "min-w-0 text-lg leading-snug italic", "data-testid": "epigraph-source", children: _jsx(Slot, { name: sourceName }) }), attribution && !attribution.handle.hidden && (_jsx("div", { className: "mt-2 min-w-0 text-sm text-muted before:mr-1 before:content-['\u2014']", "data-testid": "epigraph-attribution", children: _jsx(Slot, { name: attributionName }) }))] }));
|
|
12
|
+
}
|
|
13
|
+
registerRenderer({ kinds: ['epigraph'], layout: 'block', editor: 'custom', Page: EpigraphPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|