@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,92 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import type { ComboboxItem } from '../components/ui/select-combobox.tsx';
|
|
5
|
+
import type { OptionItem, FieldMeta } from '@coffer-org/sdk/fields';
|
|
6
|
+
import type { FieldRenderProps } from '../types.ts';
|
|
7
|
+
import { MultipleChips, Dash } from './multiple-chips.tsx';
|
|
8
|
+
export declare const parseStrArray: (value: string | string[]) => string[];
|
|
9
|
+
export { RemoveBtn, Chip, ChipList } from './multiple-chips.tsx';
|
|
10
|
+
export { MultipleChips, Dash };
|
|
11
|
+
export declare function RemoveSlot({ onRemove, className }: {
|
|
12
|
+
onRemove?: () => void;
|
|
13
|
+
className?: string;
|
|
14
|
+
}): React.JSX.Element;
|
|
15
|
+
export declare function AddBtn({ onClick, children, className, }: {
|
|
16
|
+
onClick: () => void;
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
className?: string;
|
|
19
|
+
}): React.JSX.Element;
|
|
20
|
+
export declare function UploadButton({ accept, busy, error, progress, onPick, onRetry, canRetry, compact, }: {
|
|
21
|
+
accept: string;
|
|
22
|
+
busy: boolean;
|
|
23
|
+
error: string | null;
|
|
24
|
+
progress?: number;
|
|
25
|
+
onPick: (file: File | null) => void;
|
|
26
|
+
onRetry?: () => void;
|
|
27
|
+
canRetry?: boolean;
|
|
28
|
+
compact?: boolean;
|
|
29
|
+
}): React.JSX.Element;
|
|
30
|
+
export declare function UploadError({ error, onRetry, canRetry }: {
|
|
31
|
+
error: string;
|
|
32
|
+
onRetry?: () => void;
|
|
33
|
+
canRetry?: boolean;
|
|
34
|
+
}): React.JSX.Element;
|
|
35
|
+
export declare function UploadProgress({ progress, label }: {
|
|
36
|
+
progress: number;
|
|
37
|
+
label?: string;
|
|
38
|
+
}): React.JSX.Element;
|
|
39
|
+
export declare function toComboItems(opts: OptionItem[], t: (k: string) => string): ComboboxItem[];
|
|
40
|
+
export declare function UiActionSelect({ placeholder, options, onPick, className, }: {
|
|
41
|
+
placeholder: string;
|
|
42
|
+
options: {
|
|
43
|
+
value: string;
|
|
44
|
+
label: string;
|
|
45
|
+
}[];
|
|
46
|
+
onPick: (v: string) => void;
|
|
47
|
+
className?: string;
|
|
48
|
+
}): React.JSX.Element;
|
|
49
|
+
export { CHIP_CLS } from '../components/ui/chip-cls.ts';
|
|
50
|
+
export declare function circledNum(i: number): string;
|
|
51
|
+
export declare function colsToCheckRow(value: unknown, n: number): {
|
|
52
|
+
text: string;
|
|
53
|
+
checks: boolean[];
|
|
54
|
+
};
|
|
55
|
+
export declare function checkRowToCols(row: {
|
|
56
|
+
text: string;
|
|
57
|
+
checks: boolean[];
|
|
58
|
+
}): Record<string, unknown>;
|
|
59
|
+
export declare function CheckLegend({ slots }: {
|
|
60
|
+
slots: string[];
|
|
61
|
+
}): React.JSX.Element | null;
|
|
62
|
+
export declare const checkNumbered: (slotCount: number) => boolean;
|
|
63
|
+
export declare const enterAdd: (add: () => void) => (e: React.KeyboardEvent) => void;
|
|
64
|
+
export declare function MultiTagInput({ value, onChange, renderInput, validate, serialize, display, }: {
|
|
65
|
+
value: string | string[];
|
|
66
|
+
onChange: (v: string[] | '') => void;
|
|
67
|
+
renderInput: (v: string, set: (v: string) => void, add: () => void) => React.ReactNode;
|
|
68
|
+
validate?: (v: string) => boolean;
|
|
69
|
+
serialize?: (raw: string) => string | null;
|
|
70
|
+
display?: (stored: string) => React.ReactNode;
|
|
71
|
+
}): React.JSX.Element;
|
|
72
|
+
export declare function useRowIdentity<T extends object>(rows: T[], onChange: (rows: T[]) => void): {
|
|
73
|
+
rowId: (row: T) => number;
|
|
74
|
+
updateRow: (idx: number, next: T) => void;
|
|
75
|
+
removeRow: (idx: number) => void;
|
|
76
|
+
};
|
|
77
|
+
export declare function useCompositeLiveGate(p: FieldRenderProps): {
|
|
78
|
+
live: boolean;
|
|
79
|
+
write?: (next: unknown) => Promise<string | undefined>;
|
|
80
|
+
};
|
|
81
|
+
export declare function compositeRoleValue(v: unknown): unknown;
|
|
82
|
+
export declare function hasValue(v: unknown): boolean;
|
|
83
|
+
export declare function usedElsewhere(rows: Record<string, unknown>[], unique: string[], key: string, idx: number): string[];
|
|
84
|
+
export declare function shortId(id: string): string;
|
|
85
|
+
export declare const trunc: (s: string) => string;
|
|
86
|
+
export { useTranslation };
|
|
87
|
+
export declare function multiView(field: FieldMeta, value: unknown, content: (item: unknown) => ReactNode, commit?: (v: unknown) => void | Promise<string | undefined>, chip?: {
|
|
88
|
+
chipClassName?: () => string;
|
|
89
|
+
chipStyle?: (item: unknown) => React.CSSProperties;
|
|
90
|
+
}): ReactNode;
|
|
91
|
+
export declare function orDash(value: unknown, render: (v: unknown) => ReactNode, isEmpty?: (v: unknown) => boolean): ReactNode;
|
|
92
|
+
export declare function firstCoverKey(fm: Record<string, FieldMeta>): string | undefined;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Button } from "../components/ui/button.js";
|
|
5
|
+
import { InputGroup, InputGroupAddon, InputGroupButton } from "../components/ui/input-group.js";
|
|
6
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../components/ui/select.js";
|
|
7
|
+
import { Plus, Upload, Loader2, Trash2, RotateCcw } from 'lucide-react';
|
|
8
|
+
import { useHybridScope } from "./hybrid-scope.js";
|
|
9
|
+
import { useSlotOptional } from "./slot.js";
|
|
10
|
+
import { parseJSON } from "../lib/json.js";
|
|
11
|
+
import { MultipleChips, Dash } from "./multiple-chips.js";
|
|
12
|
+
import { useOnPickerSurface } from "./picker-surface.js";
|
|
13
|
+
export const parseStrArray = (value) => Array.isArray(value) ? value : parseJSON(value, []);
|
|
14
|
+
export { RemoveBtn, Chip, ChipList } from "./multiple-chips.js";
|
|
15
|
+
export { MultipleChips, Dash };
|
|
16
|
+
export function RemoveSlot({ onRemove, className }) {
|
|
17
|
+
const { t } = useTranslation();
|
|
18
|
+
return (_jsx(Button, { type: "button", variant: "outline", size: "iconSm", onClick: onRemove, title: t('actions.delete'), "aria-hidden": onRemove ? undefined : true, tabIndex: onRemove ? undefined : -1, className: `shrink-0 rounded-lg text-muted hover:border-danger hover:text-danger${onRemove ? '' : ' invisible'}${className ? ` ${className}` : ''}`, children: _jsx(Trash2, { size: 14 }) }));
|
|
19
|
+
}
|
|
20
|
+
export function AddBtn({ onClick, children, className, }) {
|
|
21
|
+
return (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: onClick, className: `self-start font-medium text-xs rounded-lg gap-1.5 transition-colors border-border/80 text-foreground/80 hover:bg-muted/40 hover:text-foreground ${className ?? ''}`, children: children }));
|
|
22
|
+
}
|
|
23
|
+
export function UploadButton({ accept, busy, error, progress, onPick, onRetry, canRetry, compact, }) {
|
|
24
|
+
const { t } = useTranslation();
|
|
25
|
+
return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("label", { className: `inline-flex w-fit cursor-pointer items-center gap-1.5 rounded-lg border border-accent/30 text-xs font-medium text-accent hover:border-accent/60 ${compact ? 'px-2.5 py-1' : 'px-3 py-2'} ${busy ? 'pointer-events-none opacity-60' : ''}`, children: [busy ? _jsx(Loader2, { size: 13, className: "animate-spin" }) : _jsx(Upload, { size: 13 }), " ", t('actions.upload'), _jsx("input", { type: "file", accept: accept, className: "hidden", disabled: busy, onChange: (e) => onPick(e.target.files?.[0] ?? null) })] }), busy && progress != null && _jsx(UploadProgress, { progress: progress }), error && _jsx(UploadError, { error: error, onRetry: onRetry, canRetry: canRetry })] }));
|
|
26
|
+
}
|
|
27
|
+
export function UploadError({ error, onRetry, canRetry }) {
|
|
28
|
+
const { t } = useTranslation();
|
|
29
|
+
return (_jsxs("div", { className: "flex items-center gap-2 text-xs text-danger", children: [_jsx("span", { className: "min-w-0 flex-1", children: t(error) }), canRetry && onRetry && (_jsxs("button", { type: "button", className: "inline-flex shrink-0 items-center gap-1 rounded px-1.5 py-0.5 font-medium text-accent hover:bg-elevated", onClick: onRetry, children: [_jsx(RotateCcw, { size: 12 }), " ", t('actions.retry')] }))] }));
|
|
30
|
+
}
|
|
31
|
+
export function UploadProgress({ progress, label = 'upload.progress' }) {
|
|
32
|
+
const { t } = useTranslation();
|
|
33
|
+
return (_jsx("div", { role: "progressbar", "aria-label": t(label), "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": progress, className: "h-1.5 w-full overflow-hidden rounded-full bg-border", children: _jsx("div", { className: "h-full rounded-full bg-accent transition-[width]", style: { width: `${Math.max(0, Math.min(100, progress))}%` } }) }));
|
|
34
|
+
}
|
|
35
|
+
export function toComboItems(opts, t) {
|
|
36
|
+
return opts.map((o) => ({
|
|
37
|
+
value: o.value,
|
|
38
|
+
title: t(o.title),
|
|
39
|
+
subtitle: o.subtitle,
|
|
40
|
+
icon: o.icon,
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
export function UiActionSelect({ placeholder, options, onPick, className, }) {
|
|
44
|
+
const [k, setK] = useState(0);
|
|
45
|
+
return (_jsxs(Select, { onValueChange: (v) => {
|
|
46
|
+
onPick(v);
|
|
47
|
+
setK((n) => n + 1);
|
|
48
|
+
}, children: [_jsx(SelectTrigger, { className: className, children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { children: options.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }, k));
|
|
49
|
+
}
|
|
50
|
+
export { CHIP_CLS } from "../components/ui/chip-cls.js";
|
|
51
|
+
export function circledNum(i) {
|
|
52
|
+
return i >= 0 && i < 20 ? String.fromCodePoint(0x2460 + i) : String(i + 1);
|
|
53
|
+
}
|
|
54
|
+
export function colsToCheckRow(value, n) {
|
|
55
|
+
let v = value;
|
|
56
|
+
if (typeof v === 'string') {
|
|
57
|
+
try {
|
|
58
|
+
v = JSON.parse(v);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
v = {};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const o = (v && typeof v === 'object' ? v : {});
|
|
65
|
+
if (Array.isArray(o['checks'])) {
|
|
66
|
+
const arr = o['checks'];
|
|
67
|
+
return { text: String(o['text'] ?? ''), checks: Array.from({ length: n }, (_, i) => arr[i] === true) };
|
|
68
|
+
}
|
|
69
|
+
return { text: String(o['text'] ?? ''), checks: Array.from({ length: n }, (_, i) => o[`check${i}`] === true) };
|
|
70
|
+
}
|
|
71
|
+
export function checkRowToCols(row) {
|
|
72
|
+
const out = { text: row.text };
|
|
73
|
+
row.checks.forEach((c, i) => {
|
|
74
|
+
out[`check${i}`] = c;
|
|
75
|
+
});
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
export function CheckLegend({ slots }) {
|
|
79
|
+
const { t } = useTranslation();
|
|
80
|
+
if (slots.length <= 1 || !slots.some((s) => s))
|
|
81
|
+
return null;
|
|
82
|
+
const numbered = slots.length > 3;
|
|
83
|
+
return (_jsx("div", { className: "flex flex-wrap items-center gap-x-1.5 gap-y-0.5 text-xs text-muted", children: slots.map((s, i) => (_jsxs(React.Fragment, { children: [i > 0 && _jsx("span", { className: "opacity-40", children: "\u2022" }), _jsxs("span", { children: [numbered ? `${circledNum(i)} ` : '', s ? t(s) : '—'] })] }, i))) }));
|
|
84
|
+
}
|
|
85
|
+
export const checkNumbered = (slotCount) => slotCount > 3;
|
|
86
|
+
export const enterAdd = (add) => (e) => {
|
|
87
|
+
if (e.key === 'Enter') {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
add();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
export function MultiTagInput({ value, onChange, renderInput, validate, serialize, display, }) {
|
|
93
|
+
const [items, setItems] = useState(() => parseStrArray(value));
|
|
94
|
+
const [inputVal, setInputVal] = useState('');
|
|
95
|
+
const onPicker = useOnPickerSurface();
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
setItems(parseStrArray(value));
|
|
98
|
+
}, [value]);
|
|
99
|
+
function save(next) {
|
|
100
|
+
setItems(next);
|
|
101
|
+
onChange(next.length > 0 ? next : '');
|
|
102
|
+
}
|
|
103
|
+
function add() {
|
|
104
|
+
const raw = inputVal.trim();
|
|
105
|
+
if (!raw || (validate && !validate(raw)))
|
|
106
|
+
return;
|
|
107
|
+
const stored = serialize ? serialize(raw) : raw;
|
|
108
|
+
if (stored === null || items.includes(stored))
|
|
109
|
+
return;
|
|
110
|
+
save([...items, stored]);
|
|
111
|
+
setInputVal('');
|
|
112
|
+
}
|
|
113
|
+
return (_jsx(MultipleChips, { items: onPicker ? [] : items, content: (it) => (display ? display(String(it)) : String(it)), onRemove: onPicker ? undefined : (idx) => save(items.filter((_, i) => i !== idx)), editor: _jsxs(InputGroup, { className: "[&>input]:h-auto [&>input]:rounded-none [&>input]:border-0 [&>input]:bg-transparent [&>input]:shadow-none [&>input]:focus-visible:ring-0 focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50", children: [renderInput(inputVal, setInputVal, add), _jsx(InputGroupAddon, { align: "inline-end", children: _jsx(InputGroupButton, { size: "icon-xs", "aria-label": "add", onClick: add, children: _jsx(Plus, {}) }) })] }) }));
|
|
114
|
+
}
|
|
115
|
+
export function useRowIdentity(rows, onChange) {
|
|
116
|
+
const rowIds = useRef(new WeakMap());
|
|
117
|
+
const nextRowId = useRef(0);
|
|
118
|
+
const rowId = (row) => {
|
|
119
|
+
let id = rowIds.current.get(row);
|
|
120
|
+
if (id === undefined) {
|
|
121
|
+
id = ++nextRowId.current;
|
|
122
|
+
rowIds.current.set(row, id);
|
|
123
|
+
}
|
|
124
|
+
return id;
|
|
125
|
+
};
|
|
126
|
+
const updateRow = (idx, next) => {
|
|
127
|
+
const source = rows[idx];
|
|
128
|
+
if (source)
|
|
129
|
+
rowIds.current.set(next, rowId(source));
|
|
130
|
+
onChange(rows.map((r, i) => (i === idx ? next : r)));
|
|
131
|
+
};
|
|
132
|
+
const removeRow = (idx) => onChange(rows.filter((_, i) => i !== idx));
|
|
133
|
+
return { rowId, updateRow, removeRow };
|
|
134
|
+
}
|
|
135
|
+
export function useCompositeLiveGate(p) {
|
|
136
|
+
const slot = useSlotOptional();
|
|
137
|
+
const write = slot?.writable ? slot.write : undefined;
|
|
138
|
+
const live = useHybridScope() &&
|
|
139
|
+
p.mode === 'view' &&
|
|
140
|
+
!p.bare &&
|
|
141
|
+
!p.field.derived &&
|
|
142
|
+
p.field.hints['noEditControl'] !== true &&
|
|
143
|
+
Boolean(write);
|
|
144
|
+
return { live, write };
|
|
145
|
+
}
|
|
146
|
+
export function compositeRoleValue(v) {
|
|
147
|
+
return v == null || v === '' ? null : v;
|
|
148
|
+
}
|
|
149
|
+
export function hasValue(v) {
|
|
150
|
+
return v !== null && v !== undefined && v !== '';
|
|
151
|
+
}
|
|
152
|
+
export function usedElsewhere(rows, unique, key, idx) {
|
|
153
|
+
if (!unique.includes(key))
|
|
154
|
+
return [];
|
|
155
|
+
return rows
|
|
156
|
+
.filter((_, i) => i !== idx)
|
|
157
|
+
.map((r) => String(r[key] ?? ''))
|
|
158
|
+
.filter(Boolean);
|
|
159
|
+
}
|
|
160
|
+
export function shortId(id) {
|
|
161
|
+
return id.slice(0, 8) + '…';
|
|
162
|
+
}
|
|
163
|
+
export const trunc = (s) => (s.length > 60 ? s.slice(0, 57) + '…' : s);
|
|
164
|
+
export { useTranslation };
|
|
165
|
+
export function multiView(field, value, content, commit, chip) {
|
|
166
|
+
if (field.hints['multiple'] === true) {
|
|
167
|
+
const items = parseJSON(value, []);
|
|
168
|
+
return (_jsx(MultipleChips, { items: items, content: content, ...chip, onRemove: commit
|
|
169
|
+
? (idx) => {
|
|
170
|
+
const next = items.filter((_, i) => i !== idx);
|
|
171
|
+
void commit(next.length ? next : '');
|
|
172
|
+
}
|
|
173
|
+
: undefined }));
|
|
174
|
+
}
|
|
175
|
+
return hasValue(value) ? _jsx(_Fragment, { children: content(value) }) : _jsx(Dash, {});
|
|
176
|
+
}
|
|
177
|
+
export function orDash(value, render, isEmpty = (v) => !hasValue(v)) {
|
|
178
|
+
return isEmpty(value) ? _jsx(Dash, {}) : _jsx(_Fragment, { children: render(value) });
|
|
179
|
+
}
|
|
180
|
+
export function firstCoverKey(fm) {
|
|
181
|
+
return Object.keys(fm).find((k) => fm[k]?.kind === 'cover');
|
|
182
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import { type LayoutNode } from '@coffer-org/sdk/fields';
|
|
3
|
+
import type { FieldMode } from '../types.ts';
|
|
4
|
+
export type SlotRender = (handle: SlotHandle, defaultDisplay: ReactNode) => ReactNode;
|
|
5
|
+
export interface SlotHandle {
|
|
6
|
+
value: unknown;
|
|
7
|
+
field: LayoutNode;
|
|
8
|
+
write: (next: unknown) => Promise<string | undefined>;
|
|
9
|
+
error: string | undefined;
|
|
10
|
+
busy: boolean;
|
|
11
|
+
writable: boolean;
|
|
12
|
+
hidden: boolean;
|
|
13
|
+
mode: FieldMode;
|
|
14
|
+
}
|
|
15
|
+
export declare function useSlot(name?: string): SlotHandle;
|
|
16
|
+
export declare function useSlotOptional(name?: string): SlotHandle | null;
|
|
17
|
+
export declare function useSlotRender(): SlotRender | undefined;
|
|
18
|
+
export declare function useSlots(): {
|
|
19
|
+
name: string;
|
|
20
|
+
handle: SlotHandle;
|
|
21
|
+
}[];
|
|
22
|
+
export declare function SlotProvider(props: {
|
|
23
|
+
field: LayoutNode;
|
|
24
|
+
value: unknown;
|
|
25
|
+
commit?: (next: unknown) => void | Promise<string | undefined>;
|
|
26
|
+
isHidden?: (key: string) => boolean;
|
|
27
|
+
mode?: FieldMode;
|
|
28
|
+
render?: SlotRender;
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
}): ReactElement;
|
|
31
|
+
export declare function SlotScope({ name, children }: {
|
|
32
|
+
name: string;
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
}): ReactElement | null;
|
|
35
|
+
export declare function Slot({ name, render, ownLabel, }: {
|
|
36
|
+
name: string;
|
|
37
|
+
render?: SlotRender;
|
|
38
|
+
ownLabel?: boolean;
|
|
39
|
+
}): ReactElement | null;
|
|
40
|
+
export declare function SlotEditor({ name }: {
|
|
41
|
+
name: string;
|
|
42
|
+
}): ReactElement | null;
|
|
43
|
+
export declare function SlotValue({ name }: {
|
|
44
|
+
name: string;
|
|
45
|
+
}): ReactElement | null;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { hasChildren, isNamedField, wrapKey, } from '@coffer-org/sdk/fields';
|
|
5
|
+
import { elementKind, resolveRenderer } from "../registry.js";
|
|
6
|
+
import { FieldAddressProvider, useFieldAddress } from "./field-address.js";
|
|
7
|
+
import { makeIssueText } from "./field-errors.js";
|
|
8
|
+
import { makeWriter, notWritable } from "./write.js";
|
|
9
|
+
const SlotContext = createContext(null);
|
|
10
|
+
function slotChildren(field) {
|
|
11
|
+
if (hasChildren(field))
|
|
12
|
+
return field.fields;
|
|
13
|
+
const parts = field.type?.parts;
|
|
14
|
+
if (parts?.length)
|
|
15
|
+
return parts.map((p) => ({ ...wrapKey(p.role, {}, p.meta), name: p.role }));
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
function findChild(field, name) {
|
|
19
|
+
return slotChildren(field).find((c) => c.name === name);
|
|
20
|
+
}
|
|
21
|
+
function asRecord(v) {
|
|
22
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v) ? v : {};
|
|
23
|
+
}
|
|
24
|
+
function ownHandle(ctx) {
|
|
25
|
+
return {
|
|
26
|
+
value: ctx.value,
|
|
27
|
+
field: ctx.field,
|
|
28
|
+
write: ctx.ownWrite,
|
|
29
|
+
error: ctx.ownState.error,
|
|
30
|
+
busy: ctx.ownState.busy,
|
|
31
|
+
writable: ctx.writable,
|
|
32
|
+
hidden: ctx.field.key !== undefined && (ctx.isHidden?.(ctx.field.key) ?? false),
|
|
33
|
+
mode: ctx.mode,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function childHandle(ctx, name) {
|
|
37
|
+
const child = findChild(ctx.field, name);
|
|
38
|
+
if (!child)
|
|
39
|
+
throw new Error(`useSlot: '${name}' is not a field this node declares`);
|
|
40
|
+
const state = ctx.childStates[name] ?? { busy: false };
|
|
41
|
+
return {
|
|
42
|
+
value: child.key !== undefined ? asRecord(ctx.value)[child.key] : ctx.value,
|
|
43
|
+
field: child,
|
|
44
|
+
write: ctx.childWrite(child, name),
|
|
45
|
+
error: state.error,
|
|
46
|
+
busy: state.busy,
|
|
47
|
+
writable: child.key !== undefined && ctx.writable,
|
|
48
|
+
hidden: child.key !== undefined && (ctx.isHidden?.(child.key) ?? false),
|
|
49
|
+
mode: ctx.mode,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function useSlot(name) {
|
|
53
|
+
const ctx = useContext(SlotContext);
|
|
54
|
+
if (!ctx)
|
|
55
|
+
throw new Error('useSlot: no SlotProvider found in the tree');
|
|
56
|
+
return name === undefined ? ownHandle(ctx) : childHandle(ctx, name);
|
|
57
|
+
}
|
|
58
|
+
export function useSlotOptional(name) {
|
|
59
|
+
const ctx = useContext(SlotContext);
|
|
60
|
+
if (!ctx)
|
|
61
|
+
return null;
|
|
62
|
+
return name === undefined ? ownHandle(ctx) : childHandle(ctx, name);
|
|
63
|
+
}
|
|
64
|
+
export function useSlotRender() {
|
|
65
|
+
return useContext(SlotContext)?.render ?? undefined;
|
|
66
|
+
}
|
|
67
|
+
export function useSlots() {
|
|
68
|
+
const ctx = useContext(SlotContext);
|
|
69
|
+
if (!ctx)
|
|
70
|
+
throw new Error('useSlots: no SlotProvider found in the tree');
|
|
71
|
+
const named = [];
|
|
72
|
+
for (const c of slotChildren(ctx.field)) {
|
|
73
|
+
if (c.name === undefined)
|
|
74
|
+
continue;
|
|
75
|
+
named.push({ name: c.name, handle: childHandle(ctx, c.name) });
|
|
76
|
+
}
|
|
77
|
+
return named;
|
|
78
|
+
}
|
|
79
|
+
export function SlotProvider(props) {
|
|
80
|
+
const { field, value, commit, isHidden, mode = 'view', render, children } = props;
|
|
81
|
+
const writable = commit !== undefined;
|
|
82
|
+
const [ownState, setOwnState] = useState({ busy: false });
|
|
83
|
+
const [childStates, setChildStates] = useState({});
|
|
84
|
+
const { t } = useTranslation();
|
|
85
|
+
const issueText = useMemo(() => makeIssueText(t), [t]);
|
|
86
|
+
const fallback = useMemo(() => notWritable(issueText), [issueText]);
|
|
87
|
+
const lastValueProp = useRef(value);
|
|
88
|
+
const valueRef = useRef(value);
|
|
89
|
+
if (lastValueProp.current !== value) {
|
|
90
|
+
lastValueProp.current = value;
|
|
91
|
+
valueRef.current = value;
|
|
92
|
+
}
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
setOwnState({ busy: false });
|
|
95
|
+
setChildStates({});
|
|
96
|
+
}, [field, value]);
|
|
97
|
+
const ownWrite = useCallback(async (next) => {
|
|
98
|
+
setOwnState({ busy: true });
|
|
99
|
+
const err = await makeWriter(field.type, commit ?? fallback, issueText)(next);
|
|
100
|
+
setOwnState({ busy: false, error: err });
|
|
101
|
+
return err;
|
|
102
|
+
}, [field, commit, fallback, issueText]);
|
|
103
|
+
const childWrite = useCallback((child, name) => async (next) => {
|
|
104
|
+
setChildStates((m) => ({ ...m, [name]: { busy: true } }));
|
|
105
|
+
const key = child.key;
|
|
106
|
+
const childCommit = key === undefined || commit === undefined
|
|
107
|
+
? fallback
|
|
108
|
+
: (v) => {
|
|
109
|
+
const merged = { ...asRecord(valueRef.current), [key]: v };
|
|
110
|
+
valueRef.current = merged;
|
|
111
|
+
return commit(merged);
|
|
112
|
+
};
|
|
113
|
+
const err = await makeWriter(child.type, childCommit, issueText)(next);
|
|
114
|
+
setChildStates((m) => ({ ...m, [name]: { busy: false, error: err } }));
|
|
115
|
+
return err;
|
|
116
|
+
}, [commit, fallback, issueText]);
|
|
117
|
+
const ctx = useMemo(() => ({ field, value, ownState, ownWrite, childStates, childWrite, writable, commit, isHidden, mode, render }), [field, value, ownState, ownWrite, childStates, childWrite, writable, commit, isHidden, mode, render]);
|
|
118
|
+
return _jsx(SlotContext.Provider, { value: ctx, children: children });
|
|
119
|
+
}
|
|
120
|
+
function useResolvedChild(name) {
|
|
121
|
+
const ctx = useContext(SlotContext);
|
|
122
|
+
if (!ctx)
|
|
123
|
+
throw new Error('useSlot: no SlotProvider found in the tree');
|
|
124
|
+
const handle = childHandle(ctx, name);
|
|
125
|
+
const el = handle.field;
|
|
126
|
+
const isField = isNamedField(el);
|
|
127
|
+
const r = resolveRenderer({ kind: elementKind(el) });
|
|
128
|
+
const { recordCtx } = useFieldAddress();
|
|
129
|
+
const commit = el.key !== undefined ? (handle.writable ? handle.write : undefined) : ctx.commit;
|
|
130
|
+
const { t } = useTranslation();
|
|
131
|
+
return {
|
|
132
|
+
handle,
|
|
133
|
+
el,
|
|
134
|
+
isField,
|
|
135
|
+
r,
|
|
136
|
+
recordCtx: recordCtx ?? { library: '', shelf: '', recordId: null },
|
|
137
|
+
commit,
|
|
138
|
+
t,
|
|
139
|
+
isHidden: ctx.isHidden,
|
|
140
|
+
mode: ctx.mode,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function DefaultChildren() {
|
|
144
|
+
return (_jsx(_Fragment, { children: useSlots().map((s) => (_jsx(Slot, { name: s.name }, s.name))) }));
|
|
145
|
+
}
|
|
146
|
+
function childProps(base, extra) {
|
|
147
|
+
const { handle, el, isField, recordCtx, t, mode } = base;
|
|
148
|
+
const meta = isField ? el.type : undefined;
|
|
149
|
+
const labelKey = el.label ?? meta?.label;
|
|
150
|
+
return {
|
|
151
|
+
el,
|
|
152
|
+
field: meta,
|
|
153
|
+
fieldKey: el.key,
|
|
154
|
+
value: handle.value,
|
|
155
|
+
label: labelKey ? t(labelKey) : undefined,
|
|
156
|
+
error: handle.error,
|
|
157
|
+
mode,
|
|
158
|
+
recordCtx,
|
|
159
|
+
children: hasChildren(el) ? _jsx(DefaultChildren, {}) : undefined,
|
|
160
|
+
...extra,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function ChildFrame({ resolved, extra, render, ownLabel, }) {
|
|
164
|
+
const { r, handle, el, recordCtx, commit, isHidden, mode } = resolved;
|
|
165
|
+
if (!r || handle.hidden)
|
|
166
|
+
return null;
|
|
167
|
+
const effectiveMode = extra.mode ?? mode;
|
|
168
|
+
const labelExtra = ownLabel && effectiveMode === 'view' ? { ...extra, label: undefined } : extra;
|
|
169
|
+
return (_jsx(FieldAddressProvider, { fieldKey: el.key, recordCtx: recordCtx, children: _jsx(SlotProvider, { field: el, value: handle.value, commit: commit, isHidden: isHidden, mode: effectiveMode, render: render, children: _jsx(r.Page, { ...childProps(resolved, labelExtra) }) }) }));
|
|
170
|
+
}
|
|
171
|
+
export function SlotScope({ name, children }) {
|
|
172
|
+
const { handle, el, recordCtx, commit, isHidden, mode } = useResolvedChild(name);
|
|
173
|
+
if (handle.hidden)
|
|
174
|
+
return null;
|
|
175
|
+
return (_jsx(FieldAddressProvider, { fieldKey: el.key, recordCtx: recordCtx, children: _jsx(SlotProvider, { field: el, value: handle.value, commit: commit, isHidden: isHidden, mode: mode, children: children }) }));
|
|
176
|
+
}
|
|
177
|
+
export function Slot({ name, render, ownLabel, }) {
|
|
178
|
+
return _jsx(ChildFrame, { resolved: useResolvedChild(name), extra: {}, render: render, ownLabel: ownLabel });
|
|
179
|
+
}
|
|
180
|
+
export function SlotEditor({ name }) {
|
|
181
|
+
return _jsx(ChildFrame, { resolved: useResolvedChild(name), extra: { mode: 'edit', bare: true } });
|
|
182
|
+
}
|
|
183
|
+
export function SlotValue({ name }) {
|
|
184
|
+
const resolved = useResolvedChild(name);
|
|
185
|
+
const { handle, el, isField, r } = resolved;
|
|
186
|
+
if (isField)
|
|
187
|
+
return _jsx(ChildFrame, { resolved: resolved, extra: { mode: 'view', bare: true } });
|
|
188
|
+
if (handle.hidden)
|
|
189
|
+
return null;
|
|
190
|
+
const Value = r?.Value;
|
|
191
|
+
if (!Value)
|
|
192
|
+
return _jsx("span", { children: String(handle.value ?? '') });
|
|
193
|
+
return _jsx(Value, { field: undefined, value: handle.value, el: el });
|
|
194
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FieldMeta } from '@coffer-org/sdk/fields';
|
|
2
|
+
import type { FieldKeyCtx } from '../types.ts';
|
|
3
|
+
export declare function sortByField(rows: Record<string, unknown>[], sortKey: string, dir: 'asc' | 'desc', fdef: FieldMeta | undefined, ctx: FieldKeyCtx): Record<string, unknown>[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cellValue, magnitudeSub, subValue } from '@coffer-org/sdk/shelf';
|
|
2
|
+
import { fieldKeys } from "./dispatch.js";
|
|
3
|
+
function magnitudeOrder(fdef, sortKey) {
|
|
4
|
+
const sub = magnitudeSub(fdef);
|
|
5
|
+
if (sub === undefined) {
|
|
6
|
+
console.warn(`[sort] '${sortKey}' has no column to sort by; the order is unchanged.`);
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const col = fdef.columns?.[sub];
|
|
10
|
+
return { sub, numeric: col === 'real' || col === 'integer' };
|
|
11
|
+
}
|
|
12
|
+
export function sortByField(rows, sortKey, dir, fdef, ctx) {
|
|
13
|
+
const order = fdef?.columns ? magnitudeOrder(fdef, sortKey) : undefined;
|
|
14
|
+
const magnitudeOf = (row) => subValue(cellValue(fdef, sortKey, row), order.sub);
|
|
15
|
+
if (order?.numeric) {
|
|
16
|
+
const numOf = (row) => {
|
|
17
|
+
const raw = magnitudeOf(row);
|
|
18
|
+
const n = raw == null || raw === '' ? NaN : Number(raw);
|
|
19
|
+
return Number.isFinite(n) ? n : -Infinity;
|
|
20
|
+
};
|
|
21
|
+
return [...rows].sort((a, b) => {
|
|
22
|
+
const cmp = numOf(a) - numOf(b);
|
|
23
|
+
return dir === 'asc' ? cmp : -cmp;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const keyOf = (row) => {
|
|
27
|
+
if (!fdef)
|
|
28
|
+
return String(row[sortKey] ?? '');
|
|
29
|
+
if (order)
|
|
30
|
+
return String(magnitudeOf(row) ?? '');
|
|
31
|
+
const k = fieldKeys(fdef, cellValue(fdef, sortKey, row), ctx)[0];
|
|
32
|
+
return k?.sort ?? k?.value ?? '';
|
|
33
|
+
};
|
|
34
|
+
return [...rows].sort((a, b) => {
|
|
35
|
+
const cmp = keyOf(a).localeCompare(keyOf(b), undefined, { numeric: true, sensitivity: 'base' });
|
|
36
|
+
return dir === 'asc' ? cmp : -cmp;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ContainerNode, ValueNode } from '@coffer-org/sdk/fields';
|
|
2
|
+
export declare function partField(el: ContainerNode, key: string | undefined): ValueNode | undefined;
|
|
3
|
+
export type Row = Record<string, unknown>;
|
|
4
|
+
export interface CollectionRowsApi {
|
|
5
|
+
rowId: (row: Row) => number;
|
|
6
|
+
updateRow: (idx: number, key: string, v: unknown) => void;
|
|
7
|
+
removeRow: (idx: number) => void;
|
|
8
|
+
addRow: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function useCollectionRows(rows: Row[], onChange: (next: Row[]) => void): CollectionRowsApi;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { isNamedField } from '@coffer-org/sdk/fields';
|
|
2
|
+
import { useRowIdentity } from "./shared.js";
|
|
3
|
+
export function partField(el, key) {
|
|
4
|
+
return el.fields.find((f) => isNamedField(f) && f.key === key);
|
|
5
|
+
}
|
|
6
|
+
export function useCollectionRows(rows, onChange) {
|
|
7
|
+
const { rowId, updateRow: replaceRow, removeRow } = useRowIdentity(rows, onChange);
|
|
8
|
+
const updateRow = (idx, key, v) => replaceRow(idx, { ...rows[idx], [key]: v });
|
|
9
|
+
const addRow = () => onChange([...rows, {}]);
|
|
10
|
+
return { rowId, updateRow, removeRow, addRow };
|
|
11
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FieldMeta } from '@coffer-org/sdk/fields';
|
|
2
|
+
export declare function isChecked(value: unknown): boolean;
|
|
3
|
+
export declare function normalizeValue(field: Pick<FieldMeta, 'prim'>, value: unknown): unknown;
|
|
4
|
+
export declare function commitDraft(next: unknown, onChange?: (v: unknown) => void | Promise<string | undefined>): Promise<{
|
|
5
|
+
ok: boolean;
|
|
6
|
+
error: string | undefined;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function focusLeft(currentTarget: Element, related: EventTarget | null): boolean;
|
|
9
|
+
export declare function useValueEdit(value: unknown, onChange?: (v: unknown) => void | Promise<string | undefined>, normalize?: (v: unknown) => unknown): {
|
|
10
|
+
editing: boolean;
|
|
11
|
+
draft: unknown;
|
|
12
|
+
setDraft: import("react").Dispatch<unknown>;
|
|
13
|
+
busy: boolean;
|
|
14
|
+
error: string | undefined;
|
|
15
|
+
beginEdit: () => void;
|
|
16
|
+
cancel: () => void;
|
|
17
|
+
commit: (next?: unknown) => Promise<void>;
|
|
18
|
+
};
|
|
19
|
+
export declare function useFieldEdit(field: FieldMeta, value: unknown, onChange?: (v: unknown) => void | Promise<string | undefined>): {
|
|
20
|
+
editing: boolean;
|
|
21
|
+
draft: unknown;
|
|
22
|
+
setDraft: import("react").Dispatch<unknown>;
|
|
23
|
+
busy: boolean;
|
|
24
|
+
error: string | undefined;
|
|
25
|
+
beginEdit: () => void;
|
|
26
|
+
cancel: () => void;
|
|
27
|
+
commit: (next?: unknown) => Promise<void>;
|
|
28
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
export function isChecked(value) {
|
|
3
|
+
return value === 1 || value === true || value === '1' || value === 'true';
|
|
4
|
+
}
|
|
5
|
+
export function normalizeValue(field, value) {
|
|
6
|
+
if (field.prim === 'checkbox')
|
|
7
|
+
return isChecked(value);
|
|
8
|
+
return value == null ? '' : value;
|
|
9
|
+
}
|
|
10
|
+
export async function commitDraft(next, onChange) {
|
|
11
|
+
const err = await onChange?.(next);
|
|
12
|
+
const error = typeof err === 'string' ? err : undefined;
|
|
13
|
+
return { ok: !error, error };
|
|
14
|
+
}
|
|
15
|
+
export function focusLeft(currentTarget, related) {
|
|
16
|
+
return !(related instanceof Node) || !currentTarget.contains(related);
|
|
17
|
+
}
|
|
18
|
+
export function useValueEdit(value, onChange, normalize = (v) => v) {
|
|
19
|
+
const [editing, setEditing] = useState(false);
|
|
20
|
+
const norm = useRef(normalize);
|
|
21
|
+
norm.current = normalize;
|
|
22
|
+
const [draft, setDraft] = useState(() => normalize(value));
|
|
23
|
+
const [busy, setBusy] = useState(false);
|
|
24
|
+
const [error, setError] = useState(undefined);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!editing)
|
|
27
|
+
setDraft(norm.current(value));
|
|
28
|
+
}, [editing, value]);
|
|
29
|
+
const beginEdit = () => {
|
|
30
|
+
setError(undefined);
|
|
31
|
+
setEditing(true);
|
|
32
|
+
};
|
|
33
|
+
const cancel = () => {
|
|
34
|
+
setDraft(norm.current(value));
|
|
35
|
+
setError(undefined);
|
|
36
|
+
setEditing(false);
|
|
37
|
+
};
|
|
38
|
+
const commit = async (next = draft) => {
|
|
39
|
+
setBusy(true);
|
|
40
|
+
const { ok, error: err } = await commitDraft(next, onChange);
|
|
41
|
+
setBusy(false);
|
|
42
|
+
setError(err);
|
|
43
|
+
if (ok)
|
|
44
|
+
setEditing(false);
|
|
45
|
+
};
|
|
46
|
+
return { editing, draft, setDraft, busy, error, beginEdit, cancel, commit };
|
|
47
|
+
}
|
|
48
|
+
export function useFieldEdit(field, value, onChange) {
|
|
49
|
+
return useValueEdit(value, onChange, (v) => normalizeValue(field, v));
|
|
50
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FileRec } from './image.tsx';
|
|
2
|
+
export declare const MAX_UPLOAD_BYTES: number;
|
|
3
|
+
export declare function useFileUpload(): {
|
|
4
|
+
busy: boolean;
|
|
5
|
+
error: string | null;
|
|
6
|
+
progress: number;
|
|
7
|
+
upload: (file: File) => Promise<FileRec | null>;
|
|
8
|
+
retry: () => Promise<FileRec | null>;
|
|
9
|
+
canRetry: boolean;
|
|
10
|
+
};
|