@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,18 @@
|
|
|
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, useSlots } from "../slot.js";
|
|
5
|
+
import { BlockShell } from "./block-shell.js";
|
|
6
|
+
function TerminalPage({ el }) {
|
|
7
|
+
const g = el;
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
const caption = g.view?.caption;
|
|
10
|
+
const slots = useSlots();
|
|
11
|
+
const body = (_jsxs("div", { className: "overflow-hidden rounded-lg border border-border bg-surface", "data-testid": "terminal", children: [caption && (_jsx("div", { className: "border-b border-border bg-elevated px-3 py-1.5 text-xs font-medium text-muted", "data-testid": "terminal-caption", children: t(caption) })), _jsx("div", { className: "overflow-x-auto p-3 font-mono text-sm", "data-testid": "terminal-body", children: slots.map((s, i) => {
|
|
12
|
+
if (s.handle.hidden)
|
|
13
|
+
return null;
|
|
14
|
+
return (_jsx("div", { className: "min-w-0", "data-testid": `terminal-item-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
|
|
15
|
+
}) })] }));
|
|
16
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
17
|
+
}
|
|
18
|
+
registerRenderer({ kinds: ['terminal'], layout: 'block', editor: 'custom', Page: TerminalPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
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 { BlockShell } from "./block-shell.js";
|
|
5
|
+
function todayISO() {
|
|
6
|
+
return new Date().toISOString().slice(0, 10);
|
|
7
|
+
}
|
|
8
|
+
function isPastDate(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 t < t0;
|
|
17
|
+
}
|
|
18
|
+
function TimelinePage({ el }) {
|
|
19
|
+
const g = el;
|
|
20
|
+
const slots = useSlots();
|
|
21
|
+
const body = (_jsxs("div", { className: "relative flex flex-col gap-2", "data-testid": "timeline", children: [_jsx("div", { className: "absolute top-2 bottom-2 left-[5px] w-px bg-border", "aria-hidden": true }), slots.map((s, i) => {
|
|
22
|
+
if (s.handle.hidden)
|
|
23
|
+
return null;
|
|
24
|
+
return (_jsx("div", { className: "relative min-w-0", "data-testid": `timeline-item-${i}`, children: _jsx(Slot, { name: s.name, render: (handle, defaultDisplay) => {
|
|
25
|
+
const past = isPastDate(handle.value, todayISO());
|
|
26
|
+
return (_jsxs("span", { className: "flex min-w-0 items-start gap-3", "data-testid": "timeline-entry", children: [_jsx("span", { className: `relative z-10 mt-1 h-2.5 w-2.5 shrink-0 rounded-full border-2 border-accent ${past ? 'bg-accent' : 'bg-surface'}`, "data-testid": "timeline-dot", "data-past": past === null ? undefined : past, "aria-hidden": true }), _jsx("span", { className: "min-w-0 flex-1 pb-1", children: defaultDisplay })] }));
|
|
27
|
+
} }) }, s.name));
|
|
28
|
+
})] }));
|
|
29
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
30
|
+
}
|
|
31
|
+
registerRenderer({ kinds: ['timeline'], layout: 'block', editor: 'custom', Page: TimelinePage });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FieldMeta } from '@coffer-org/sdk/fields';
|
|
2
|
+
import type { BoxGeom } from '../types.ts';
|
|
3
|
+
export declare const FIELD_ROW_CLS = "min-h-9 px-2";
|
|
4
|
+
export declare function resolveBox(field: FieldMeta, geom: BoxGeom): BoxGeom;
|
|
5
|
+
export declare function boxClass(box: BoxGeom): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { fieldLayout, resolveRenderer } from "../registry.js";
|
|
2
|
+
export const FIELD_ROW_CLS = 'min-h-9 px-2';
|
|
3
|
+
export function resolveBox(field, geom) {
|
|
4
|
+
const declared = resolveRenderer(field)?.box;
|
|
5
|
+
if (declared)
|
|
6
|
+
return declared;
|
|
7
|
+
if (fieldLayout(field) === 'block')
|
|
8
|
+
return 'none';
|
|
9
|
+
return geom;
|
|
10
|
+
}
|
|
11
|
+
export function boxClass(box) {
|
|
12
|
+
switch (box) {
|
|
13
|
+
case 'title':
|
|
14
|
+
return 'flex items-center self-start w-full rounded-lg border border-transparent px-3 py-1 text-[28px] font-bold leading-tight';
|
|
15
|
+
case 'textarea':
|
|
16
|
+
return 'flex self-start w-full rounded-lg border border-transparent px-3 py-2 text-sm whitespace-pre-wrap min-h-20';
|
|
17
|
+
case 'none':
|
|
18
|
+
return '';
|
|
19
|
+
case 'inline':
|
|
20
|
+
default:
|
|
21
|
+
return `flex w-full min-w-0 items-center self-start ${FIELD_ROW_CLS} rounded-lg border border-transparent text-sm`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { BoxGeom } from '../types.ts';
|
|
3
|
+
import type { FieldMeta, ContainerNode } from '@coffer-org/sdk/fields';
|
|
4
|
+
export declare const STACK = "gap-3.5";
|
|
5
|
+
export declare const LAYOUT_STACK = "gap-6";
|
|
6
|
+
export declare const PAGE_STACK = "gap-6";
|
|
7
|
+
export declare function Rail({ children }: {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
export declare function FieldLabel({ label, required, actions, reserveAction, }: {
|
|
11
|
+
label: string;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
actions?: ReactNode;
|
|
14
|
+
reserveAction?: boolean;
|
|
15
|
+
htmlFor?: string;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
export declare function FieldLabelText({ label, required }: {
|
|
18
|
+
label: string;
|
|
19
|
+
required?: boolean;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
export declare const GROUP_TITLE_CLS = "flex items-center gap-2 font-semibold text-[13px] text-accent";
|
|
22
|
+
export type GroupTone = 'section' | 'group';
|
|
23
|
+
export declare const DEFAULT_GROUP_ICON = "lucide:folder";
|
|
24
|
+
export declare const DEFAULT_COLLECTION_ICON = "lucide:list";
|
|
25
|
+
export declare const DEFAULT_SECTION_ICON = "lucide:puzzle";
|
|
26
|
+
export declare function groupIcon(g: ContainerNode): string;
|
|
27
|
+
export declare function GroupTitle({ label, icon, actions, as, tone, }: {
|
|
28
|
+
label: string;
|
|
29
|
+
icon?: string;
|
|
30
|
+
actions?: ReactNode;
|
|
31
|
+
as?: 'h3' | 'legend';
|
|
32
|
+
tone?: GroupTone;
|
|
33
|
+
}): import("react").JSX.Element;
|
|
34
|
+
export declare const EDIT_REVEAL = "opacity-0 transition-opacity group-hover/field:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100";
|
|
35
|
+
export declare const useGroupDepth: () => number;
|
|
36
|
+
export declare function GroupDepthProvider({ depth, children }: {
|
|
37
|
+
depth: number;
|
|
38
|
+
children: ReactNode;
|
|
39
|
+
}): import("react").JSX.Element;
|
|
40
|
+
export declare function groupScopeClass(depth: number): string;
|
|
41
|
+
export declare function groupRevealClass(depth: number): string;
|
|
42
|
+
export declare const ACTION_CHIP = "h-7 w-7 rounded-md border border-border bg-surface/80 text-muted hover:border-accent/60 hover:text-accent";
|
|
43
|
+
export declare const ACTION_CHIP_ACCENT = "h-7 w-7 rounded-md border border-accent/40 bg-accent/10 text-accent hover:border-accent hover:bg-accent/20 hover:text-accent";
|
|
44
|
+
export declare function EditToggle({ onEdit, variant, scope, }: {
|
|
45
|
+
onEdit: () => void;
|
|
46
|
+
variant?: 'inline' | 'overlay';
|
|
47
|
+
scope?: 'field' | 'group';
|
|
48
|
+
}): import("react").JSX.Element;
|
|
49
|
+
export declare function SaveCancel({ size, busy, disabled, onSave, onCancel, }: {
|
|
50
|
+
size: 'xs' | 'sm';
|
|
51
|
+
busy: boolean;
|
|
52
|
+
disabled?: boolean;
|
|
53
|
+
onSave: () => void;
|
|
54
|
+
onCancel: () => void;
|
|
55
|
+
}): import("react").JSX.Element;
|
|
56
|
+
export declare const EDIT_INLINE_PAD = "min-h-9 px-2 border border-transparent";
|
|
57
|
+
export declare function FieldBox({ geom, field, children }: {
|
|
58
|
+
geom: BoxGeom;
|
|
59
|
+
field: FieldMeta;
|
|
60
|
+
children: ReactNode;
|
|
61
|
+
}): import("react").JSX.Element;
|
|
62
|
+
export declare function FieldError({ error }: {
|
|
63
|
+
error?: string;
|
|
64
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Check, Pencil, X } from 'lucide-react';
|
|
5
|
+
import { Button } from "../components/ui/button.js";
|
|
6
|
+
import { Icon } from "../components/Icon.js";
|
|
7
|
+
import { resolveBox, boxClass, FIELD_ROW_CLS } from "./box.js";
|
|
8
|
+
import { isCollectionGroup } from '@coffer-org/sdk/fields';
|
|
9
|
+
export const STACK = 'gap-3.5';
|
|
10
|
+
export const LAYOUT_STACK = 'gap-6';
|
|
11
|
+
export const PAGE_STACK = 'gap-6';
|
|
12
|
+
export function Rail({ children }) {
|
|
13
|
+
return (_jsx("div", { className: "border-l-2 border-border/80 bg-muted/10 rounded-r-xl pl-4 pr-3.5 py-3 transition-colors", children: children }));
|
|
14
|
+
}
|
|
15
|
+
export function FieldLabel({ label, required, actions, reserveAction, }) {
|
|
16
|
+
return (_jsxs("div", { className: `flex items-center gap-2 mb-0.5 min-h-8 ${reserveAction ? 'pr-24' : ''}`, children: [_jsx(FieldLabelText, { label: label, required: required }), actions && _jsx("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: actions })] }));
|
|
17
|
+
}
|
|
18
|
+
export function FieldLabelText({ label, required }) {
|
|
19
|
+
return (_jsxs("span", { className: "min-w-0 text-[11px] font-medium text-muted", children: [label, required && _jsx("span", { className: "text-danger ml-0.5", children: "*" })] }));
|
|
20
|
+
}
|
|
21
|
+
const GROUP_TITLE_BASE = 'flex items-center gap-2 font-semibold';
|
|
22
|
+
export const GROUP_TITLE_CLS = `${GROUP_TITLE_BASE} text-[13px] text-accent`;
|
|
23
|
+
export const DEFAULT_GROUP_ICON = 'lucide:folder';
|
|
24
|
+
export const DEFAULT_COLLECTION_ICON = 'lucide:list';
|
|
25
|
+
export const DEFAULT_SECTION_ICON = 'lucide:puzzle';
|
|
26
|
+
export function groupIcon(g) {
|
|
27
|
+
return g.icon ?? (isCollectionGroup(g) ? DEFAULT_COLLECTION_ICON : DEFAULT_GROUP_ICON);
|
|
28
|
+
}
|
|
29
|
+
export function GroupTitle({ label, icon, actions, as = 'h3', tone = 'group', }) {
|
|
30
|
+
const Tag = as;
|
|
31
|
+
const depth = useGroupDepth();
|
|
32
|
+
const section = tone === 'section';
|
|
33
|
+
const size = section ? 'text-[15px]' : 'text-[13px]';
|
|
34
|
+
const color = section || depth === 0 ? 'text-accent' : 'text-muted';
|
|
35
|
+
return (_jsxs(Tag, { className: `${GROUP_TITLE_BASE} ${size} ${color}${as === 'legend' ? ' px-1 w-full' : ''}`, children: [icon &&
|
|
36
|
+
(section ? (_jsx("span", { className: "flex size-6 shrink-0 items-center justify-center rounded-md bg-accent/10 text-accent", children: _jsx(Icon, { name: icon, size: 14 }) })) : (_jsx(Icon, { name: icon, size: 14, className: "shrink-0" }))), label, actions && _jsx("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: actions })] }));
|
|
37
|
+
}
|
|
38
|
+
export const EDIT_REVEAL = 'opacity-0 transition-opacity group-hover/field:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100';
|
|
39
|
+
const GroupDepthContext = createContext(0);
|
|
40
|
+
export const useGroupDepth = () => useContext(GroupDepthContext);
|
|
41
|
+
export function GroupDepthProvider({ depth, children }) {
|
|
42
|
+
return _jsx(GroupDepthContext.Provider, { value: depth, children: children });
|
|
43
|
+
}
|
|
44
|
+
export function groupScopeClass(depth) {
|
|
45
|
+
switch (depth & 3) {
|
|
46
|
+
case 0:
|
|
47
|
+
return 'group/group';
|
|
48
|
+
case 1:
|
|
49
|
+
return 'group/group2';
|
|
50
|
+
case 2:
|
|
51
|
+
return 'group/group3';
|
|
52
|
+
default:
|
|
53
|
+
return 'group/group4';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const GROUP_REVEAL_BY_DEPTH = [
|
|
57
|
+
'opacity-0 transition-opacity group-hover/group:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100',
|
|
58
|
+
'opacity-0 transition-opacity group-hover/group2:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100',
|
|
59
|
+
'opacity-0 transition-opacity group-hover/group3:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100',
|
|
60
|
+
'opacity-0 transition-opacity group-hover/group4:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100',
|
|
61
|
+
];
|
|
62
|
+
export function groupRevealClass(depth) {
|
|
63
|
+
return GROUP_REVEAL_BY_DEPTH[depth & 3] ?? GROUP_REVEAL_BY_DEPTH[0];
|
|
64
|
+
}
|
|
65
|
+
export const ACTION_CHIP = 'h-7 w-7 rounded-md border border-border bg-surface/80 text-muted hover:border-accent/60 hover:text-accent';
|
|
66
|
+
export const ACTION_CHIP_ACCENT = 'h-7 w-7 rounded-md border border-accent/40 bg-accent/10 text-accent hover:border-accent hover:bg-accent/20 hover:text-accent';
|
|
67
|
+
export function EditToggle({ onEdit, variant = 'inline', scope = 'field', }) {
|
|
68
|
+
const { t } = useTranslation();
|
|
69
|
+
const depth = useGroupDepth();
|
|
70
|
+
const reveal = scope === 'group' ? groupRevealClass(depth) : EDIT_REVEAL;
|
|
71
|
+
const pos = variant === 'overlay' ? 'absolute right-1 top-0 z-10' : '';
|
|
72
|
+
return (_jsx(Button, { type: "button", variant: "outline", size: "iconXs", className: `${pos} ${ACTION_CHIP} ${reveal}`, onClick: onEdit, title: t('actions.edit'), children: _jsx(Pencil, { size: 13 }) }));
|
|
73
|
+
}
|
|
74
|
+
export function SaveCancel({ size, busy, disabled, onSave, onCancel, }) {
|
|
75
|
+
const { t } = useTranslation();
|
|
76
|
+
const btnSize = size === 'xs' ? 'iconXs' : 'iconSm';
|
|
77
|
+
const icon = size === 'xs' ? 13 : 14;
|
|
78
|
+
const off = busy || disabled;
|
|
79
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "outline", size: btnSize, disabled: off, onClick: onSave, className: ACTION_CHIP_ACCENT, title: t('actions.save'), children: _jsx(Check, { size: icon }) }), _jsx(Button, { type: "button", variant: "outline", size: btnSize, onClick: onCancel, className: "h-7 w-7 rounded-md border border-border bg-surface/80 text-muted hover:border-danger hover:text-danger", title: t('actions.cancel'), children: _jsx(X, { size: icon }) })] }));
|
|
80
|
+
}
|
|
81
|
+
export const EDIT_INLINE_PAD = `${FIELD_ROW_CLS} border border-transparent`;
|
|
82
|
+
export function FieldBox({ geom, field, children }) {
|
|
83
|
+
const box = resolveBox(field, geom);
|
|
84
|
+
if (box === 'none')
|
|
85
|
+
return _jsx(_Fragment, { children: children });
|
|
86
|
+
return _jsx("div", { className: boxClass(box), children: children });
|
|
87
|
+
}
|
|
88
|
+
export function FieldError({ error }) {
|
|
89
|
+
if (!error)
|
|
90
|
+
return null;
|
|
91
|
+
return (_jsxs("p", { className: "text-xs text-danger flex items-center gap-1 mt-0.5", children: [_jsx(Icon, { name: "lucide:alert-triangle", size: 12, className: "shrink-0" }), " ", error] }));
|
|
92
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
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 { FieldFrame } from "../field-frame.js";
|
|
5
|
+
import { NumberInput } from "./number.js";
|
|
6
|
+
function AmountDisplay({ field, value }) {
|
|
7
|
+
const { t } = useTranslation();
|
|
8
|
+
const u = field.hints['unitValue'];
|
|
9
|
+
if (value == null || value === '')
|
|
10
|
+
return _jsx("span", { className: "text-muted", children: "\u2014" });
|
|
11
|
+
return (_jsxs("span", { className: "font-mono", children: [String(value), u ? _jsx("span", { className: "ml-1 text-xs text-muted", children: t(`core.units.${String(u)}`) }) : null] }));
|
|
12
|
+
}
|
|
13
|
+
function AmountPage(p) {
|
|
14
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "inline", width: "w-32", bare: p.bare, view: (v) => _jsx(AmountDisplay, { field: p.field, value: v }), edit: (v, onChange) => (_jsx(NumberInput, { name: `field-${p.field.label}`, field: p.field, value: v, onChange: onChange })) }));
|
|
15
|
+
}
|
|
16
|
+
function AmountInline({ value }) {
|
|
17
|
+
return _jsx("span", { className: "truncate font-mono", children: value == null || value === '' ? '—' : String(value) });
|
|
18
|
+
}
|
|
19
|
+
registerRenderer({ kinds: ['amount'], Page: AmountPage, Inline: AmountInline });
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { FieldMeta } from '@coffer-org/sdk/fields';
|
|
3
|
+
export declare function attachmentSlots(field: FieldMeta): {
|
|
4
|
+
lKey: string;
|
|
5
|
+
fKey: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function attachmentFileKind(field: FieldMeta): string;
|
|
8
|
+
export declare function attachmentRoleFixed(field: FieldMeta, role: string): boolean;
|
|
9
|
+
export declare function AttachmentStack({ name, file }: {
|
|
10
|
+
name: ReactNode;
|
|
11
|
+
file: ReactNode;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
13
|
+
export declare function AttachmentBody({ field, value }: {
|
|
14
|
+
field: FieldMeta;
|
|
15
|
+
value: unknown;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
export declare function AttachmentFileCell({ file, fixed, kind, onChange, }: {
|
|
18
|
+
file: unknown;
|
|
19
|
+
fixed: boolean;
|
|
20
|
+
kind: string;
|
|
21
|
+
onChange?: (v: unknown) => void;
|
|
22
|
+
}): import("react").JSX.Element;
|
|
23
|
+
export declare function AttachmentLabelCell({ label, fixed, meta, fieldLabel, onChange, }: {
|
|
24
|
+
label: unknown;
|
|
25
|
+
fixed: boolean;
|
|
26
|
+
meta: FieldMeta;
|
|
27
|
+
fieldLabel?: string;
|
|
28
|
+
onChange?: (v: unknown) => void | Promise<string | undefined>;
|
|
29
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { partValueKeys } from '@coffer-org/sdk/fields';
|
|
3
|
+
import { registerRenderer } from "../../registry.js";
|
|
4
|
+
import { Input } from "../../components/ui/input.js";
|
|
5
|
+
import { FieldFrame } from "../field-frame.js";
|
|
6
|
+
import { FieldLabel, FieldError } from "../chrome.js";
|
|
7
|
+
import { PopoverField } from "../popover-field.js";
|
|
8
|
+
import { Dash, compositeRoleValue, useCompositeLiveGate } from "../shared.js";
|
|
9
|
+
import { parseFileRecs } from "../image.js";
|
|
10
|
+
import { ReadOnlyFiles, SingleFileWidget } from "./file.js";
|
|
11
|
+
export function attachmentSlots(field) {
|
|
12
|
+
const slots = partValueKeys(field.parts ?? []);
|
|
13
|
+
return { lKey: slots['label'] ?? 'label', fKey: slots['file'] ?? 'file' };
|
|
14
|
+
}
|
|
15
|
+
function parseAttachment(v, field) {
|
|
16
|
+
const { lKey, fKey } = attachmentSlots(field);
|
|
17
|
+
const obj = v && typeof v === 'object' ? v : null;
|
|
18
|
+
if (!obj)
|
|
19
|
+
return {};
|
|
20
|
+
return { label: obj[lKey], file: obj[fKey] };
|
|
21
|
+
}
|
|
22
|
+
export function attachmentFileKind(field) {
|
|
23
|
+
return field.parts?.find((p) => p.role === 'file')?.meta.kind ?? 'file';
|
|
24
|
+
}
|
|
25
|
+
export function attachmentRoleFixed(field, role) {
|
|
26
|
+
const part = field.parts?.find((p) => p.role === role);
|
|
27
|
+
return part != null && part.mode !== 'stored';
|
|
28
|
+
}
|
|
29
|
+
export function AttachmentStack({ name, file }) {
|
|
30
|
+
return (_jsxs("div", { "data-testid": "attachment", className: "flex min-w-0 flex-col gap-1", children: [name, file] }));
|
|
31
|
+
}
|
|
32
|
+
function AttachmentName({ label, dash }) {
|
|
33
|
+
if (label == null || label === '')
|
|
34
|
+
return dash ? _jsx(Dash, {}) : null;
|
|
35
|
+
return (_jsx("span", { "data-testid": "attachment-label", className: "block truncate text-[15px] font-medium", children: String(label) }));
|
|
36
|
+
}
|
|
37
|
+
export function AttachmentBody({ field, value }) {
|
|
38
|
+
const { label, file } = parseAttachment(value, field);
|
|
39
|
+
return (_jsx(AttachmentStack, { name: _jsx(AttachmentName, { label: label }), file: _jsx(ReadOnlyFiles, { kind: attachmentFileKind(field), value: file ?? '' }) }));
|
|
40
|
+
}
|
|
41
|
+
export function AttachmentFileCell({ file, fixed, kind, onChange, }) {
|
|
42
|
+
if (fixed || !onChange)
|
|
43
|
+
return _jsx(ReadOnlyFiles, { kind: kind, value: file ?? '' });
|
|
44
|
+
return _jsx(SingleFileWidget, { kind: kind, value: file ?? '', onChange: onChange });
|
|
45
|
+
}
|
|
46
|
+
export function AttachmentLabelCell({ label, fixed, meta, fieldLabel, onChange, }) {
|
|
47
|
+
if (fixed || !onChange)
|
|
48
|
+
return _jsx(AttachmentName, { label: label, dash: true });
|
|
49
|
+
const pickerField = { ...meta, kind: 'text', hints: { ...meta.hints, noLabel: true } };
|
|
50
|
+
return (_jsx(PopoverField, { field: pickerField, value: label ?? '', label: fieldLabel, geom: "none", width: "w-full", view: (v) => (v != null && v !== '' ? _jsx(AttachmentName, { label: v }) : _jsx(Dash, {})), edit: (v, set) => _jsx(Input, { value: v == null ? '' : String(v), onChange: (e) => set(e.target.value) }), onCommit: onChange }));
|
|
51
|
+
}
|
|
52
|
+
function AttachmentInput({ field, value, onChange, }) {
|
|
53
|
+
const { lKey, fKey } = attachmentSlots(field);
|
|
54
|
+
const { label, file } = parseAttachment(value, field);
|
|
55
|
+
const kind = attachmentFileKind(field);
|
|
56
|
+
const labelFixed = attachmentRoleFixed(field, 'label');
|
|
57
|
+
const fileFixed = attachmentRoleFixed(field, 'file');
|
|
58
|
+
const set = (next) => {
|
|
59
|
+
const l = compositeRoleValue(next.label);
|
|
60
|
+
const f = compositeRoleValue(next.file);
|
|
61
|
+
onChange(l == null && f == null ? '' : { [lKey]: l, [fKey]: f });
|
|
62
|
+
};
|
|
63
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [labelFixed ? (_jsx(AttachmentName, { label: label, dash: true })) : (_jsx(Input, { id: `field-${field.label}`, value: label == null ? '' : String(label), onChange: (e) => set({ label: e.target.value, file }) })), fileFixed ? (_jsx(ReadOnlyFiles, { kind: kind, value: file ?? '' })) : (_jsx(SingleFileWidget, { kind: kind, value: file ?? '', onChange: (v) => set({ label, file: v }) }))] }));
|
|
64
|
+
}
|
|
65
|
+
function AttachmentLive({ field, value, onChange, label, required, error, }) {
|
|
66
|
+
const { lKey, fKey } = attachmentSlots(field);
|
|
67
|
+
const { label: name, file } = parseAttachment(value, field);
|
|
68
|
+
const kind = attachmentFileKind(field);
|
|
69
|
+
const labelFixed = attachmentRoleFixed(field, 'label');
|
|
70
|
+
const fileFixed = attachmentRoleFixed(field, 'file');
|
|
71
|
+
const labelPart = field.parts?.find((p) => p.role === 'label');
|
|
72
|
+
const noLabel = field.hints['noLabel'] === true || label === undefined;
|
|
73
|
+
const write = (nextLabel, nextFile) => {
|
|
74
|
+
const l = compositeRoleValue(nextLabel);
|
|
75
|
+
const f = compositeRoleValue(nextFile);
|
|
76
|
+
return onChange(l == null && f == null ? '' : { [lKey]: l, [fKey]: f });
|
|
77
|
+
};
|
|
78
|
+
return (_jsxs("div", { className: "group/field relative w-full", children: [!noLabel && _jsx(FieldLabel, { label: label, required: required }), _jsx(AttachmentStack, { name: _jsx(AttachmentLabelCell, { label: name, fixed: labelFixed, meta: labelPart?.meta ?? field, fieldLabel: label, onChange: labelFixed ? undefined : (v) => write(v, file) }), file: _jsx(AttachmentFileCell, { file: file, fixed: fileFixed, kind: kind, onChange: fileFixed ? undefined : (v) => void write(name, v) }) }), _jsx(FieldError, { error: error })] }));
|
|
79
|
+
}
|
|
80
|
+
function AttachmentPage(p) {
|
|
81
|
+
const { live, write } = useCompositeLiveGate(p);
|
|
82
|
+
if (live) {
|
|
83
|
+
return (_jsx(AttachmentLive, { field: p.field, value: p.value, onChange: write, label: p.label, required: p.required, error: p.error }));
|
|
84
|
+
}
|
|
85
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "none", width: "w-full", bare: p.bare, view: (v) => _jsx(AttachmentBody, { field: p.field, value: v }), edit: (value, onChange) => (_jsx(AttachmentInput, { field: p.field, value: value, onChange: onChange })) }));
|
|
86
|
+
}
|
|
87
|
+
function AttachmentInline({ field, value }) {
|
|
88
|
+
const { label, file } = parseAttachment(value, field);
|
|
89
|
+
if (label != null && label !== '')
|
|
90
|
+
return _jsx("span", { className: "truncate", children: String(label) });
|
|
91
|
+
const rec = parseFileRecs(file)[0];
|
|
92
|
+
if (rec)
|
|
93
|
+
return _jsx("span", { className: "truncate text-muted", children: rec.name });
|
|
94
|
+
return _jsx("span", { className: "text-muted", children: "\u2014" });
|
|
95
|
+
}
|
|
96
|
+
registerRenderer({
|
|
97
|
+
kinds: ['attachment'],
|
|
98
|
+
layout: 'block',
|
|
99
|
+
Page: AttachmentPage,
|
|
100
|
+
Inline: AttachmentInline,
|
|
101
|
+
editor: 'custom',
|
|
102
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Checkbox } from "../../components/ui/checkbox.js";
|
|
3
|
+
import { Icon } from "../../components/Icon.js";
|
|
4
|
+
import { registerRenderer } from "../../registry.js";
|
|
5
|
+
import { isChecked } from "../use-field-edit.js";
|
|
6
|
+
import { asBool, fieldOptions } from "../../lib/format.js";
|
|
7
|
+
import { FieldFrame } from "../field-frame.js";
|
|
8
|
+
import { useRecordSave } from "../record-save-ctx.js";
|
|
9
|
+
import { useHybridScope } from "../hybrid-scope.js";
|
|
10
|
+
function selectOpts(field, selectOptions) {
|
|
11
|
+
return selectOptions ?? fieldOptions(field);
|
|
12
|
+
}
|
|
13
|
+
function BooleanValue({ value }) {
|
|
14
|
+
return _jsx("span", { children: isChecked(value) ? _jsx(Icon, { name: "lucide:check", size: 14, className: "inline text-accent" }) : '—' });
|
|
15
|
+
}
|
|
16
|
+
function BooleanCheckbox({ value, onChange, label, }) {
|
|
17
|
+
const { saving } = useRecordSave();
|
|
18
|
+
return (_jsx(Checkbox, { checked: isChecked(value), disabled: saving, "aria-label": label, onCheckedChange: (c) => onChange?.(c === true) }));
|
|
19
|
+
}
|
|
20
|
+
function BooleanPage(p) {
|
|
21
|
+
const live = useHybridScope();
|
|
22
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "inline", width: "w-32", bare: p.bare, view: (v, commit) => commit && live ? (_jsx(BooleanCheckbox, { value: v, label: p.label, onChange: (b) => void commit(b) })) : (_jsx(BooleanValue, { field: p.field, value: v })), edit: (v, onChange) => _jsx(BooleanCheckbox, { value: v, label: p.label, onChange: onChange }) }));
|
|
23
|
+
}
|
|
24
|
+
function BooleanInline({ value }) {
|
|
25
|
+
return _jsx("span", { children: isChecked(value) ? _jsx(Icon, { name: "lucide:check", size: 14, className: "inline text-accent" }) : '—' });
|
|
26
|
+
}
|
|
27
|
+
function booleanKeys(value, _field, ctx) {
|
|
28
|
+
if (value == null || value === '')
|
|
29
|
+
return [];
|
|
30
|
+
const b = asBool(value);
|
|
31
|
+
return [{ value: b ? 'true' : 'false', rawValue: b, label: b ? ctx.t('group.yes') : ctx.t('group.no') }];
|
|
32
|
+
}
|
|
33
|
+
registerRenderer({
|
|
34
|
+
kinds: ['boolean'],
|
|
35
|
+
Page: BooleanPage,
|
|
36
|
+
Inline: BooleanInline,
|
|
37
|
+
Value: BooleanValue,
|
|
38
|
+
editor: 'inplace',
|
|
39
|
+
keys: booleanKeys,
|
|
40
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { RelationPickerWidget, MeasuredWidget, MeasuredMultiInput, CheckWidget, MultiCheckInput, } from "../widgets/edit.js";
|
|
3
|
+
import { RelationValues, MeasuredDisplay, measuredUnitLabel, measuredSlots, measuredChipContent, CheckDisplay, MultiCheckDisplay, } from "../widgets/display.js";
|
|
4
|
+
import { registerRenderer, multiBlock } from "../../registry.js";
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
import { getShelf } from "../../schema-registry.js";
|
|
7
|
+
import { Button } from "../../components/ui/button.js";
|
|
8
|
+
import { useTranslation, MultipleChips, parseStrArray, colsToCheckRow } from "../shared.js";
|
|
9
|
+
import { parseJSON } from "../../lib/json.js";
|
|
10
|
+
import { FieldFrame } from "../field-frame.js";
|
|
11
|
+
import { OptionList } from "../option-list.js";
|
|
12
|
+
import { useRecordLabels, useRecordsByIds } from "../../lib/use-record-labels.js";
|
|
13
|
+
import { recordOption } from "../../lib/record-option.js";
|
|
14
|
+
function RelationPage(p) {
|
|
15
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "inline", width: "w-56", bare: p.bare, view: (v) => _jsx(RelationValues, { field: p.field, value: v }), edit: (v, onChange) => (_jsx(RelationPickerWidget, { field: p.field, value: v, onChange: onChange, excludeValues: p.excludeValues })) }));
|
|
16
|
+
}
|
|
17
|
+
function RelationPicker({ field, draft, setDraft, save }) {
|
|
18
|
+
const { t } = useTranslation();
|
|
19
|
+
const library = field.relation?.library ?? '';
|
|
20
|
+
const shelf = field.relation?.shelf ?? '';
|
|
21
|
+
const multi = Boolean(field.hints['multi']);
|
|
22
|
+
const displayKey = String(field.hints['displayKey'] ?? 'name');
|
|
23
|
+
const filterQuery = useMemo(() => {
|
|
24
|
+
const f = field.relation?.filter;
|
|
25
|
+
if (!f)
|
|
26
|
+
return undefined;
|
|
27
|
+
return Object.fromEntries(Object.entries(f).map(([k, v]) => [k, String(v)]));
|
|
28
|
+
}, [field.relation?.filter]);
|
|
29
|
+
const selected = useMemo(() => {
|
|
30
|
+
if (draft == null || draft === '')
|
|
31
|
+
return [];
|
|
32
|
+
if (multi) {
|
|
33
|
+
const a = Array.isArray(draft) ? draft : parseJSON(draft, []);
|
|
34
|
+
return a.map(String);
|
|
35
|
+
}
|
|
36
|
+
return [String(draft)];
|
|
37
|
+
}, [draft, multi]);
|
|
38
|
+
const { records, loadState } = useRecordLabels(library, shelf, displayKey, filterQuery);
|
|
39
|
+
const selectedMap = useRecordsByIds(library, shelf, selected);
|
|
40
|
+
const m = getShelf(library, shelf);
|
|
41
|
+
const options = useMemo(() => {
|
|
42
|
+
const byId = new Map(records.map((r) => [String(r['id']), r]));
|
|
43
|
+
for (const [id, row] of selectedMap)
|
|
44
|
+
if (!byId.has(id))
|
|
45
|
+
byId.set(id, row);
|
|
46
|
+
return [...byId.entries()].map(([id, row]) => {
|
|
47
|
+
const o = m ? recordOption(m, row) : { title: id, icon: undefined, subtitle: undefined };
|
|
48
|
+
return {
|
|
49
|
+
value: id,
|
|
50
|
+
title: o.title || id,
|
|
51
|
+
...(o.icon && { icon: o.icon }),
|
|
52
|
+
...(o.subtitle && { subtitle: o.subtitle }),
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
}, [records, selectedMap, m]);
|
|
56
|
+
if (loadState === 'loading')
|
|
57
|
+
return _jsx("p", { className: "text-muted text-sm py-1", children: t('form.loading') });
|
|
58
|
+
if (loadState === 'error')
|
|
59
|
+
return _jsx("p", { className: "text-danger text-sm py-1", children: t('form.loadError') });
|
|
60
|
+
if (!options.length)
|
|
61
|
+
return _jsx("p", { className: "text-muted text-sm py-1", children: t('form.noRecords') });
|
|
62
|
+
return (_jsxs(_Fragment, { children: [_jsx(OptionList, { field: field, options: options, value: multi ? selected : draft, multiple: multi, literal: true, onPick: (next) => {
|
|
63
|
+
if (!multi) {
|
|
64
|
+
save(next === '' ? '' : Number(next));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const ids = next.map(Number);
|
|
68
|
+
setDraft(ids.length ? ids : '');
|
|
69
|
+
} }), multi && (_jsx("div", { className: "mt-2 flex justify-end border-t border-border pt-2", children: _jsx(Button, { type: "button", size: "sm", onClick: () => save(), children: t('actions.save') }) }))] }));
|
|
70
|
+
}
|
|
71
|
+
function RelationInline({ value }) {
|
|
72
|
+
const a = parseJSON(value, null);
|
|
73
|
+
const label = Array.isArray(a) ? (a.length ? String(a.length) : '—') : value ? '1' : '—';
|
|
74
|
+
return _jsx("span", { className: "truncate", children: label });
|
|
75
|
+
}
|
|
76
|
+
function relationKeys(value, _field, ctx) {
|
|
77
|
+
if (value == null || value === '')
|
|
78
|
+
return [];
|
|
79
|
+
const id = String(value);
|
|
80
|
+
const label = ctx.labelMap?.get(id) ?? id;
|
|
81
|
+
return [{ value: id, rawValue: value, label, sort: label }];
|
|
82
|
+
}
|
|
83
|
+
registerRenderer({
|
|
84
|
+
kinds: ['relation'],
|
|
85
|
+
layout: multiBlock,
|
|
86
|
+
Page: RelationPage,
|
|
87
|
+
Inline: RelationInline,
|
|
88
|
+
Picker: RelationPicker,
|
|
89
|
+
keys: relationKeys,
|
|
90
|
+
});
|
|
91
|
+
function MeasuredMultiDisplay({ field, value }) {
|
|
92
|
+
const { t } = useTranslation();
|
|
93
|
+
const items = Array.isArray(value) ? value : parseStrArray(String(value ?? ''));
|
|
94
|
+
return _jsx(MultipleChips, { items: items, content: measuredChipContent(field, t) });
|
|
95
|
+
}
|
|
96
|
+
function MeasuredPage(p) {
|
|
97
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "inline", width: "w-48", bare: p.bare, view: (v) => p.field.hints['multiple'] === true ? (_jsx(MeasuredMultiDisplay, { field: p.field, value: v })) : (_jsx(MeasuredDisplay, { field: p.field, value: v })), edit: (v, onChange) => p.field.hints['multiple'] === true ? (_jsx(MeasuredMultiInput, { field: p.field, value: v, onChange: onChange })) : (_jsx(MeasuredWidget, { value: v, onChange: onChange, field: p.field })) }));
|
|
98
|
+
}
|
|
99
|
+
function MeasuredInline({ field, value }) {
|
|
100
|
+
const { t } = useTranslation();
|
|
101
|
+
const { vKey, uKey } = measuredSlots(field);
|
|
102
|
+
const o = parseJSON(value, null);
|
|
103
|
+
const label = o ? `${o[vKey]} ${measuredUnitLabel(field, String(o[uKey] ?? ''), t)}` : '—';
|
|
104
|
+
return _jsx("span", { className: "truncate", children: label });
|
|
105
|
+
}
|
|
106
|
+
registerRenderer({
|
|
107
|
+
kinds: ['measured'],
|
|
108
|
+
Page: MeasuredPage,
|
|
109
|
+
Inline: MeasuredInline,
|
|
110
|
+
});
|
|
111
|
+
function CheckPage(p) {
|
|
112
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "none", width: "w-full", bare: p.bare, view: (v) => {
|
|
113
|
+
const slots = p.field.hints['slots'] ?? [''];
|
|
114
|
+
const fields = p.field.hints['fields'];
|
|
115
|
+
return p.field.hints['multiple'] === true ? (_jsx(MultiCheckDisplay, { value: v, slots: slots })) : (_jsx(CheckDisplay, { value: v, slots: slots, fields: fields, recordCtx: p.recordCtx }));
|
|
116
|
+
}, edit: (v, onChange) => p.field.hints['multiple'] === true ? (_jsx(MultiCheckInput, { field: p.field, value: v, onChange: onChange })) : (_jsx(CheckWidget, { field: p.field, value: v, recordCtx: p.recordCtx, onChange: onChange })) }));
|
|
117
|
+
}
|
|
118
|
+
function CheckInline({ value, field }) {
|
|
119
|
+
const slots = field.hints['slots'] ?? [''];
|
|
120
|
+
const row = colsToCheckRow(value, slots.length);
|
|
121
|
+
const total = row.checks.length;
|
|
122
|
+
if (!row.text && !row.checks.some(Boolean))
|
|
123
|
+
return _jsx("span", { className: "truncate", children: "\u2014" });
|
|
124
|
+
const label = total ? `${row.checks.filter(Boolean).length}/${total}` : row.text || '—';
|
|
125
|
+
return _jsx("span", { className: "truncate", children: label });
|
|
126
|
+
}
|
|
127
|
+
registerRenderer({
|
|
128
|
+
kinds: ['check'],
|
|
129
|
+
layout: 'block',
|
|
130
|
+
Page: CheckPage,
|
|
131
|
+
Inline: CheckInline,
|
|
132
|
+
});
|