@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,139 @@
|
|
|
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 { hasChildren, isNamedField, isStorageGroup } from '@coffer-org/sdk/fields';
|
|
5
|
+
import { Trash2 } from 'lucide-react';
|
|
6
|
+
import { AddBtn, usedElsewhere } from "../shared.js";
|
|
7
|
+
import { RemoveBtn } from "../multiple-chips.js";
|
|
8
|
+
import { FieldLabel, Rail, STACK, GroupTitle, groupIcon, useGroupDepth, GroupDepthProvider, groupScopeClass, } from "../chrome.js";
|
|
9
|
+
import { Button } from "../../components/ui/button.js";
|
|
10
|
+
import { FieldSlot } from "../dispatch.js";
|
|
11
|
+
import { useFieldErrors } from "../field-errors.js";
|
|
12
|
+
import { GroupField } from "./group-field.js";
|
|
13
|
+
import { useGroupEdit } from "./group-edit.js";
|
|
14
|
+
import { useHybridScope } from "../hybrid-scope.js";
|
|
15
|
+
import { FlowRow } from "./flow-row.js";
|
|
16
|
+
import { fieldLayout } from "../../registry.js";
|
|
17
|
+
export const subFull = (f) => fieldLayout(f) === 'block';
|
|
18
|
+
function RowActionSlot({ onRemove }) {
|
|
19
|
+
const { t } = useTranslation();
|
|
20
|
+
return (_jsxs("div", { className: "flex shrink-0 flex-col", children: [_jsx("div", { className: "min-h-8 mb-1.5", "aria-hidden": true }), _jsx(Button, { type: "button", variant: "outline", size: "iconSm", onClick: onRemove, title: t('actions.delete'), "aria-hidden": onRemove ? undefined : true, tabIndex: onRemove ? undefined : -1, className: `rounded-lg text-muted hover:border-danger hover:text-danger${onRemove ? '' : ' invisible'}`, children: _jsx(Trash2, { size: 14 }) })] }));
|
|
21
|
+
}
|
|
22
|
+
export function CollectionWidget({ group, value, onChange, recordCtx, }) {
|
|
23
|
+
const { t } = useTranslation();
|
|
24
|
+
const errorAt = useFieldErrors();
|
|
25
|
+
const rows = Array.isArray(value) ? value : [];
|
|
26
|
+
const unique = group.unique ?? [];
|
|
27
|
+
function update(idx, key, v) {
|
|
28
|
+
onChange(rows.map((r, i) => (i === idx ? { ...r, [key]: v } : r)));
|
|
29
|
+
}
|
|
30
|
+
function renderSubField(sub, row, idx) {
|
|
31
|
+
if (sub.type.derived) {
|
|
32
|
+
return (_jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [sub.type.label ? _jsx(FieldLabel, { label: t(sub.type.label) }) : null, _jsx(FieldSlot, { field: sub.type, value: row[sub.key] ?? '', mode: "view", bare: true, recordCtx: recordCtx })] }, sub.key));
|
|
33
|
+
}
|
|
34
|
+
return (_jsx("div", { className: "flex flex-col gap-0.5 min-w-0", children: _jsx(FieldSlot, { field: sub.type, fieldKey: sub.key, value: row[sub.key] ?? '', excludeValues: usedElsewhere(rows, unique, sub.key, idx), onChange: (v) => update(idx, sub.key, v), label: sub.type.label ? t(sub.type.label) : undefined, mode: "edit", recordCtx: recordCtx, error: isStorageGroup(group) ? errorAt([group.key, idx, sub.key]) : undefined }) }, sub.key));
|
|
35
|
+
}
|
|
36
|
+
function renderRow(row, idx) {
|
|
37
|
+
return (_jsx(Rail, { children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("div", { className: "flex-1 min-w-0", children: _jsx(FlowRow, { scroll: group.display === 'scroll', items: group.fields.flatMap((sub, sIdx) => {
|
|
38
|
+
if (hasChildren(sub)) {
|
|
39
|
+
if (!isStorageGroup(sub) && sub.display === 'scroll') {
|
|
40
|
+
return [
|
|
41
|
+
{
|
|
42
|
+
key: sub.label ?? 'flow',
|
|
43
|
+
full: true,
|
|
44
|
+
node: (_jsx(FlowRow, { scroll: true, items: sub.fields.flatMap((c) => isNamedField(c) ? [{ key: c.key, node: renderSubField(c, row, idx) }] : []) })),
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
key: sub.key ?? sub.label ?? 'g',
|
|
51
|
+
full: sub.kind === 'per-weekday' ? false : true,
|
|
52
|
+
node: (_jsx(GroupField, { group: sub, value: row[sub.key], onChange: (v) => update(idx, sub.key, v), recordCtx: recordCtx, mode: "edit" })),
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
if (!isNamedField(sub))
|
|
57
|
+
return [];
|
|
58
|
+
return [{ key: sub.key, full: subFull(sub.type), node: renderSubField(sub, row, idx) }];
|
|
59
|
+
}) }) }), _jsx(RowActionSlot, { onRemove: () => onChange(rows.filter((_, i) => i !== idx)) })] }) }, idx));
|
|
60
|
+
}
|
|
61
|
+
return (_jsxs("div", { className: `flex flex-col ${STACK}`, children: [rows.map(renderRow), _jsx(AddBtn, { onClick: () => onChange([...rows, {}]), children: t('form.addRow') })] }));
|
|
62
|
+
}
|
|
63
|
+
export function CollectionGroup({ group, value, onChange, recordCtx, mode, }) {
|
|
64
|
+
const { t } = useTranslation();
|
|
65
|
+
const depth = useGroupDepth();
|
|
66
|
+
const live = useHybridScope() && mode === 'view';
|
|
67
|
+
const g = useGroupEdit(mode, value, (v) => onChange(v));
|
|
68
|
+
if (g.mode === 'view') {
|
|
69
|
+
return (_jsx(CollectionView, { group: group, value: g.value, onChange: (v) => g.onChange(v), recordCtx: recordCtx, actions: live ? null : g.actions, editable: live }));
|
|
70
|
+
}
|
|
71
|
+
return (_jsxs("fieldset", { className: `${groupScopeClass(depth)} flex flex-col min-w-0 ${STACK}`, children: [_jsx(GroupTitle, { as: "legend", label: t(group.label), icon: groupIcon(group), actions: g.actions }), _jsx(CollectionWidget, { group: group, value: g.value, onChange: (v) => g.onChange(v), recordCtx: recordCtx })] }));
|
|
72
|
+
}
|
|
73
|
+
const fieldsetCls = (depth) => `${groupScopeClass(depth)} flex flex-col min-w-0 ${STACK}`;
|
|
74
|
+
function CollectionCell({ sub, row, group, recordCtx, onChange, }) {
|
|
75
|
+
const { t } = useTranslation();
|
|
76
|
+
const live = useHybridScope() && Boolean(onChange);
|
|
77
|
+
const wired = live ? onChange : undefined;
|
|
78
|
+
return (_jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [sub.type.label ? _jsx(FieldLabel, { label: t(sub.type.label) }) : null, _jsx(FieldSlot, { field: sub.type, fieldKey: live ? sub.key : undefined, value: row[sub.key] ?? '', mode: "view", bare: !live, recordCtx: recordCtx, onChange: wired })] }));
|
|
79
|
+
}
|
|
80
|
+
export function CollectionView({ group, value, onChange, recordCtx, actions, editable = false, }) {
|
|
81
|
+
const { t } = useTranslation();
|
|
82
|
+
const depth = useGroupDepth();
|
|
83
|
+
const saved = Array.isArray(value) ? value : [];
|
|
84
|
+
const [pending, setPending] = useState([]);
|
|
85
|
+
const rows = [...saved, ...pending];
|
|
86
|
+
const filled = (r) => Object.values(r).some((v) => v !== '' && v != null && v !== false);
|
|
87
|
+
function persistCell(idx, key, v) {
|
|
88
|
+
const next = rows.map((r, i) => (i === idx ? { ...r, [key]: v } : r)).filter(filled);
|
|
89
|
+
setPending([]);
|
|
90
|
+
onChange(next);
|
|
91
|
+
}
|
|
92
|
+
function removeRow(idx) {
|
|
93
|
+
if (idx < saved.length) {
|
|
94
|
+
setPending([]);
|
|
95
|
+
onChange(saved.filter((_, i) => i !== idx));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
setPending((ps) => ps.filter((_, i) => i !== idx - saved.length));
|
|
99
|
+
}
|
|
100
|
+
return (_jsxs("fieldset", { className: fieldsetCls(depth), children: [_jsx(GroupTitle, { as: "legend", label: t(group.label), icon: groupIcon(group), actions: actions }), _jsx(GroupDepthProvider, { depth: depth + 1, children: _jsxs("div", { className: `flex flex-col ${STACK}`, children: [rows.map((row, idx) => (_jsx(Rail, { children: _jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [_jsx("div", { className: "min-w-0 flex-1", children: _jsx(FlowRow, { scroll: group.display === 'scroll', items: group.fields.flatMap((sub, sIdx) => {
|
|
101
|
+
if (hasChildren(sub)) {
|
|
102
|
+
if (!isStorageGroup(sub) && sub.display === 'scroll') {
|
|
103
|
+
return [
|
|
104
|
+
{
|
|
105
|
+
key: sub.label ?? 'flow',
|
|
106
|
+
full: true,
|
|
107
|
+
node: (_jsx(FlowRow, { scroll: true, items: sub.fields.flatMap((c) => {
|
|
108
|
+
if (!isNamedField(c))
|
|
109
|
+
return [];
|
|
110
|
+
const cKey = c.key;
|
|
111
|
+
return [
|
|
112
|
+
{
|
|
113
|
+
key: cKey,
|
|
114
|
+
node: (_jsx(CollectionCell, { sub: c, row: row, group: group, recordCtx: recordCtx, onChange: (v) => persistCell(idx, cKey, v) })),
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
}) })),
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
return [
|
|
122
|
+
{
|
|
123
|
+
key: sub.key ?? sub.label ?? 'g',
|
|
124
|
+
full: sub.kind === 'per-weekday' ? false : true,
|
|
125
|
+
node: (_jsx(GroupField, { group: sub, value: row[sub.key], onChange: (v) => persistCell(idx, sub.key, v), recordCtx: recordCtx, mode: "view" })),
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
if (!isNamedField(sub))
|
|
130
|
+
return [];
|
|
131
|
+
return [
|
|
132
|
+
{
|
|
133
|
+
key: sub.key,
|
|
134
|
+
full: subFull(sub.type),
|
|
135
|
+
node: (_jsx(CollectionCell, { sub: sub, row: row, group: group, recordCtx: recordCtx, onChange: (v) => persistCell(idx, sub.key, v) })),
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
}) }) }), editable && _jsx(RemoveBtn, { className: "mt-6", onClick: () => removeRow(idx) })] }) }, idx))), editable && _jsx(AddBtn, { onClick: () => setPending((ps) => [...ps, {}]), children: t('form.addRow') })] }) })] }));
|
|
139
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Textarea } from "../../components/ui/textarea.js";
|
|
4
|
+
import { Dash, compositeRoleValue } from "../shared.js";
|
|
5
|
+
import { twoCellGroupPage } from "./two-cell-group.js";
|
|
6
|
+
import { ImageWidget, parseFileRecs } from "../image.js";
|
|
7
|
+
import { IllustratedBody, IllustratedFloat, illustratedSlots, illustratedLayout, illustratedImageKind, IllustratedPicture, IllustratedImageCell, IllustratedTextCell, } from "../core/illustrated.js";
|
|
8
|
+
function IllustratedRowEdit({ meta, row, onChange }) {
|
|
9
|
+
const { tKey, iKey } = illustratedSlots(meta);
|
|
10
|
+
const { width } = illustratedLayout(meta);
|
|
11
|
+
const kind = illustratedImageKind(meta);
|
|
12
|
+
const text = row[tKey];
|
|
13
|
+
const image = row[iKey];
|
|
14
|
+
const textPart = meta.parts?.find((p) => p.role === 'text');
|
|
15
|
+
const textFixed = textPart != null && textPart.mode !== 'stored';
|
|
16
|
+
const imagePart = meta.parts?.find((p) => p.role === 'image');
|
|
17
|
+
const imageFixed = imagePart != null && imagePart.mode !== 'stored';
|
|
18
|
+
const rec = image != null && image !== '' ? parseFileRecs(image)[0] : undefined;
|
|
19
|
+
return (_jsxs("div", { className: "flex flex-1 flex-col gap-3 min-w-0", children: [imageFixed ? (_jsx("div", { className: "text-sm text-muted", children: rec ? _jsx(IllustratedPicture, { name: rec.name, width: width, kind: kind }) : _jsx(Dash, {}) })) : (_jsx(ImageWidget, { value: image ?? '', onChange: (v) => onChange({ ...row, [iKey]: compositeRoleValue(v) }), width: width, poster: kind === 'poster' })), textFixed ? (_jsx("p", { className: "text-[15px] text-muted whitespace-pre-wrap", children: text != null && text !== '' ? String(text) : _jsx(Dash, {}) })) : (_jsx(Textarea, { value: text == null ? '' : String(text), onChange: (e) => onChange({ ...row, [tKey]: compositeRoleValue(e.target.value) }) }))] }));
|
|
20
|
+
}
|
|
21
|
+
function IllustratedRowLive({ meta, row, onChange }) {
|
|
22
|
+
const { tKey, iKey } = illustratedSlots(meta);
|
|
23
|
+
const { width, side } = illustratedLayout(meta);
|
|
24
|
+
const kind = illustratedImageKind(meta);
|
|
25
|
+
const text = row[tKey];
|
|
26
|
+
const image = row[iKey];
|
|
27
|
+
const textPart = meta.parts?.find((p) => p.role === 'text');
|
|
28
|
+
const textFixed = textPart != null && textPart.mode !== 'stored';
|
|
29
|
+
const imagePart = meta.parts?.find((p) => p.role === 'image');
|
|
30
|
+
const imageFixed = imagePart != null && imagePart.mode !== 'stored';
|
|
31
|
+
return (_jsx("div", { className: "flex-1 min-w-0", children: _jsx(IllustratedFloat, { width: width, side: side, picture: _jsx(IllustratedImageCell, { image: image, fixed: imageFixed, width: width, kind: kind, onChange: imageFixed ? undefined : (v) => onChange({ ...row, [iKey]: compositeRoleValue(v) }) }), prose: _jsx(IllustratedTextCell, { text: text, fixed: textFixed, meta: textPart?.meta ?? meta, onChange: textFixed ? undefined : (v) => onChange({ ...row, [tKey]: compositeRoleValue(v) }) }) }) }));
|
|
32
|
+
}
|
|
33
|
+
const IllustratedGroupPage = twoCellGroupPage({
|
|
34
|
+
kind: 'illustrated',
|
|
35
|
+
testid: 'illustrated-list',
|
|
36
|
+
rowGap: 'gap-4',
|
|
37
|
+
RowEdit: IllustratedRowEdit,
|
|
38
|
+
RowLive: IllustratedRowLive,
|
|
39
|
+
Body: IllustratedBody,
|
|
40
|
+
});
|
|
41
|
+
registerRenderer({ kinds: ['illustratedList'], layout: 'block', Page: IllustratedGroupPage, editor: 'custom' });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContainerNode } from '@coffer-org/sdk/fields';
|
|
2
|
+
import type { RecordCtx, FieldMode } from '../../types.ts';
|
|
3
|
+
type Row = Record<string, unknown>;
|
|
4
|
+
export declare function TableGroup({ group, value, onChange, recordCtx, mode, }: {
|
|
5
|
+
group: ContainerNode;
|
|
6
|
+
value: unknown;
|
|
7
|
+
onChange: (v: Row[]) => void;
|
|
8
|
+
recordCtx: RecordCtx;
|
|
9
|
+
mode: FieldMode;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
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 { Trash2 } from 'lucide-react';
|
|
5
|
+
import { hasValue, hasChildren, isNamedField, isStorageGroup } from '@coffer-org/sdk/fields';
|
|
6
|
+
import { FieldSlot } from "../dispatch.js";
|
|
7
|
+
import { useFieldErrors } from "../field-errors.js";
|
|
8
|
+
import { AddBtn, usedElsewhere } from "../shared.js";
|
|
9
|
+
import { GroupTitle, groupIcon, useGroupDepth, GroupDepthProvider, groupScopeClass } from "../chrome.js";
|
|
10
|
+
import { Button } from "../../components/ui/button.js";
|
|
11
|
+
import { useGroupEdit } from "./group-edit.js";
|
|
12
|
+
import { useHybridScope } from "../hybrid-scope.js";
|
|
13
|
+
export function TableGroup({ group, value, onChange, recordCtx, mode, }) {
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
const errorAt = useFieldErrors();
|
|
16
|
+
const depth = useGroupDepth();
|
|
17
|
+
const subs = group.fields.filter((f) => isNamedField(f) || (hasChildren(f) && !!f.kind));
|
|
18
|
+
const colKey = (s) => s.key;
|
|
19
|
+
const colLabel = (s) => (hasValue(s) ? s.type.label : s.label);
|
|
20
|
+
const colSlot = (s) => hasValue(s) ? { field: s.type } : { field: s, el: s };
|
|
21
|
+
const growColIdx = (() => {
|
|
22
|
+
const textIdx = subs.findIndex((s) => hasValue(s) && s.type.prim === 'text');
|
|
23
|
+
if (textIdx !== -1)
|
|
24
|
+
return textIdx;
|
|
25
|
+
const stringIdx = subs.findIndex((s) => hasValue(s) &&
|
|
26
|
+
(s.type.prim === 'string' || s.type.kind === 'link' || s.type.kind === 'url' || s.type.kind === 'relation'));
|
|
27
|
+
if (stringIdx !== -1)
|
|
28
|
+
return stringIdx;
|
|
29
|
+
return 0;
|
|
30
|
+
})();
|
|
31
|
+
const isGrowCol = (_s, i) => i === growColIdx;
|
|
32
|
+
const unique = group.unique ?? [];
|
|
33
|
+
const g = useGroupEdit(mode, value, (v) => onChange(v));
|
|
34
|
+
const isEdit = g.mode !== 'view';
|
|
35
|
+
const live = useHybridScope() && mode === 'view';
|
|
36
|
+
const saved = Array.isArray(g.value) ? g.value : [];
|
|
37
|
+
const [pending, setPending] = useState([]);
|
|
38
|
+
const rows = live ? [...saved, ...pending] : saved;
|
|
39
|
+
const filled = (r) => Object.values(r).some((v) => v !== '' && v != null && v !== false);
|
|
40
|
+
const setRows = (next) => {
|
|
41
|
+
setPending([]);
|
|
42
|
+
g.onChange(live ? next.filter(filled) : next);
|
|
43
|
+
};
|
|
44
|
+
const update = (idx, key, v) => setRows(rows.map((r, i) => (i === idx ? { ...r, [key]: v } : r)));
|
|
45
|
+
const removeRow = (idx) => {
|
|
46
|
+
if (!live || idx < saved.length)
|
|
47
|
+
return setRows(rows.filter((_, i) => i !== idx));
|
|
48
|
+
setPending((ps) => ps.filter((_, i) => i !== idx - saved.length));
|
|
49
|
+
};
|
|
50
|
+
const addRow = () => (live ? setPending((ps) => [...ps, {}]) : setRows([...rows, {}]));
|
|
51
|
+
const rowTools = isEdit || live;
|
|
52
|
+
const header = (_jsx("thead", { children: _jsxs("tr", { className: "border-b border-border/60 bg-muted/15", children: [subs.map((s, i) => (_jsx("th", { className: `text-left text-[11px] font-semibold text-muted uppercase tracking-wider px-3.5 py-2.5 ${isGrowCol(s, i) ? 'w-full min-w-[140px]' : 'whitespace-nowrap min-w-[90px]'}`, children: colLabel(s) ? t(colLabel(s)) : '' }, colKey(s)))), rowTools && _jsx("th", { className: "w-10 px-2", "aria-hidden": true })] }) }));
|
|
53
|
+
const body = (_jsx("tbody", { className: "divide-y divide-border/30", children: rows.map((row, idx) => (_jsxs("tr", { className: "align-top hover:bg-muted/10 transition-colors", children: [subs.map((s, i) => (_jsx("td", { className: `px-3.5 py-2.5 min-h-10 ${isGrowCol(s, i) ? 'min-w-[140px]' : 'min-w-[90px]'}`, children: _jsx(FieldSlot, { ...colSlot(s), value: row[colKey(s)] ?? (hasValue(s) ? '' : null), excludeValues: usedElsewhere(rows, unique, colKey(s), idx), onChange: (v) => update(idx, colKey(s), v), fieldKey: live ? colKey(s) : undefined, mode: isEdit ? 'edit' : 'view', bare: !live, recordCtx: recordCtx, error: isStorageGroup(group) ? errorAt([group.key, idx, colKey(s)]) : undefined }) }, colKey(s)))), rowTools && (_jsx("td", { className: "px-2 py-2 min-h-10 w-10", children: _jsx(Button, { type: "button", variant: "outline", size: "iconSm", onClick: () => removeRow(idx), title: t('actions.delete'), className: "rounded-lg text-muted hover:border-danger hover:text-danger", children: _jsx(Trash2, { size: 14 }) }) }))] }, idx))) }));
|
|
54
|
+
const tableEl = (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("div", { className: "overflow-x-auto rounded-lg border border-border/60 bg-card/20 shadow-2xs", children: _jsxs("table", { className: "w-full border-collapse text-sm", children: [header, body] }) }), rowTools && _jsx(AddBtn, { onClick: addRow, children: t('form.addRow') })] }));
|
|
55
|
+
if (mode !== 'view')
|
|
56
|
+
return tableEl;
|
|
57
|
+
return (_jsxs("section", { className: `${groupScopeClass(depth)} flex flex-col gap-2 min-w-0`, children: [_jsx(GroupTitle, { as: "h3", label: t(group.label), icon: groupIcon(group), actions: live ? null : g.actions }), _jsx(GroupDepthProvider, { depth: depth + 1, children: tableEl })] }));
|
|
58
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { FieldMeta } from '@coffer-org/sdk/fields';
|
|
3
|
+
import type { ElementRenderProps } from '../../types.ts';
|
|
4
|
+
export type Row = Record<string, unknown>;
|
|
5
|
+
export interface TwoCellRowProps {
|
|
6
|
+
meta: FieldMeta;
|
|
7
|
+
row: Row;
|
|
8
|
+
onChange: (row: Row) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface TwoCellGroupSpec {
|
|
11
|
+
kind: string;
|
|
12
|
+
testid: string;
|
|
13
|
+
rowGap: string;
|
|
14
|
+
RowEdit: FC<TwoCellRowProps>;
|
|
15
|
+
RowLive: FC<TwoCellRowProps>;
|
|
16
|
+
Body: FC<{
|
|
17
|
+
field: FieldMeta;
|
|
18
|
+
value: unknown;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
export declare function twoCellGroupPage(spec: TwoCellGroupSpec): FC<ElementRenderProps>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Trash2 } from 'lucide-react';
|
|
4
|
+
import { Button } from "../../components/ui/button.js";
|
|
5
|
+
import { AddBtn, useRowIdentity } from "../shared.js";
|
|
6
|
+
import { useGroupDepth, GroupDepthProvider, groupScopeClass } from "../chrome.js";
|
|
7
|
+
import { useGroupEdit } from "./group-edit.js";
|
|
8
|
+
import { useHybridScope } from "../hybrid-scope.js";
|
|
9
|
+
import { useSlot } from "../slot.js";
|
|
10
|
+
function compositeRowMeta(group, kind) {
|
|
11
|
+
const parts = group.fields.map((f) => {
|
|
12
|
+
const item = f;
|
|
13
|
+
return {
|
|
14
|
+
role: item.key,
|
|
15
|
+
key: item.key,
|
|
16
|
+
meta: item.type,
|
|
17
|
+
mode: item.type.derived ? 'computedStored' : 'stored',
|
|
18
|
+
overridden: false,
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
return { kind, label: '', required: false, prim: kind, column: 'text', hints: {}, parts };
|
|
22
|
+
}
|
|
23
|
+
export function twoCellGroupPage(spec) {
|
|
24
|
+
const { RowEdit, RowLive, Body } = spec;
|
|
25
|
+
return function TwoCellGroupPage({ el, value, mode }) {
|
|
26
|
+
const { t } = useTranslation();
|
|
27
|
+
const depth = useGroupDepth();
|
|
28
|
+
const { write } = useSlot();
|
|
29
|
+
const group = el;
|
|
30
|
+
const meta = compositeRowMeta(group, spec.kind);
|
|
31
|
+
const g = useGroupEdit(mode, value, write);
|
|
32
|
+
const rows = Array.isArray(g.value) ? g.value : [];
|
|
33
|
+
const controlled = g.mode !== 'view';
|
|
34
|
+
const live = useHybridScope() && mode === 'view';
|
|
35
|
+
const rowTools = controlled || live;
|
|
36
|
+
const { rowId, updateRow, removeRow } = useRowIdentity(rows, g.onChange);
|
|
37
|
+
const content = (_jsxs("div", { className: `flex min-w-0 flex-col ${spec.rowGap}`, "data-testid": spec.testid, children: [rows.map((row, idx) => (_jsxs("div", { className: "flex items-start gap-2", children: [controlled ? (_jsx(RowEdit, { meta: meta, row: row, onChange: (next) => updateRow(idx, next) })) : live ? (_jsx(RowLive, { meta: meta, row: row, onChange: (next) => updateRow(idx, next) })) : (_jsx("div", { className: "min-w-0 flex-1", children: _jsx(Body, { field: meta, value: row }) })), rowTools && (_jsx(Button, { type: "button", variant: "ghost", size: "iconSm", onClick: () => removeRow(idx), title: t('actions.delete'), className: "h-5 w-5 shrink-0 rounded text-muted hover:text-danger", children: _jsx(Trash2, { size: 12 }) }))] }, rowId(row)))), rowTools && _jsx(AddBtn, { onClick: () => g.onChange([...rows, {}]), children: t('form.addRow') })] }));
|
|
38
|
+
if (mode !== 'view')
|
|
39
|
+
return content;
|
|
40
|
+
return (_jsxs("section", { className: `${groupScopeClass(depth)} flex min-w-0 flex-col gap-1`, children: [_jsx("div", { className: "flex items-center gap-2", children: _jsx("h3", { className: "text-[13px] font-semibold text-accent", children: t(group.label) }) }), _jsx(GroupDepthProvider, { depth: depth + 1, children: content })] }));
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type UrlValue } from '@coffer-org/sdk/fields';
|
|
2
|
+
import type { FieldMode } from '../../types.ts';
|
|
3
|
+
export declare function UrlWidget({ value, onChange, mode, }: {
|
|
4
|
+
value: unknown;
|
|
5
|
+
onChange: (v: UrlValue | null) => void;
|
|
6
|
+
mode: FieldMode;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ExternalLink } from 'lucide-react';
|
|
3
|
+
import { parseUrl, buildUrl, hrefOf } from '@coffer-org/sdk/fields';
|
|
4
|
+
import { Input } from "../../components/ui/input.js";
|
|
5
|
+
import { Dash } from "../shared.js";
|
|
6
|
+
export function UrlWidget({ value, onChange, mode, }) {
|
|
7
|
+
const v = (value ?? null);
|
|
8
|
+
if (mode === 'view') {
|
|
9
|
+
const text = buildUrl(v);
|
|
10
|
+
if (!text)
|
|
11
|
+
return _jsx(Dash, {});
|
|
12
|
+
return (_jsxs("a", { href: hrefOf(v), target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 text-accent hover:underline text-sm break-all", children: [text, " ", _jsx(ExternalLink, { size: 12 })] }));
|
|
13
|
+
}
|
|
14
|
+
return _jsx(Input, { type: "text", inputMode: "url", value: buildUrl(v), onChange: (e) => onChange(parseUrl(e.target.value)) });
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ContainerNode } from '@coffer-org/sdk/fields';
|
|
2
|
+
import type { FieldMode } from '../../types.ts';
|
|
3
|
+
type DayRow = {
|
|
4
|
+
day: string;
|
|
5
|
+
value: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function WeekdayGrid({ group, value, onChange, mode, }: {
|
|
8
|
+
group: ContainerNode;
|
|
9
|
+
value: unknown;
|
|
10
|
+
onChange: (v: DayRow[]) => void;
|
|
11
|
+
mode: FieldMode;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { isNamedField } from '@coffer-org/sdk/fields';
|
|
4
|
+
import { Input } from "../../components/ui/input.js";
|
|
5
|
+
import { Dash } from "../shared.js";
|
|
6
|
+
import { EDIT_INLINE_PAD } from "../chrome.js";
|
|
7
|
+
import { PopoverField } from "../popover-field.js";
|
|
8
|
+
import { useHybridScope } from "../hybrid-scope.js";
|
|
9
|
+
const DAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];
|
|
10
|
+
const DAY_META = {
|
|
11
|
+
kind: 'number',
|
|
12
|
+
prim: 'number',
|
|
13
|
+
column: 'real',
|
|
14
|
+
label: '',
|
|
15
|
+
required: false,
|
|
16
|
+
hints: {},
|
|
17
|
+
};
|
|
18
|
+
export function WeekdayGrid({ group, value, onChange, mode, }) {
|
|
19
|
+
const { t } = useTranslation();
|
|
20
|
+
const daysShort = t('weekdaysShort', { returnObjects: true });
|
|
21
|
+
const onRecordPage = useHybridScope();
|
|
22
|
+
const rows = Array.isArray(value) ? value : [];
|
|
23
|
+
const byDay = new Map(rows.map((r) => [r.day, Number(r.value)]));
|
|
24
|
+
const editing = mode !== 'view';
|
|
25
|
+
const valueField = group.fields.slice(1).find((f) => isNamedField(f));
|
|
26
|
+
const min = valueField?.type.hints['min'];
|
|
27
|
+
const max = valueField?.type.hints['max'];
|
|
28
|
+
const step = valueField?.type.hints['step'];
|
|
29
|
+
const dayField = valueField ? { ...valueField.type, label: '', required: false } : DAY_META;
|
|
30
|
+
function setDay(day, n) {
|
|
31
|
+
const next = DAYS.flatMap((d) => {
|
|
32
|
+
const v = d === day ? n : (byDay.get(d) ?? 0);
|
|
33
|
+
return v > 0 ? [{ day: d, value: v }] : [];
|
|
34
|
+
});
|
|
35
|
+
onChange(next);
|
|
36
|
+
}
|
|
37
|
+
const numberInput = (v, set) => (_jsx(Input, { type: "number", className: "w-full text-center px-1 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none", min: min, max: max, step: step ?? 'any', value: v === 0 || v == null ? '' : String(v), onChange: (e) => set(e.target.value === '' ? '' : Number(e.target.value)) }));
|
|
38
|
+
const peak = Math.max(max ?? 0, ...DAYS.map((d) => byDay.get(d) ?? 0), 1);
|
|
39
|
+
const today = DAYS[(new Date().getDay() + 6) % 7];
|
|
40
|
+
const cell = (n) => (_jsxs("div", { className: `relative h-11 w-full overflow-hidden rounded-lg border ${n ? 'border-accent/30 text-accent' : 'border-border text-muted/40'}`, children: [n > 0 && (_jsx("span", { "aria-hidden": true, className: "absolute inset-x-0 bottom-0 bg-accent/15", style: { height: `${Math.round((Math.min(n, peak) / peak) * 100)}%` } })), _jsx("span", { className: "relative flex h-full items-center justify-center text-sm font-semibold tabular-nums", children: n || '·' })] }));
|
|
41
|
+
if (!editing && !onRecordPage && !rows.some((r) => Number(r.value) > 0))
|
|
42
|
+
return _jsx(Dash, {});
|
|
43
|
+
return (_jsx("div", { className: `flex ${EDIT_INLINE_PAD}`, children: _jsx("div", { className: "grid grid-cols-[repeat(7,minmax(2.25rem,1fr))] gap-1.5 w-full max-w-72", children: DAYS.map((day, i) => {
|
|
44
|
+
const n = byDay.get(day) ?? 0;
|
|
45
|
+
return (_jsxs("div", { className: "flex flex-col items-stretch gap-1 min-w-0 text-center", children: [_jsx("span", { className: `text-[10px] ${day === today ? 'font-semibold text-accent' : 'text-muted'}`, children: daysShort[i] }), onRecordPage ? (_jsx(PopoverField, { field: dayField, value: n || '', geom: "none", width: "w-full", view: () => cell(n), edit: numberInput, onCommit: (v) => setDay(day, Number(v) || 0) })) : editing ? (numberInput(n || '', (nv) => setDay(day, Number(nv) || 0))) : (cell(n))] }, day));
|
|
46
|
+
}) }) }));
|
|
47
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type FieldMeta } from '@coffer-org/sdk/fields';
|
|
2
|
+
export type Commit = (next: unknown) => void | Promise<string | undefined>;
|
|
3
|
+
export type IssueText = (code: string, params?: Record<string, unknown>) => string;
|
|
4
|
+
export declare function makeWriter(field: FieldMeta | undefined, commit: Commit, issueText?: IssueText, validateWith?: FieldMeta): (next: unknown) => Promise<string | undefined>;
|
|
5
|
+
export declare function notWritable(issueText?: IssueText): Commit;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { validateField } from '@coffer-org/sdk/fields';
|
|
2
|
+
const bareCode = (code) => code;
|
|
3
|
+
export function makeWriter(field, commit, issueText = bareCode, validateWith) {
|
|
4
|
+
return async (next) => {
|
|
5
|
+
const judge = validateWith ?? field;
|
|
6
|
+
const issue = judge?.zod ? validateField(judge, next) : null;
|
|
7
|
+
if (issue)
|
|
8
|
+
return issueText(issue.code, issue.params);
|
|
9
|
+
return (await commit(next)) ?? undefined;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function notWritable(issueText = bareCode) {
|
|
13
|
+
return async () => issueText('notWritable');
|
|
14
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Registry } from '@coffer-org/sdk/compose';
|
|
2
|
+
export type SchemaRegistry = Pick<Registry, 'getShelf' | 'getOptionList'>;
|
|
3
|
+
export declare function setSchemaRegistry(registry: SchemaRegistry): void;
|
|
4
|
+
export declare function resetSchemaRegistry(): void;
|
|
5
|
+
export declare const getShelf: SchemaRegistry['getShelf'];
|
|
6
|
+
export declare const getOptionList: SchemaRegistry['getOptionList'];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const MEMBERS = ['getShelf', 'getOptionList'];
|
|
2
|
+
let schema = null;
|
|
3
|
+
export function setSchemaRegistry(registry) {
|
|
4
|
+
if (!registry || typeof registry !== 'object') {
|
|
5
|
+
throw new Error('[web-ui] setSchemaRegistry() needs the composed registry object');
|
|
6
|
+
}
|
|
7
|
+
const missing = MEMBERS.filter((name) => typeof registry[name] !== 'function');
|
|
8
|
+
if (missing.length) {
|
|
9
|
+
throw new Error(`[web-ui] setSchemaRegistry() is missing ${missing.join(', ')} — ` +
|
|
10
|
+
`the kit reads these from the host's composed schema`);
|
|
11
|
+
}
|
|
12
|
+
schema = registry;
|
|
13
|
+
}
|
|
14
|
+
export function resetSchemaRegistry() {
|
|
15
|
+
schema = null;
|
|
16
|
+
}
|
|
17
|
+
function must() {
|
|
18
|
+
if (!schema) {
|
|
19
|
+
throw new Error('[web-ui] the schema registry is not registered — the application must call ' +
|
|
20
|
+
'setSchemaRegistry() with its composed registry before the first render');
|
|
21
|
+
}
|
|
22
|
+
return schema;
|
|
23
|
+
}
|
|
24
|
+
export const getShelf = (library, shelf) => must().getShelf(library, shelf);
|
|
25
|
+
export const getOptionList = (name) => must().getOptionList(name);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface SiteRoute {
|
|
2
|
+
pattern: string;
|
|
3
|
+
purpose: string;
|
|
4
|
+
}
|
|
5
|
+
export interface SiteShelf {
|
|
6
|
+
library: string;
|
|
7
|
+
shelf: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function registerSiteLinks(next: SiteRoute[]): void;
|
|
10
|
+
export declare function getSiteLinks(): SiteRoute[];
|
|
11
|
+
export declare function registerSiteShelves(next: SiteShelf[]): void;
|
|
12
|
+
export declare function getSiteShelves(): SiteShelf[];
|
|
13
|
+
export declare function resetSiteLinks(): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
let routes = [];
|
|
2
|
+
let shelves = [];
|
|
3
|
+
export function registerSiteLinks(next) {
|
|
4
|
+
routes = [...next];
|
|
5
|
+
}
|
|
6
|
+
export function getSiteLinks() {
|
|
7
|
+
return [...routes];
|
|
8
|
+
}
|
|
9
|
+
export function registerSiteShelves(next) {
|
|
10
|
+
shelves = [...next];
|
|
11
|
+
}
|
|
12
|
+
export function getSiteShelves() {
|
|
13
|
+
return [...shelves];
|
|
14
|
+
}
|
|
15
|
+
export function resetSiteLinks() {
|
|
16
|
+
routes = [];
|
|
17
|
+
shelves = [];
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function stripMarkdown(src: string): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function stripMarkdown(src) {
|
|
2
|
+
return (src ?? '')
|
|
3
|
+
.replace(/```[\s\S]*?```/g, ' ')
|
|
4
|
+
.replace(/`([^`]+)`/g, '$1')
|
|
5
|
+
.replace(/!\[[^\]]*\]\([^)]*\)/g, ' ')
|
|
6
|
+
.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
|
|
7
|
+
.replace(/^#{1,6}\s+/gm, '')
|
|
8
|
+
.replace(/[*_~>#-]+/g, ' ')
|
|
9
|
+
.replace(/\s+/g, ' ')
|
|
10
|
+
.trim();
|
|
11
|
+
}
|