@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,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Link } from 'react-router-dom';
|
|
3
|
+
import { getShelf } from "../../schema-registry.js";
|
|
4
|
+
import { OptionIconView } from "../../components/ui/option-icon.js";
|
|
5
|
+
import { recordOption } from "../../lib/record-option.js";
|
|
6
|
+
import { useRecordsByIds } from "../../lib/use-record-labels.js";
|
|
7
|
+
import { Dash, shortId } from "../shared.js";
|
|
8
|
+
import { registerRenderer } from "../../registry.js";
|
|
9
|
+
import { Slot } from "../slot.js";
|
|
10
|
+
import { BlockShell } from "./block-shell.js";
|
|
11
|
+
import { relationIds } from "./relation-source.js";
|
|
12
|
+
function PeopleRoster({ field, value }) {
|
|
13
|
+
const library = field.relation?.library ?? '';
|
|
14
|
+
const shelf = field.relation?.shelf ?? '';
|
|
15
|
+
const ids = relationIds(field, value);
|
|
16
|
+
const recordMap = useRecordsByIds(library, shelf, ids);
|
|
17
|
+
const m = getShelf(library, shelf);
|
|
18
|
+
if (!ids.length)
|
|
19
|
+
return _jsx(Dash, {});
|
|
20
|
+
return (_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1", "data-testid": "people", children: [_jsx("div", { className: "flex items-center", "data-testid": "people-avatars", children: ids.map((id, i) => {
|
|
21
|
+
const row = recordMap.get(id);
|
|
22
|
+
const { icon } = m && row ? recordOption(m, row) : { icon: undefined };
|
|
23
|
+
return (_jsx("span", { className: `rounded-full ring-2 ring-surface ${i > 0 ? '-ml-2.5' : ''}`, style: { zIndex: ids.length - i }, "data-testid": `people-avatar-${i}`, children: icon ? (_jsx(OptionIconView, { icon: icon, framed: true, size: "md" })) : (_jsx(OptionIconView, { icon: { kind: 'icon', value: m?.icon ?? 'lucide:user' }, framed: true, size: "md" })) }, id));
|
|
24
|
+
}) }), _jsx("div", { className: "flex flex-wrap items-center gap-x-1.5 text-sm", "data-testid": "people-names", children: ids.map((id) => {
|
|
25
|
+
const row = recordMap.get(id);
|
|
26
|
+
const { title } = m && row ? recordOption(m, row) : { title: shortId(id) };
|
|
27
|
+
return (_jsx(Link, { to: `/${library}/${shelf}/${id}`, className: "hover:text-accent hover:underline", children: title }, id));
|
|
28
|
+
}) })] }));
|
|
29
|
+
}
|
|
30
|
+
function PeoplePage({ el }) {
|
|
31
|
+
const g = el;
|
|
32
|
+
const sourceName = g.view?.source;
|
|
33
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: _jsx(Slot, { name: sourceName, render: (handle) => _jsx(PeopleRoster, { field: handle.field.type, value: handle.value }) }) }));
|
|
34
|
+
}
|
|
35
|
+
registerRenderer({ kinds: ['people'], layout: 'block', editor: 'custom', Page: PeoplePage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { BlockShell, findSlot, findSlots } from "./block-shell.js";
|
|
5
|
+
function PlaquePage({ el }) {
|
|
6
|
+
const g = el;
|
|
7
|
+
const titleName = g.view?.title;
|
|
8
|
+
const subtitleNames = g.view?.subtitle ?? [];
|
|
9
|
+
const slots = useSlots();
|
|
10
|
+
const title = findSlot(slots, titleName);
|
|
11
|
+
const subtitles = findSlots(slots, subtitleNames);
|
|
12
|
+
const body = (_jsxs("div", { className: "flex flex-col items-center gap-3 py-4 text-center", "data-testid": "plaque", children: [title && !title.handle.hidden && (_jsx("div", { className: "min-w-0 w-full border-b border-border pb-3", "data-testid": "plaque-item-0", children: _jsx(Slot, { name: titleName, render: (_handle, defaultDisplay) => (_jsx("div", { className: "text-base font-semibold text-text", "data-testid": "plaque-title", children: defaultDisplay })) }) })), subtitles.map((s, i) => s.handle.hidden ? null : (_jsx("div", { className: "min-w-0", "data-testid": `plaque-item-${i + 1}`, children: _jsx(Slot, { name: s.name, render: (_handle, defaultDisplay) => (_jsx("div", { className: "text-xs font-medium uppercase tracking-wider text-muted", "data-testid": "plaque-subtitle", children: defaultDisplay })) }) }, s.name)))] }));
|
|
13
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
14
|
+
}
|
|
15
|
+
registerRenderer({ kinds: ['plaque'], layout: 'block', editor: 'custom', Page: PlaquePage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { BlockShell } from "./block-shell.js";
|
|
5
|
+
const COLS = {
|
|
6
|
+
2: 'grid-cols-1 @sm:grid-cols-2',
|
|
7
|
+
3: 'grid-cols-1 @sm:grid-cols-2 @md:grid-cols-3',
|
|
8
|
+
4: 'grid-cols-1 @sm:grid-cols-2 @md:grid-cols-4',
|
|
9
|
+
};
|
|
10
|
+
function PropertiesPage({ el }) {
|
|
11
|
+
const g = el;
|
|
12
|
+
const columns = g.view?.columns ?? 2;
|
|
13
|
+
const colClass = COLS[columns] ?? COLS[2];
|
|
14
|
+
const slots = useSlots();
|
|
15
|
+
const body = (_jsx("div", { className: `@container grid gap-x-4 gap-y-3.5 p-4 rounded-xl border border-border/60 bg-muted/15 ${colClass}`, children: slots.map((s, i) => {
|
|
16
|
+
if (s.handle.hidden)
|
|
17
|
+
return null;
|
|
18
|
+
return (_jsx("div", { className: "min-w-0", "data-testid": `properties-item-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
|
|
19
|
+
}) }));
|
|
20
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
21
|
+
}
|
|
22
|
+
registerRenderer({ kinds: ['properties'], layout: 'block', editor: 'custom', Page: PropertiesPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { BlockShell } from "./block-shell.js";
|
|
5
|
+
const DROP_CAP_CLS = 'first-letter:float-left first-letter:mr-2 first-letter:text-5xl first-letter:font-bold first-letter:leading-[0.8]';
|
|
6
|
+
function ProsePage({ el }) {
|
|
7
|
+
const g = el;
|
|
8
|
+
const dropCapName = g.view?.dropCap;
|
|
9
|
+
const slots = useSlots();
|
|
10
|
+
const body = (_jsx("div", { className: "max-w-[65ch] space-y-3 font-serif text-base leading-relaxed", "data-testid": "prose", children: slots.map((s, i) => {
|
|
11
|
+
if (s.handle.hidden)
|
|
12
|
+
return null;
|
|
13
|
+
return (_jsx("div", { className: `min-w-0 ${s.name === dropCapName ? DROP_CAP_CLS : ''}`, "data-testid": `prose-item-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
|
|
14
|
+
}) }));
|
|
15
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
16
|
+
}
|
|
17
|
+
registerRenderer({ kinds: ['prose'], layout: 'block', editor: 'custom', Page: ProsePage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { registerRenderer } from "../../registry.js";
|
|
4
|
+
import { Slot, useSlots } from "../slot.js";
|
|
5
|
+
import { BlockShell, fieldLabel, findSlot, findSlots } from "./block-shell.js";
|
|
6
|
+
function ReceiptPage({ el }) {
|
|
7
|
+
const g = el;
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
const lineNames = g.view?.lines ?? [];
|
|
10
|
+
const totalName = g.view?.total;
|
|
11
|
+
const slots = useSlots();
|
|
12
|
+
const lines = findSlots(slots, lineNames);
|
|
13
|
+
const total = findSlot(slots, totalName);
|
|
14
|
+
const body = (_jsxs("div", { className: "flex flex-col", "data-testid": "receipt", children: [_jsx("div", { className: "flex flex-col divide-y divide-dashed divide-border", children: lines.map((line, i) => line.handle.hidden ? null : (_jsx("div", { className: "min-w-0 py-1.5", "data-testid": `receipt-line-${i}`, children: _jsx(Slot, { name: line.name, ownLabel: true, render: (handle, defaultDisplay) => (_jsxs("span", { className: "flex w-full min-w-0 items-baseline justify-between gap-3", "data-testid": "receipt-line", children: [_jsx("span", { className: "min-w-0 text-muted", children: fieldLabel(handle, t) }), _jsx("span", { className: "shrink-0 text-right tabular-nums", children: defaultDisplay })] })) }) }, line.name))) }), total && !total.handle.hidden && (_jsx("div", { className: "mt-2 min-w-0 border-t-2 border-border pt-2", "data-testid": "receipt-total-row", children: _jsx(Slot, { name: totalName, ownLabel: true, render: (handle, defaultDisplay) => (_jsxs("span", { className: "flex w-full min-w-0 items-baseline justify-between gap-3 text-lg font-bold", "data-testid": "receipt-total", children: [_jsx("span", { className: "min-w-0", children: fieldLabel(handle, t) }), _jsx("span", { className: "shrink-0 text-right tabular-nums", children: defaultDisplay })] })) }) }))] }));
|
|
15
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
16
|
+
}
|
|
17
|
+
registerRenderer({
|
|
18
|
+
kinds: ['receipt'],
|
|
19
|
+
layout: 'block',
|
|
20
|
+
editor: 'custom',
|
|
21
|
+
Page: ReceiptPage,
|
|
22
|
+
childWidth: 'w-full',
|
|
23
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReactNode, ReactElement } from 'react';
|
|
2
|
+
import type { ContainerNode } from '@coffer-org/sdk/fields';
|
|
3
|
+
import type { DisplayOverride } from '../display-override.tsx';
|
|
4
|
+
export type { DisplayOverride };
|
|
5
|
+
export interface BlockComponent {
|
|
6
|
+
(p: {
|
|
7
|
+
el: ContainerNode;
|
|
8
|
+
items: ReactNode[];
|
|
9
|
+
label?: string;
|
|
10
|
+
icon?: string;
|
|
11
|
+
}): ReactElement;
|
|
12
|
+
displays?: (el: ContainerNode) => readonly (DisplayOverride | undefined)[];
|
|
13
|
+
childWidth?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function registerBlock(kind: string, c: BlockComponent): void;
|
|
16
|
+
export declare function resolveBlock(kind: string | undefined): BlockComponent | undefined;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { parseJSON } from "../../lib/json.js";
|
|
2
|
+
import { hasValue } from "../shared.js";
|
|
3
|
+
export function relationIds(field, value) {
|
|
4
|
+
const multi = Boolean(field.hints['multi']);
|
|
5
|
+
return multi ? parseJSON(value, []).map(String) : hasValue(value) ? [String(value)] : [];
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { findSlot } from "./block-shell.js";
|
|
5
|
+
function RoutePage({ el }) {
|
|
6
|
+
const g = el;
|
|
7
|
+
const fromName = g.view?.from;
|
|
8
|
+
const toName = g.view?.to;
|
|
9
|
+
const departName = g.view?.depart;
|
|
10
|
+
const arriveName = g.view?.arrive;
|
|
11
|
+
const durationName = g.view?.duration;
|
|
12
|
+
const slots = useSlots();
|
|
13
|
+
const from = findSlot(slots, fromName);
|
|
14
|
+
const to = findSlot(slots, toName);
|
|
15
|
+
const depart = findSlot(slots, departName);
|
|
16
|
+
const arrive = findSlot(slots, arriveName);
|
|
17
|
+
const duration = findSlot(slots, durationName);
|
|
18
|
+
const fromVisible = from !== undefined && !from.handle.hidden;
|
|
19
|
+
const toVisible = to !== undefined && !to.handle.hidden;
|
|
20
|
+
const durationVisible = duration !== undefined && !duration.handle.hidden;
|
|
21
|
+
return (_jsxs("div", { className: "flex items-start gap-3", "data-testid": "route", children: [fromVisible && (_jsxs("div", { className: "flex min-w-0 flex-1 flex-col items-start gap-1", "data-testid": "route-from", children: [_jsx(Slot, { name: fromName, render: (_handle, defaultDisplay) => (_jsx("span", { className: "text-xl font-bold", "data-testid": "route-endpoint-0", children: defaultDisplay })) }), depart && !depart.handle.hidden && (_jsx("span", { className: "min-w-0 text-xs text-muted", "data-testid": "route-depart", children: _jsx(Slot, { name: departName }) }))] })), (fromVisible || toVisible || durationVisible) && (_jsxs("div", { className: "relative flex min-w-[3rem] flex-1 shrink-0 flex-col items-center justify-center self-stretch", "data-testid": "route-axis", children: [_jsx("span", { className: "absolute inset-x-0 top-1/2 border-t border-dashed border-border", "aria-hidden": true }), durationVisible && (_jsx("span", { className: "relative z-10 bg-surface px-1.5 text-xs font-medium text-muted", "data-testid": "route-duration", children: _jsx(Slot, { name: durationName }) }))] })), toVisible && (_jsxs("div", { className: "flex min-w-0 flex-1 flex-col items-end gap-1 text-right", "data-testid": "route-to", children: [_jsx(Slot, { name: toName, render: (_handle, defaultDisplay) => (_jsx("span", { className: "text-xl font-bold", "data-testid": "route-endpoint-1", children: defaultDisplay })) }), arrive && !arrive.handle.hidden && (_jsx("span", { className: "min-w-0 text-xs text-muted", "data-testid": "route-arrive", children: _jsx(Slot, { name: arriveName }) }))] }))] }));
|
|
22
|
+
}
|
|
23
|
+
registerRenderer({ kinds: ['route'], layout: 'block', editor: 'custom', Page: RoutePage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { BlockShell, findSlot } from "./block-shell.js";
|
|
5
|
+
function ScorePage({ el }) {
|
|
6
|
+
const g = el;
|
|
7
|
+
const sourceName = g.view?.source;
|
|
8
|
+
const verdictName = g.view?.verdict;
|
|
9
|
+
const max = g.view?.max;
|
|
10
|
+
const slots = useSlots();
|
|
11
|
+
const verdict = findSlot(slots, verdictName);
|
|
12
|
+
const body = (_jsxs("div", { className: "flex flex-wrap items-baseline gap-3", "data-testid": "score", children: [_jsx("div", { className: "min-w-0", children: _jsx(Slot, { name: sourceName, render: (_handle, defaultDisplay) => (_jsxs("span", { className: "inline-flex items-baseline gap-0.5 text-3xl font-bold tabular-nums", "data-testid": "score-figure", children: [defaultDisplay, max != null && _jsxs("span", { className: "text-lg font-normal text-muted", children: ["/", max] })] })) }) }), verdict && !verdict.handle.hidden && (_jsx("div", { className: "min-w-0 text-muted", "data-testid": "score-verdict", children: _jsx(Slot, { name: verdictName }) }))] }));
|
|
13
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
14
|
+
}
|
|
15
|
+
registerRenderer({ kinds: ['score'], layout: 'block', editor: 'custom', Page: ScorePage });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface SeriesPoint {
|
|
2
|
+
value: number;
|
|
3
|
+
t?: number;
|
|
4
|
+
row?: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
export interface SeriesBox {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
pad?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface Sparkline {
|
|
12
|
+
path: string;
|
|
13
|
+
coords: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
}[];
|
|
17
|
+
min: number | undefined;
|
|
18
|
+
max: number | undefined;
|
|
19
|
+
}
|
|
20
|
+
export declare function parseSeriesDate(v: unknown): number | undefined;
|
|
21
|
+
export declare function toSeries(rows: Record<string, unknown>[], value: string, date: string): SeriesPoint[];
|
|
22
|
+
export declare function sparkline(input: SeriesPoint[], box: SeriesBox): Sparkline;
|
|
23
|
+
export declare function seriesDelta(points: SeriesPoint[]): number | undefined;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export function parseSeriesDate(v) {
|
|
2
|
+
const s = typeof v === 'string' ? v : typeof v === 'number' ? String(v) : '';
|
|
3
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?/.exec(s);
|
|
4
|
+
if (!m)
|
|
5
|
+
return undefined;
|
|
6
|
+
return Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4] ?? 0), Number(m[5] ?? 0), Number(m[6] ?? 0));
|
|
7
|
+
}
|
|
8
|
+
function toNumber(v) {
|
|
9
|
+
if (typeof v === 'number')
|
|
10
|
+
return Number.isFinite(v) ? v : undefined;
|
|
11
|
+
if (typeof v === 'string' && v.trim() !== '') {
|
|
12
|
+
const n = Number(v);
|
|
13
|
+
return Number.isFinite(n) ? n : undefined;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
export function toSeries(rows, value, date) {
|
|
18
|
+
const points = [];
|
|
19
|
+
for (const row of rows) {
|
|
20
|
+
const v = toNumber(row[value]);
|
|
21
|
+
if (v === undefined)
|
|
22
|
+
continue;
|
|
23
|
+
points.push({ value: v, t: parseSeriesDate(row[date]), row });
|
|
24
|
+
}
|
|
25
|
+
if (points.length > 1 && points.every((p) => p.t !== undefined)) {
|
|
26
|
+
points.sort((a, b) => a.t - b.t);
|
|
27
|
+
}
|
|
28
|
+
return points;
|
|
29
|
+
}
|
|
30
|
+
function round(n) {
|
|
31
|
+
return Math.round(n * 100) / 100;
|
|
32
|
+
}
|
|
33
|
+
export function sparkline(input, box) {
|
|
34
|
+
const pad = box.pad ?? 0;
|
|
35
|
+
const innerW = Math.max(0, box.width - pad * 2);
|
|
36
|
+
const innerH = Math.max(0, box.height - pad * 2);
|
|
37
|
+
const points = input.filter((p) => Number.isFinite(p.value));
|
|
38
|
+
if (points.length === 0)
|
|
39
|
+
return { path: '', coords: [], min: undefined, max: undefined };
|
|
40
|
+
const values = points.map((p) => p.value);
|
|
41
|
+
const min = Math.min(...values);
|
|
42
|
+
const max = Math.max(...values);
|
|
43
|
+
const range = max - min;
|
|
44
|
+
const coords = points.map((p, i) => {
|
|
45
|
+
const fx = points.length > 1 ? i / (points.length - 1) : 0;
|
|
46
|
+
const fy = range > 0 ? (p.value - min) / range : 0.5;
|
|
47
|
+
return { x: round(pad + fx * innerW), y: round(pad + (1 - fy) * innerH) };
|
|
48
|
+
});
|
|
49
|
+
const move = coords.map((c, i) => `${i === 0 ? 'M' : 'L'} ${c.x} ${c.y}`).join(' ');
|
|
50
|
+
const path = coords.length === 1 ? `${move} L ${coords[0].x} ${coords[0].y}` : move;
|
|
51
|
+
return { path, coords, min, max };
|
|
52
|
+
}
|
|
53
|
+
export function seriesDelta(points) {
|
|
54
|
+
if (points.length < 2)
|
|
55
|
+
return undefined;
|
|
56
|
+
return points[points.length - 1].value - points[points.length - 2].value;
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Icon } from "../../components/Icon.js";
|
|
4
|
+
import { registerRenderer } from "../../registry.js";
|
|
5
|
+
import { Slot, useSlots } from "../slot.js";
|
|
6
|
+
import { fieldLabel, findSlot, findSlots } from "./block-shell.js";
|
|
7
|
+
function SpecimenPage({ el }) {
|
|
8
|
+
const g = el;
|
|
9
|
+
const { t } = useTranslation();
|
|
10
|
+
const titleName = g.view?.title;
|
|
11
|
+
const subtitleName = g.view?.subtitle;
|
|
12
|
+
const conditionNames = g.view?.conditions ?? [];
|
|
13
|
+
const slots = useSlots();
|
|
14
|
+
const subtitle = findSlot(slots, subtitleName);
|
|
15
|
+
const conditions = findSlots(slots, conditionNames);
|
|
16
|
+
const visibleConditions = conditions.filter((c) => !c.handle.hidden);
|
|
17
|
+
return (_jsxs("div", { className: "space-y-2", "data-testid": "specimen", children: [_jsx("div", { className: "min-w-0", children: _jsx(Slot, { name: titleName, render: (_handle, defaultDisplay) => (_jsx("span", { className: "text-xl font-medium italic", "data-testid": "specimen-title", children: defaultDisplay })) }) }), subtitle && !subtitle.handle.hidden && (_jsx("div", { className: "min-w-0 text-sm text-muted", "data-testid": "specimen-subtitle", children: _jsx(Slot, { name: subtitleName }) })), visibleConditions.length > 0 && (_jsx("div", { className: "flex flex-col gap-1", "data-testid": "specimen-conditions", children: conditions.map((c, i) => c.handle.hidden ? null : (_jsx("div", { "data-testid": `specimen-condition-${i}`, children: _jsx(Slot, { name: c.name, ownLabel: true, render: (handle, defaultDisplay) => (_jsxs("span", { className: "inline-flex min-w-0 items-baseline gap-1.5", children: [_jsx(Icon, { name: "lucide:circle", size: 8, className: "relative top-[0.15em] shrink-0 fill-current text-muted" }), _jsx("span", { className: "shrink-0 text-muted", children: fieldLabel(handle, t) }), _jsx("span", { className: "min-w-0 font-medium text-text", children: defaultDisplay })] })) }) }, c.name))) }))] }));
|
|
18
|
+
}
|
|
19
|
+
registerRenderer({ kinds: ['specimen'], layout: 'block', editor: 'custom', Page: SpecimenPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot } from "../slot.js";
|
|
4
|
+
registerRenderer({
|
|
5
|
+
kinds: ['split'],
|
|
6
|
+
layout: 'block',
|
|
7
|
+
editor: 'custom',
|
|
8
|
+
Page: ({ el }) => {
|
|
9
|
+
const g = el;
|
|
10
|
+
const [a, b] = g.view?.ratio ?? [1, 1];
|
|
11
|
+
const first = g.view?.first ?? [];
|
|
12
|
+
const second = g.view?.second ?? [];
|
|
13
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-4 sm:[grid-template-columns:var(--split)]", style: { '--split': `${a}fr ${b}fr` }, children: [_jsx("div", { className: "min-w-0 space-y-3", children: first.map((n) => (_jsx(Slot, { name: n }, n))) }), _jsx("div", { className: "min-w-0 space-y-3", children: second.map((n) => (_jsx(Slot, { name: n }, n))) })] }));
|
|
14
|
+
},
|
|
15
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { findSlot, findSlots } from "./block-shell.js";
|
|
5
|
+
function SpreadPage({ el }) {
|
|
6
|
+
const g = el;
|
|
7
|
+
const visualName = g.view?.visual;
|
|
8
|
+
const quoteName = g.view?.quote;
|
|
9
|
+
const factNames = g.view?.facts ?? [];
|
|
10
|
+
const slots = useSlots();
|
|
11
|
+
const quote = findSlot(slots, quoteName);
|
|
12
|
+
const facts = findSlots(slots, factNames);
|
|
13
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]", children: [_jsx("div", { className: "min-w-0", children: _jsx(Slot, { name: visualName }) }), _jsxs("div", { className: "flex min-w-0 flex-col gap-3", children: [quote && !quote.handle.hidden && (_jsx("blockquote", { className: "border-l-2 border-accent pl-3 italic text-muted", children: _jsx(Slot, { name: quoteName }) })), facts.map((f) => (_jsx(Slot, { name: f.name }, f.name)))] })] }));
|
|
14
|
+
}
|
|
15
|
+
registerRenderer({ kinds: ['spread'], layout: 'block', editor: 'custom', Page: SpreadPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { registerRenderer } from "../../registry.js";
|
|
3
|
+
import { Slot, useSlots } from "../slot.js";
|
|
4
|
+
import { BlockShell } from "./block-shell.js";
|
|
5
|
+
function StackPage({ el }) {
|
|
6
|
+
const g = el;
|
|
7
|
+
const slots = useSlots();
|
|
8
|
+
const body = (_jsx("div", { className: "flex flex-col gap-3 w-full", children: slots.map((s, i) => {
|
|
9
|
+
if (s.handle.hidden)
|
|
10
|
+
return null;
|
|
11
|
+
return (_jsx("div", { className: "w-full min-w-0", "data-testid": `stack-item-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
|
|
12
|
+
}) }));
|
|
13
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
14
|
+
}
|
|
15
|
+
registerRenderer({ kinds: ['stack'], layout: 'block', editor: 'custom', Page: StackPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { registerRenderer } from "../../registry.js";
|
|
4
|
+
import { Slot, useSlots } from "../slot.js";
|
|
5
|
+
import { BlockShell, fieldLabel } from "./block-shell.js";
|
|
6
|
+
function StatsPage({ el }) {
|
|
7
|
+
const g = el;
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
const slots = useSlots();
|
|
10
|
+
const body = (_jsx("div", { className: "flex flex-wrap gap-x-8 gap-y-4 sm:flex-nowrap sm:divide-x sm:divide-border py-1", "data-testid": "stats", children: slots.map((s, i) => {
|
|
11
|
+
if (s.handle.hidden)
|
|
12
|
+
return null;
|
|
13
|
+
return (_jsx("div", { className: "min-w-0 flex-1 sm:px-5 sm:first:pl-0", "data-testid": `stats-item-${i}`, children: _jsx(Slot, { name: s.name, ownLabel: true, render: (handle, defaultDisplay) => (_jsxs("span", { className: "flex min-w-0 flex-col items-start gap-0.5 py-1", "data-testid": "stats-figure", children: [_jsx("span", { className: "min-w-0 text-2xl font-semibold tabular-nums text-text", children: defaultDisplay }), _jsx("span", { className: "min-w-0 text-[11px] font-medium uppercase tracking-wider text-muted", children: fieldLabel(handle, t) })] })) }) }, s.name));
|
|
14
|
+
}) }));
|
|
15
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
16
|
+
}
|
|
17
|
+
registerRenderer({
|
|
18
|
+
kinds: ['stats'],
|
|
19
|
+
layout: 'block',
|
|
20
|
+
editor: 'custom',
|
|
21
|
+
Page: StatsPage,
|
|
22
|
+
childWidth: 'w-full',
|
|
23
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { optionChipProps } from "../option-value.js";
|
|
3
|
+
import { registerRenderer } from "../../registry.js";
|
|
4
|
+
import { Slot, useSlots } from "../slot.js";
|
|
5
|
+
import { BlockShell, findSlot } from "./block-shell.js";
|
|
6
|
+
function StatusPage({ el }) {
|
|
7
|
+
const g = el;
|
|
8
|
+
const sourceName = g.view?.source;
|
|
9
|
+
const sinceName = g.view?.since;
|
|
10
|
+
const slots = useSlots();
|
|
11
|
+
const since = findSlot(slots, sinceName);
|
|
12
|
+
const body = (_jsxs("div", { className: "flex flex-wrap items-center gap-3", "data-testid": "status", children: [_jsx("div", { className: "min-w-0", children: _jsx(Slot, { name: sourceName, render: (handle, defaultDisplay) => {
|
|
13
|
+
const { chipClassName, chipStyle } = optionChipProps(handle.field.type);
|
|
14
|
+
return (_jsx("span", { className: `inline-flex min-w-0 items-center gap-2 rounded-lg border px-3 py-1.5 font-medium ${chipClassName ? chipClassName() : ''}`, style: chipStyle ? chipStyle(handle.value) : undefined, "data-testid": "status-banner", children: defaultDisplay }));
|
|
15
|
+
} }) }), since && !since.handle.hidden && (_jsx("div", { className: "min-w-0 text-sm text-muted", "data-testid": "status-since", children: _jsx(Slot, { name: sinceName }) }))] }));
|
|
16
|
+
return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
|
|
17
|
+
}
|
|
18
|
+
registerRenderer({ kinds: ['status'], layout: 'block', editor: 'custom', Page: StatusPage });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContainerNode, ValueNode } from '@coffer-org/sdk/fields';
|
|
2
|
+
import type { TableAggregate } from '@coffer-org/sdk/blocks';
|
|
3
|
+
import { type Row } from '../use-collection-rows.ts';
|
|
4
|
+
export type { Row };
|
|
5
|
+
export type Col = ValueNode & {
|
|
6
|
+
key: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function columnsOf(el: ContainerNode): Col[];
|
|
9
|
+
export declare function warnUnknownKey(el: ContainerNode, option: string, key: string): void;
|
|
10
|
+
export declare function aggregate(kind: TableAggregate, f: Col, rows: Row[]): number | undefined;
|
|
11
|
+
export declare function totalValue(f: Col, rows: Row[], n: number): unknown;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { isNamedField, isStorageGroup } from '@coffer-org/sdk/fields';
|
|
5
|
+
import { cellValue, magnitudeSub, subValue, withSubValue } from '@coffer-org/sdk/shelf';
|
|
6
|
+
import { registerRenderer } from "../../registry.js";
|
|
7
|
+
import { Icon } from "../../components/Icon.js";
|
|
8
|
+
import { AddBtn } from "../shared.js";
|
|
9
|
+
import { RemoveBtn } from "../multiple-chips.js";
|
|
10
|
+
import { groupScopeClass, useGroupDepth, GroupDepthProvider } from "../chrome.js";
|
|
11
|
+
import { useGroupEdit } from "../widgets/group-edit.js";
|
|
12
|
+
import { useHybridScope } from "../hybrid-scope.js";
|
|
13
|
+
import { FieldSlot, fieldKeys } from "../dispatch.js";
|
|
14
|
+
import { useFieldErrors } from "../field-errors.js";
|
|
15
|
+
import { sortByField } from "../sort-rows.js";
|
|
16
|
+
import { useCollectionRows } from "../use-collection-rows.js";
|
|
17
|
+
import { useSlot } from "../slot.js";
|
|
18
|
+
const NO_VALUE = ' __table_none__';
|
|
19
|
+
export function columnsOf(el) {
|
|
20
|
+
return el.fields.filter((f) => isNamedField(f));
|
|
21
|
+
}
|
|
22
|
+
const warned = new Set();
|
|
23
|
+
export function warnUnknownKey(el, option, key) {
|
|
24
|
+
const id = `${el.key}:${option}:${key}`;
|
|
25
|
+
if (warned.has(id))
|
|
26
|
+
return;
|
|
27
|
+
warned.add(id);
|
|
28
|
+
console.warn(`[table] '${el.key}': ${option} names '${key}', which is not a column of the row; ignored.`);
|
|
29
|
+
}
|
|
30
|
+
function numericCell(f, row) {
|
|
31
|
+
const v = cellValue(f.type, f.key, row);
|
|
32
|
+
const sub = magnitudeSub(f.type);
|
|
33
|
+
const raw = sub ? subValue(v, sub) : v;
|
|
34
|
+
const n = raw == null || raw === '' ? NaN : Number(raw);
|
|
35
|
+
return Number.isFinite(n) ? n : undefined;
|
|
36
|
+
}
|
|
37
|
+
export function aggregate(kind, f, rows) {
|
|
38
|
+
if (kind === 'count')
|
|
39
|
+
return rows.length;
|
|
40
|
+
const nums = rows.map((r) => numericCell(f, r)).filter((n) => n !== undefined);
|
|
41
|
+
if (!nums.length)
|
|
42
|
+
return undefined;
|
|
43
|
+
if (kind === 'sum')
|
|
44
|
+
return nums.reduce((a, b) => a + b, 0);
|
|
45
|
+
if (kind === 'avg')
|
|
46
|
+
return nums.reduce((a, b) => a + b, 0) / nums.length;
|
|
47
|
+
if (kind === 'min')
|
|
48
|
+
return Math.min(...nums);
|
|
49
|
+
return Math.max(...nums);
|
|
50
|
+
}
|
|
51
|
+
export function totalValue(f, rows, n) {
|
|
52
|
+
const sub = magnitudeSub(f.type);
|
|
53
|
+
if (!sub)
|
|
54
|
+
return n;
|
|
55
|
+
const sample = rows.map((r) => cellValue(f.type, f.key, r)).find((v) => v && typeof v === 'object');
|
|
56
|
+
return withSubValue(sample, sub, n);
|
|
57
|
+
}
|
|
58
|
+
function cellNode(f, row, editing, live, onCommit, recordCtx, error) {
|
|
59
|
+
if (editing) {
|
|
60
|
+
return (_jsx(FieldSlot, { el: f, field: f.type, fieldKey: f.key, value: row[f.key] ?? '', onChange: onCommit, mode: "edit", bare: true, recordCtx: recordCtx, error: error }));
|
|
61
|
+
}
|
|
62
|
+
return (_jsx(FieldSlot, { el: f, field: f.type, fieldKey: live ? f.key : undefined, value: row[f.key] ?? '', mode: "view", bare: !live, recordCtx: recordCtx, onChange: live ? onCommit : undefined, error: error }));
|
|
63
|
+
}
|
|
64
|
+
function TablePage({ el, value, recordCtx, mode }) {
|
|
65
|
+
const { t } = useTranslation();
|
|
66
|
+
const depth = useGroupDepth();
|
|
67
|
+
const errorAt = useFieldErrors();
|
|
68
|
+
const { write } = useSlot();
|
|
69
|
+
const group = el;
|
|
70
|
+
const cols = columnsOf(group);
|
|
71
|
+
const declaredSort = group.view?.['sort'];
|
|
72
|
+
const declaredDir = group.view?.['dir'] ?? 'asc';
|
|
73
|
+
const colOf = (key) => cols.find((f) => f.key === key);
|
|
74
|
+
if (declaredSort && !colOf(declaredSort))
|
|
75
|
+
warnUnknownKey(group, 'sort', declaredSort);
|
|
76
|
+
const [sort, setSort] = useState(declaredSort && colOf(declaredSort) ? { key: declaredSort, dir: declaredDir } : null);
|
|
77
|
+
const cycle = (key) => setSort((s) => (s?.key !== key ? { key, dir: 'asc' } : s.dir === 'asc' ? { key, dir: 'desc' } : null));
|
|
78
|
+
const g = useGroupEdit(mode, value, (v) => write(v));
|
|
79
|
+
const rows = Array.isArray(g.value) ? g.value : [];
|
|
80
|
+
const editing = g.mode !== 'view';
|
|
81
|
+
const live = useHybridScope() && mode === 'view';
|
|
82
|
+
const keyCtx = useMemo(() => ({ t }), [t]);
|
|
83
|
+
const ordered = useMemo(() => {
|
|
84
|
+
if (editing || !sort)
|
|
85
|
+
return rows;
|
|
86
|
+
const f = colOf(sort.key);
|
|
87
|
+
if (!f)
|
|
88
|
+
return rows;
|
|
89
|
+
return sortByField(rows, sort.key, sort.dir, f.type, keyCtx);
|
|
90
|
+
}, [rows, sort, editing, keyCtx]);
|
|
91
|
+
const indexOf = useMemo(() => new Map(rows.map((r, i) => [r, i])), [rows]);
|
|
92
|
+
const groupKey = group.view?.['groupBy'];
|
|
93
|
+
const groupFld = colOf(groupKey);
|
|
94
|
+
if (groupKey && !groupFld)
|
|
95
|
+
warnUnknownKey(group, 'groupBy', groupKey);
|
|
96
|
+
const buckets = useMemo(() => {
|
|
97
|
+
if (editing || !groupKey || !groupFld)
|
|
98
|
+
return [{ key: null, sort: '', label: '', rows: ordered }];
|
|
99
|
+
const byValue = new Map();
|
|
100
|
+
const none = [];
|
|
101
|
+
for (const row of ordered) {
|
|
102
|
+
const k = fieldKeys(groupFld.type, cellValue(groupFld.type, groupKey, row), keyCtx)[0];
|
|
103
|
+
if (!k) {
|
|
104
|
+
none.push(row);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const b = byValue.get(k.value) ?? { key: k.value, sort: k.sort ?? k.value, label: k.label, rows: [] };
|
|
108
|
+
b.rows.push(row);
|
|
109
|
+
byValue.set(k.value, b);
|
|
110
|
+
}
|
|
111
|
+
const out = [...byValue.values()].sort((a, b) => a.sort.localeCompare(b.sort, undefined, { numeric: true, sensitivity: 'base' }));
|
|
112
|
+
if (none.length)
|
|
113
|
+
out.push({ key: NO_VALUE, sort: NO_VALUE, label: t('table.noValue'), rows: none });
|
|
114
|
+
return out;
|
|
115
|
+
}, [ordered, editing, groupKey, groupFld, keyCtx, t]);
|
|
116
|
+
const rowTools = editing || live;
|
|
117
|
+
const numbered = group.view?.['numbered'] === true;
|
|
118
|
+
const totals = group.view?.['totals'];
|
|
119
|
+
for (const key of Object.keys(totals ?? {}))
|
|
120
|
+
if (!colOf(key))
|
|
121
|
+
warnUnknownKey(group, 'totals', key);
|
|
122
|
+
const hasTotals = Boolean(totals && Object.keys(totals).some((k) => colOf(k)));
|
|
123
|
+
const totalRow = (bucketRows, testId) => (_jsxs("tr", { "data-testid": testId, className: "text-xs text-muted", children: [numbered && _jsx("td", { className: "border-b border-border py-1.5 pr-3" }), cols.map((f, i) => {
|
|
124
|
+
const kind = totals?.[f.key];
|
|
125
|
+
const n = kind && colOf(f.key) ? aggregate(kind, f, bucketRows) : undefined;
|
|
126
|
+
return (_jsxs("td", { className: "border-b border-border py-1.5 pr-4", children: [i === 0 && !totals?.[f.key] ? t('table.total') : null, n === undefined ? null : (_jsx(FieldSlot, { el: f, field: f.type, value: totalValue(f, bucketRows, n), mode: "view", bare: true, recordCtx: recordCtx }))] }, f.key));
|
|
127
|
+
}), rowTools && _jsx("td", { className: "border-b border-border" })] }));
|
|
128
|
+
const { rowId, updateRow, removeRow, addRow } = useCollectionRows(rows, (v) => g.onChange(v));
|
|
129
|
+
const errorFor = (idx, key) => isStorageGroup(group) ? errorAt([group.key, idx, key]) : undefined;
|
|
130
|
+
const bodyRow = (row, idx, seq) => (_jsxs("tr", { "data-testid": `table-row-${idx}`, children: [numbered && _jsx("td", { className: "border-b border-border py-2 pr-3 text-xs text-muted", children: seq }), cols.map((f) => (_jsx("td", { className: "border-b border-border py-2 pr-4 align-top", children: cellNode(f, row, editing, live, (v) => updateRow(idx, f.key, v), recordCtx, errorFor(idx, f.key)) }, f.key))), rowTools && (_jsx("td", { className: "border-b border-border py-2 align-top", children: _jsx(RemoveBtn, { className: "mt-0.5", onClick: () => removeRow(idx) }) }))] }, rowId(row)));
|
|
131
|
+
const content = (_jsxs("div", { className: "flex flex-col gap-2 min-w-0", children: [_jsx("div", { className: "overflow-auto max-h-[60vh]", children: _jsxs("table", { className: "w-full border-separate border-spacing-0 text-sm", "data-testid": "table-block", children: [_jsx("thead", { className: "eink-solid sticky top-0 z-10 bg-bg/95 backdrop-blur", children: _jsxs("tr", { children: [numbered && _jsx("th", { className: "border-b border-border py-1.5 pr-3" }), cols.map((f) => (_jsx("th", { className: "border-b border-border py-1.5 pr-4 text-left text-xs font-normal text-muted", children: _jsxs("button", { type: "button", className: "inline-flex items-center gap-1 focus-visible:outline-2 focus-visible:outline-accent", onClick: () => cycle(f.key), children: [t(f.type.label), !editing && sort?.key === f.key && (_jsx(Icon, { name: sort.dir === 'asc' ? 'lucide:arrow-up' : 'lucide:arrow-down', className: "size-3" }))] }) }, f.key))), rowTools && _jsx("th", { className: "border-b border-border" })] }) }), buckets.map((b) => (_jsxs("tbody", { className: "tabular-nums", children: [b.key !== null && (_jsx("tr", { "data-testid": `table-group-${b.key}`, children: _jsxs("th", { colSpan: cols.length + (numbered ? 1 : 0) + (rowTools ? 1 : 0), className: "border-b border-border pt-4 pb-1 text-left text-xs font-medium text-muted", children: [b.key === NO_VALUE || !groupFld ? (b.label) : (_jsx(FieldSlot, { el: groupFld, field: groupFld.type, value: b.rows[0]?.[groupKey ?? ''], mode: "view", bare: true, recordCtx: recordCtx })), ' ', _jsx("span", { className: "text-muted/70", children: b.rows.length })] }) })), b.rows.map((row, i) => bodyRow(row, indexOf.get(row) ?? -1, i + 1)), hasTotals && b.key !== null && totalRow(b.rows, `table-subtotal-${b.key}`)] }, b.key ?? 'all'))), hasTotals && _jsx("tfoot", { children: totalRow(rows, 'table-total') })] }) }), rowTools && _jsx(AddBtn, { onClick: addRow, children: t('form.addRow') })] }));
|
|
132
|
+
if (mode !== 'view')
|
|
133
|
+
return content;
|
|
134
|
+
return (_jsxs("section", { className: `${groupScopeClass(depth)} flex flex-col gap-1 min-w-0`, "data-testid": "table", children: [group.label && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "text-[13px] font-semibold text-accent", children: t(group.label) }), !live && g.actions && _jsx("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: g.actions })] })), _jsx(GroupDepthProvider, { depth: depth + 1, children: content })] }));
|
|
135
|
+
}
|
|
136
|
+
registerRenderer({ kinds: ['table'], layout: 'block', Page: TablePage, editor: 'custom' });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Icon } from "../../components/Icon.js";
|
|
5
|
+
import { registerRenderer } from "../../registry.js";
|
|
6
|
+
import { Slot, SlotScope, useSlots } from "../slot.js";
|
|
7
|
+
function TabFields() {
|
|
8
|
+
return (_jsx(_Fragment, { children: useSlots().map((s) => (_jsx(Slot, { name: s.name }, s.name))) }));
|
|
9
|
+
}
|
|
10
|
+
function TabsPage({ el }) {
|
|
11
|
+
const g = el;
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
const tabs = g.view?.tabs ?? [];
|
|
14
|
+
const [active, setActive] = useState(0);
|
|
15
|
+
return (_jsxs("div", { className: "min-w-0", children: [_jsx("div", { role: "tablist", className: "flex flex-nowrap gap-1 overflow-x-auto border-b border-border", children: tabs.map((tab, i) => (_jsxs("button", { type: "button", role: "tab", "aria-selected": i === active, onClick: () => setActive(i), className: `flex shrink-0 items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${i === active ? 'border-accent text-accent' : 'border-transparent text-muted'}`, children: [tab.icon && _jsx(Icon, { name: tab.icon, size: 14, className: "shrink-0" }), t(tab.label)] }, tab.name))) }), tabs.map((tab, i) => (_jsx("div", { role: "tabpanel", hidden: i !== active, className: "min-w-0 space-y-3 pt-3", children: _jsx(SlotScope, { name: tab.name, children: _jsx(TabFields, {}) }) }, tab.name)))] }));
|
|
16
|
+
}
|
|
17
|
+
registerRenderer({ kinds: ['tabs'], layout: 'block', editor: 'custom', Page: TabsPage });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|