@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,215 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Input } from "../../components/ui/input.js";
|
|
3
|
+
import { registerRenderer } from "../../registry.js";
|
|
4
|
+
import { MultiTagInput, MultipleChips, Dash, orDash, useTranslation } from "../shared.js";
|
|
5
|
+
import { Icon } from "../../components/Icon.js";
|
|
6
|
+
import { parseJSON } from "../../lib/json.js";
|
|
7
|
+
import { DatePicker } from "../../components/ui/date-picker.js";
|
|
8
|
+
import { YearPicker, MonthPicker, YearGridPanel, MonthGridPanel } from "../../components/ui/month-year-picker.js";
|
|
9
|
+
import { TimePicker, TimePickerInputGroup, TimePickerInput, TimePickerSeparator, TimePickerTrigger, TimePickerContent, TimePickerHour, TimePickerMinute, TimePickerSecond, TimePickerClear, } from "../../components/ui/time-picker.js";
|
|
10
|
+
import { isoToDisplay, isoDateTimeToDisplay, splitDateTime, joinDateTime, isoToDate, dateToIso, } from "../../lib/date-format.js";
|
|
11
|
+
import { Calendar } from "../../components/ui/calendar.js";
|
|
12
|
+
import { Button } from "../../components/ui/button.js";
|
|
13
|
+
import { OptionList } from "../option-list.js";
|
|
14
|
+
import { FieldFrame } from "../field-frame.js";
|
|
15
|
+
function gran(hints, def) {
|
|
16
|
+
return hints['granularity'] ?? def;
|
|
17
|
+
}
|
|
18
|
+
function dtLocal(v, granularity = 'minute') {
|
|
19
|
+
const s = String(v ?? '');
|
|
20
|
+
if (granularity === 'second')
|
|
21
|
+
return s.length > 19 ? s.slice(0, 19) : s;
|
|
22
|
+
if (granularity === 'hour')
|
|
23
|
+
return s.length > 13 ? s.slice(0, 13) : s;
|
|
24
|
+
return s.length > 16 ? s.slice(0, 16) : s;
|
|
25
|
+
}
|
|
26
|
+
function dateChipContent(icon, format) {
|
|
27
|
+
return function DateChip(item) {
|
|
28
|
+
return (_jsxs(_Fragment, { children: [_jsx(Icon, { name: icon, size: 13, className: "shrink-0 text-muted" }), _jsx("span", { children: format ? format(String(item)) : String(item) })] }));
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function DateChips({ value, format, icon }) {
|
|
32
|
+
const items = parseJSON(value, []);
|
|
33
|
+
return _jsx(MultipleChips, { items: items, content: dateChipContent(icon, format) });
|
|
34
|
+
}
|
|
35
|
+
function DateValue({ value }) {
|
|
36
|
+
return _jsx(_Fragment, { children: dateChipContent('lucide:calendar')(value) });
|
|
37
|
+
}
|
|
38
|
+
function DateTimeValue({ value }) {
|
|
39
|
+
return _jsx(_Fragment, { children: dateChipContent('lucide:calendar-clock', (s) => s.replace('T', ' '))(value) });
|
|
40
|
+
}
|
|
41
|
+
function DateDisplay({ field, value }) {
|
|
42
|
+
if (field.hints['multiple'] === true)
|
|
43
|
+
return _jsx(DateChips, { value: value, icon: "lucide:calendar" });
|
|
44
|
+
return orDash(value, (v) => {
|
|
45
|
+
const g = gran(field.hints, 'day');
|
|
46
|
+
return _jsx("span", { children: g === 'month' || g === 'year' ? String(v) : isoToDisplay(String(v)) || String(v) });
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function DateInput({ name, field, value, onChange }) {
|
|
50
|
+
const g = gran(field.hints, 'day');
|
|
51
|
+
const inputType = g === 'month' ? 'month' : g === 'year' ? 'number' : 'date';
|
|
52
|
+
if (field.hints['multiple'] === true) {
|
|
53
|
+
return (_jsx(MultiTagInput, { value: value, onChange: onChange, display: dateChipContent('lucide:calendar'), renderInput: (v, set, add) => (_jsx(Input, { type: inputType, value: v, onChange: (e) => set(e.target.value), className: "flex-1", onKeyDown: (e) => {
|
|
54
|
+
if (e.key === 'Enter') {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
add();
|
|
57
|
+
}
|
|
58
|
+
} })) }));
|
|
59
|
+
}
|
|
60
|
+
const single = typeof value === 'string' ? value : String(value ?? '');
|
|
61
|
+
if (g === 'year')
|
|
62
|
+
return _jsx(YearPicker, { value: single, onChange: onChange });
|
|
63
|
+
if (g === 'month')
|
|
64
|
+
return _jsx(MonthPicker, { value: single, onChange: onChange });
|
|
65
|
+
return _jsx(DatePicker, { value: single, onChange: onChange });
|
|
66
|
+
}
|
|
67
|
+
function DatePage(p) {
|
|
68
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "inline", width: "w-40", bare: p.bare, view: (v) => _jsx(DateDisplay, { field: p.field, value: v }), edit: (value, onChange) => (_jsx(DateInput, { name: `field-${p.field.label}`, field: p.field, value: value, onChange: onChange })) }));
|
|
69
|
+
}
|
|
70
|
+
function dateKeys(value, field) {
|
|
71
|
+
if (value == null || value === '')
|
|
72
|
+
return [];
|
|
73
|
+
const g = gran(field.hints, 'day');
|
|
74
|
+
const s = String(value);
|
|
75
|
+
const label = g === 'year' || g === 'month' ? s : isoToDisplay(s) || s;
|
|
76
|
+
return [{ value: s, rawValue: value, label }];
|
|
77
|
+
}
|
|
78
|
+
export function DatePickerBody({ field, draft, save }) {
|
|
79
|
+
const g = gran(field.hints, 'day');
|
|
80
|
+
const single = typeof draft === 'string' ? draft : String(draft ?? '');
|
|
81
|
+
if (g === 'year')
|
|
82
|
+
return _jsx(YearGridPanel, { value: single, onChange: (v) => save(v) });
|
|
83
|
+
if (g === 'month')
|
|
84
|
+
return _jsx(MonthGridPanel, { value: single, onChange: (v) => save(v) });
|
|
85
|
+
const date = isoToDate(single);
|
|
86
|
+
return (_jsx(Calendar, { mode: "single", selected: date, captionLayout: "dropdown", defaultMonth: date, className: "w-full", onSelect: (d) => save(d ? dateToIso(d) : '') }));
|
|
87
|
+
}
|
|
88
|
+
registerRenderer({
|
|
89
|
+
kinds: ['date'],
|
|
90
|
+
Page: DatePage,
|
|
91
|
+
Value: DateValue,
|
|
92
|
+
Picker: DatePickerBody,
|
|
93
|
+
keys: dateKeys,
|
|
94
|
+
});
|
|
95
|
+
const TIME_VALIDATE = {
|
|
96
|
+
hour: /^([01]\d|2[0-3])$/,
|
|
97
|
+
minute: /^([01]\d|2[0-3]):[0-5]\d$/,
|
|
98
|
+
second: /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$/,
|
|
99
|
+
};
|
|
100
|
+
function TimeDisplay({ field, value }) {
|
|
101
|
+
if (field.hints['multiple'] !== true && (value == null || value === ''))
|
|
102
|
+
return _jsx(Dash, {});
|
|
103
|
+
return field.hints['multiple'] === true ? (_jsx(DateChips, { value: value, icon: "lucide:clock" })) : (_jsx("span", { className: "font-mono text-sm", children: String(value) }));
|
|
104
|
+
}
|
|
105
|
+
function TimeInput({ name, field, value, onChange }) {
|
|
106
|
+
const g = gran(field.hints, 'second');
|
|
107
|
+
const step = g === 'second' ? 1 : g === 'minute' ? 60 : 3600;
|
|
108
|
+
if (field.hints['multiple'] === true) {
|
|
109
|
+
return (_jsx(MultiTagInput, { value: value, onChange: onChange, validate: (v) => TIME_VALIDATE[g]?.test(v) ?? false, display: dateChipContent('lucide:clock'), renderInput: (v, set, add) => (_jsx(Input, { type: "time", step: step, value: v, onChange: (e) => set(e.target.value), className: "w-36", onKeyDown: (e) => {
|
|
110
|
+
if (e.key === 'Enter') {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
add();
|
|
113
|
+
}
|
|
114
|
+
} })) }));
|
|
115
|
+
}
|
|
116
|
+
const single = typeof value === 'string' ? value : String(value ?? '');
|
|
117
|
+
if (g === 'hour') {
|
|
118
|
+
return (_jsx(Input, { id: name, type: "time", step: step, value: single, onChange: (e) => onChange(e.target.value), className: `appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none` }));
|
|
119
|
+
}
|
|
120
|
+
const withSeconds = g === 'second';
|
|
121
|
+
return (_jsxs(TimePicker, { value: single, onValueChange: onChange, showSeconds: withSeconds, children: [_jsxs(TimePickerInputGroup, { id: name, children: [_jsx(TimePickerInput, { segment: "hour" }), _jsx(TimePickerSeparator, {}), _jsx(TimePickerInput, { segment: "minute" }), withSeconds && (_jsxs(_Fragment, { children: [_jsx(TimePickerSeparator, {}), _jsx(TimePickerInput, { segment: "second" })] })), _jsx(TimePickerTrigger, {})] }), _jsxs(TimePickerContent, { children: [_jsx(TimePickerHour, {}), _jsx(TimePickerMinute, {}), withSeconds && _jsx(TimePickerSecond, {}), _jsx(TimePickerClear, {})] })] }));
|
|
122
|
+
}
|
|
123
|
+
function TimePage(p) {
|
|
124
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "inline", width: "w-32", bare: p.bare, view: (v) => _jsx(TimeDisplay, { field: p.field, value: v }), edit: (value, onChange) => (_jsx(TimeInput, { name: `field-${p.field.label}`, field: p.field, value: value, onChange: onChange })) }));
|
|
125
|
+
}
|
|
126
|
+
function pad2(n) {
|
|
127
|
+
return String(n).padStart(2, '0');
|
|
128
|
+
}
|
|
129
|
+
const HOURS = Array.from({ length: 24 }, (_, i) => ({ value: pad2(i), title: pad2(i) }));
|
|
130
|
+
const MINUTES = Array.from({ length: 60 }, (_, i) => ({ value: pad2(i), title: pad2(i) }));
|
|
131
|
+
function ClockColumns({ value, onChange, granularity, }) {
|
|
132
|
+
const [h = '', m = '', sec = ''] = value.split(':');
|
|
133
|
+
const withMinutes = granularity !== 'hour';
|
|
134
|
+
const withSeconds = granularity === 'second';
|
|
135
|
+
const build = (nh, nm, ns) => {
|
|
136
|
+
if (!withMinutes)
|
|
137
|
+
return nh;
|
|
138
|
+
const base = `${nh || '00'}:${nm || '00'}`;
|
|
139
|
+
return withSeconds ? `${base}:${ns || '00'}` : base;
|
|
140
|
+
};
|
|
141
|
+
const column = (opts, cur, pick) => (_jsx("div", { className: "min-w-0 flex-1", children: _jsx(OptionList, { options: opts, value: cur, multiple: false, literal: true, search: false, onPick: (v) => pick(String(v)) }) }));
|
|
142
|
+
return (_jsxs("div", { className: "flex min-w-0 gap-1", children: [column(HOURS, h, (v) => onChange(build(v, m, sec))), withMinutes && column(MINUTES, m, (v) => onChange(build(h, v, sec))), withSeconds && column(MINUTES, sec, (v) => onChange(build(h, m, v)))] }));
|
|
143
|
+
}
|
|
144
|
+
function ClockActions({ save, remove, removeLabel }) {
|
|
145
|
+
const { t } = useTranslation();
|
|
146
|
+
return (_jsxs("div", { className: "mt-2 flex items-center justify-end gap-2 border-t border-border pt-2", children: [remove && (_jsx(Button, { type: "button", variant: "destructiveOutline", size: "sm", className: "mr-auto", onClick: remove, children: removeLabel ?? t('actions.delete') })), _jsx(Button, { type: "button", size: "sm", onClick: () => save(), children: t('actions.save') })] }));
|
|
147
|
+
}
|
|
148
|
+
function TimePickerBody({ field, draft, setDraft, save, remove, removeLabel }) {
|
|
149
|
+
const g = gran(field.hints, 'second');
|
|
150
|
+
const single = typeof draft === 'string' ? draft : String(draft ?? '');
|
|
151
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx(ClockColumns, { value: single, onChange: setDraft, granularity: g }), _jsx(ClockActions, { save: save, remove: remove, removeLabel: removeLabel })] }));
|
|
152
|
+
}
|
|
153
|
+
function TimeValue({ value }) {
|
|
154
|
+
return _jsx("span", { className: "font-mono text-sm", children: String(value ?? '') });
|
|
155
|
+
}
|
|
156
|
+
registerRenderer({
|
|
157
|
+
kinds: ['time'],
|
|
158
|
+
Page: TimePage,
|
|
159
|
+
Value: TimeValue,
|
|
160
|
+
Picker: TimePickerBody,
|
|
161
|
+
});
|
|
162
|
+
const DT_VALIDATE = {
|
|
163
|
+
hour: /^\d{4}-\d{2}-\d{2}T([01]\d|2[0-3])$/,
|
|
164
|
+
minute: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/,
|
|
165
|
+
second: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/,
|
|
166
|
+
};
|
|
167
|
+
function DateTimeDisplay({ field, value }) {
|
|
168
|
+
if (field.hints['multiple'] !== true && (value == null || value === ''))
|
|
169
|
+
return _jsx(Dash, {});
|
|
170
|
+
const g = gran(field.hints, 'minute');
|
|
171
|
+
return field.hints['multiple'] === true ? (_jsx(DateChips, { value: value, icon: "lucide:calendar-clock", format: (s) => dtLocal(s, g).replace('T', ' ') })) : (_jsx("span", { children: isoDateTimeToDisplay(dtLocal(value, g)) }));
|
|
172
|
+
}
|
|
173
|
+
function DateTimeInput({ name, field, value, onChange }) {
|
|
174
|
+
const g = gran(field.hints, 'minute');
|
|
175
|
+
const step = g === 'second' ? 1 : g === 'hour' ? 3600 : 60;
|
|
176
|
+
if (field.hints['multiple'] === true) {
|
|
177
|
+
return (_jsx(MultiTagInput, { value: value, onChange: onChange, validate: (v) => DT_VALIDATE[g]?.test(v) ?? false, display: dateChipContent('lucide:calendar-clock', (s) => dtLocal(s, g).replace('T', ' ')), renderInput: (v, set, add) => (_jsx(Input, { type: "datetime-local", step: step, value: v, onChange: (e) => set(e.target.value), className: "w-52", onKeyDown: (e) => {
|
|
178
|
+
if (e.key === 'Enter') {
|
|
179
|
+
e.preventDefault();
|
|
180
|
+
add();
|
|
181
|
+
}
|
|
182
|
+
} })) }));
|
|
183
|
+
}
|
|
184
|
+
const { date, time } = splitDateTime(dtLocal(value, g));
|
|
185
|
+
return (_jsxs("div", { className: "flex gap-2", children: [_jsx(DatePicker, { value: date, onChange: (d) => onChange(joinDateTime(d, time || '00:00')) }), _jsx(Input, { id: name, type: "time", step: step, value: time, onChange: (e) => onChange(joinDateTime(date, e.target.value)), className: `w-32 appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none` })] }));
|
|
186
|
+
}
|
|
187
|
+
function DateTimePage(p) {
|
|
188
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "inline", width: "w-56", bare: p.bare, view: (v) => _jsx(DateTimeDisplay, { field: p.field, value: v }), edit: (value, onChange) => (_jsx(DateTimeInput, { name: `field-${p.field.label}`, field: p.field, value: value, onChange: onChange })) }));
|
|
189
|
+
}
|
|
190
|
+
function DateTimeInline({ value, field }) {
|
|
191
|
+
const g = gran(field.hints, 'minute');
|
|
192
|
+
return _jsx("span", { className: "truncate", children: isoDateTimeToDisplay(dtLocal(value, g)) });
|
|
193
|
+
}
|
|
194
|
+
function dateTimeKeys(value, field) {
|
|
195
|
+
if (value == null || value === '')
|
|
196
|
+
return [];
|
|
197
|
+
const g = gran(field.hints, 'minute');
|
|
198
|
+
const s = dtLocal(value, g);
|
|
199
|
+
return [{ value: s, rawValue: value, label: isoDateTimeToDisplay(s) }];
|
|
200
|
+
}
|
|
201
|
+
function DateTimePickerBody({ field, draft, setDraft, save, remove, removeLabel }) {
|
|
202
|
+
const g = gran(field.hints, 'minute');
|
|
203
|
+
const { date, time } = splitDateTime(dtLocal(draft, g));
|
|
204
|
+
const day = isoToDate(date);
|
|
205
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap gap-2", children: [_jsx(Calendar, { mode: "single", selected: day, captionLayout: "dropdown", defaultMonth: day, onSelect: (d) => setDraft(joinDateTime(d ? dateToIso(d) : '', time || '00:00')) }), _jsx("div", { className: "min-w-0 flex-1", children: _jsx(ClockColumns, { value: time, onChange: (v) => setDraft(joinDateTime(date, v)), granularity: g }) })] }), _jsx(ClockActions, { save: save, remove: remove, removeLabel: removeLabel })] }));
|
|
206
|
+
}
|
|
207
|
+
registerRenderer({
|
|
208
|
+
kinds: ['datetime'],
|
|
209
|
+
pickerSize: 'large',
|
|
210
|
+
Page: DateTimePage,
|
|
211
|
+
Picker: DateTimePickerBody,
|
|
212
|
+
Inline: DateTimeInline,
|
|
213
|
+
Value: DateTimeValue,
|
|
214
|
+
keys: dateTimeKeys,
|
|
215
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function fmtDimensions(v: unknown): string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Input } from "../../components/ui/input.js";
|
|
3
|
+
import { registerRenderer } from "../../registry.js";
|
|
4
|
+
import { EDIT_INLINE_PAD } from "../chrome.js";
|
|
5
|
+
import { FieldFrame } from "../field-frame.js";
|
|
6
|
+
import { Dash } from "../shared.js";
|
|
7
|
+
function parse(v) {
|
|
8
|
+
if (v && typeof v === 'object')
|
|
9
|
+
return v;
|
|
10
|
+
try {
|
|
11
|
+
const o = JSON.parse(String(v ?? ''));
|
|
12
|
+
return o && typeof o === 'object' ? o : {};
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function fmtDimensions(v) {
|
|
19
|
+
const d = parse(v);
|
|
20
|
+
if (d.w == null || d.h == null || d.d == null)
|
|
21
|
+
return '';
|
|
22
|
+
const cm = (mm) => Number((mm / 10).toFixed(1));
|
|
23
|
+
return `${cm(d.w)}×${cm(d.h)}×${cm(d.d)} см`;
|
|
24
|
+
}
|
|
25
|
+
function DimInput({ value, onChange }) {
|
|
26
|
+
const d = parse(value);
|
|
27
|
+
const set = (k, raw) => {
|
|
28
|
+
const next = { ...d, [k]: raw === '' ? undefined : Math.round(Number(raw)) };
|
|
29
|
+
onChange(next.w == null && next.h == null && next.d == null ? '' : next);
|
|
30
|
+
};
|
|
31
|
+
const cell = (k, ph) => (_jsx(Input, { type: "number", min: 0, className: "w-20 min-w-0", placeholder: ph, value: d[k] ?? '', onChange: (e) => set(k, e.target.value) }));
|
|
32
|
+
return (_jsxs("div", { className: `flex items-center gap-1 min-w-0 ${EDIT_INLINE_PAD}`, children: [cell('w', 'Ш'), _jsx("span", { className: "text-muted", children: "\u00D7" }), cell('h', 'В'), _jsx("span", { className: "text-muted", children: "\u00D7" }), cell('d', 'Г'), _jsx("span", { className: "ml-1 text-xs text-muted", children: "\u043C\u043C" })] }));
|
|
33
|
+
}
|
|
34
|
+
function DimView({ value }) {
|
|
35
|
+
const s = fmtDimensions(value);
|
|
36
|
+
return s ? _jsx("span", { className: "tabular-nums", children: s }) : _jsx(Dash, {});
|
|
37
|
+
}
|
|
38
|
+
function DimPage(p) {
|
|
39
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "inline", width: "w-fit", bare: p.bare, view: (v) => _jsx(DimView, { value: v }), edit: (value, onChange) => _jsx(DimInput, { value: value, onChange: onChange }) }));
|
|
40
|
+
}
|
|
41
|
+
function DimInline({ value }) {
|
|
42
|
+
return _jsx(DimView, { value: value });
|
|
43
|
+
}
|
|
44
|
+
registerRenderer({ kinds: ['dimensions'], Page: DimPage, Inline: DimInline });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type EmbedRec } from '../../host-services.ts';
|
|
2
|
+
import type { FieldInlineProps } from '../../types.ts';
|
|
3
|
+
export declare function parseEmbeds(value: unknown): EmbedRec[];
|
|
4
|
+
export declare function getHostname(url: string): string;
|
|
5
|
+
export declare function safeHref(url: string): string | undefined;
|
|
6
|
+
export declare function EmbedGallery({ value, onChange, multiple, }: {
|
|
7
|
+
value: unknown;
|
|
8
|
+
onChange?: (v: EmbedRec[] | '') => void;
|
|
9
|
+
multiple?: boolean;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export declare function EmbedInline(p: FieldInlineProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Play, Link2, Trash2, ExternalLink } from 'lucide-react';
|
|
4
|
+
import { Button } from "../../components/ui/button.js";
|
|
5
|
+
import { Input } from "../../components/ui/input.js";
|
|
6
|
+
import { Dialog, DialogContent, DialogTitle } from "../../components/ui/dialog.js";
|
|
7
|
+
import { resolveEmbed } from "../../host-services.js";
|
|
8
|
+
import { registerRenderer } from "../../registry.js";
|
|
9
|
+
import { useTranslation } from "../shared.js";
|
|
10
|
+
import { FieldFrame } from "../field-frame.js";
|
|
11
|
+
export function parseEmbeds(value) {
|
|
12
|
+
const isRec = (x) => !!x && typeof x === 'object' && typeof x.url === 'string';
|
|
13
|
+
if (Array.isArray(value))
|
|
14
|
+
return value.filter(isRec);
|
|
15
|
+
if (isRec(value))
|
|
16
|
+
return [value];
|
|
17
|
+
try {
|
|
18
|
+
const v = JSON.parse(String(value ?? ''));
|
|
19
|
+
if (Array.isArray(v))
|
|
20
|
+
return v.filter(isRec);
|
|
21
|
+
if (isRec(v))
|
|
22
|
+
return [v];
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const playable = (r) => (r.type === 'video' || r.type === 'rich') && !!r.html;
|
|
30
|
+
export function getHostname(url) {
|
|
31
|
+
try {
|
|
32
|
+
return new URL(url).hostname;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return url;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function safeHref(url) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = new URL(url);
|
|
41
|
+
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
|
|
42
|
+
return url;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
function EmbedCard({ rec, onOpen, onRemove }) {
|
|
50
|
+
return (_jsxs("div", { className: "group relative @container", children: [_jsxs("button", { type: "button", onClick: onOpen, className: "flex w-full items-center gap-2 overflow-hidden rounded-lg border border-border text-left @[20rem]:flex-col @[20rem]:items-stretch @[20rem]:gap-0", children: [_jsxs("div", { className: "relative aspect-video w-20 shrink-0 bg-muted/40 @[20rem]:w-full", children: [rec.thumbnail ? (_jsx("img", { src: rec.thumbnail, alt: "", className: "h-full w-full object-cover" })) : (_jsx(Link2, { className: "absolute inset-0 m-auto text-muted", size: 20 })), playable(rec) && (_jsx("span", { className: "absolute inset-0 m-auto flex h-8 w-8 items-center justify-center rounded-full bg-black/60 text-white", children: _jsx(Play, { size: 16 }) }))] }), _jsxs("div", { className: "min-w-0 flex-1 px-2 py-2", children: [_jsx("div", { className: "truncate text-sm font-medium", children: rec.title ?? rec.url }), rec.provider && _jsx("div", { className: "truncate text-xs text-muted", children: rec.provider })] })] }), onRemove && (_jsx(Button, { type: "button", variant: "ghost", size: "iconSm", className: "absolute right-1 top-1 text-muted opacity-0 hover:text-danger group-hover:opacity-100", onClick: onRemove, children: _jsx(Trash2, { size: 13 }) }))] }));
|
|
51
|
+
}
|
|
52
|
+
function EmbedViewCard({ rec }) {
|
|
53
|
+
const title = rec.title || getHostname(rec.url);
|
|
54
|
+
const href = safeHref(rec.url);
|
|
55
|
+
const contentClass = 'group flex items-center gap-2 overflow-hidden rounded-lg border border-border bg-muted/5 px-3 py-2 transition-colors hover:bg-muted/15';
|
|
56
|
+
const content = (_jsxs(_Fragment, { children: [_jsx("div", { className: "shrink-0 text-muted group-hover:text-text", children: playable(rec) ? _jsx(Play, { size: 16 }) : _jsx(Link2, { size: 16 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-medium", children: title }), _jsx("div", { className: "truncate text-xs text-muted", children: rec.url })] })] }));
|
|
57
|
+
return href ? (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: contentClass, children: content })) : (_jsx("div", { className: contentClass, children: content }));
|
|
58
|
+
}
|
|
59
|
+
function EmbedDialog({ rec, onClose }) {
|
|
60
|
+
const { t } = useTranslation();
|
|
61
|
+
const href = safeHref(rec.url);
|
|
62
|
+
return (_jsx(Dialog, { open: true, onOpenChange: (o) => !o && onClose(), children: _jsxs(DialogContent, { className: "max-w-2xl", children: [_jsx(DialogTitle, { className: "truncate", children: rec.title ?? rec.url }), playable(rec) ? (_jsx("iframe", { title: rec.title ?? 'embed', srcDoc: rec.html, sandbox: "allow-scripts allow-presentation", allowFullScreen: true, className: "aspect-video w-full rounded-md border-0" })) : (_jsxs("div", { className: "flex flex-col gap-3", children: [rec.thumbnail && _jsx("img", { src: rec.thumbnail, alt: "", className: "w-full rounded-md" }), rec.description && _jsx("p", { className: "text-sm text-muted", children: rec.description })] })), href && (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", children: _jsxs(Button, { type: "button", variant: "outline", className: "w-full", children: [_jsx(ExternalLink, { size: 15 }), " ", t('embed.open')] }) }))] }) }));
|
|
63
|
+
}
|
|
64
|
+
export function EmbedGallery({ value, onChange, multiple, }) {
|
|
65
|
+
const { t } = useTranslation();
|
|
66
|
+
const list = parseEmbeds(value);
|
|
67
|
+
const [open, setOpen] = useState(null);
|
|
68
|
+
const [url, setUrl] = useState('');
|
|
69
|
+
const [busy, setBusy] = useState(false);
|
|
70
|
+
const [err, setErr] = useState(false);
|
|
71
|
+
const save = (next) => onChange?.(next.length ? next : '');
|
|
72
|
+
async function add() {
|
|
73
|
+
if (busy)
|
|
74
|
+
return;
|
|
75
|
+
if (!url.trim())
|
|
76
|
+
return;
|
|
77
|
+
setBusy(true);
|
|
78
|
+
setErr(false);
|
|
79
|
+
const rec = await resolveEmbed(url.trim());
|
|
80
|
+
setBusy(false);
|
|
81
|
+
if (!rec)
|
|
82
|
+
return setErr(true);
|
|
83
|
+
save([...list, rec]);
|
|
84
|
+
setUrl('');
|
|
85
|
+
}
|
|
86
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [onChange ? (_jsx("div", { className: "grid grid-cols-1 gap-2 @md:grid-cols-2 @2xl:grid-cols-3", children: list.map((rec, i) => (_jsx(EmbedCard, { rec: rec, onOpen: () => setOpen(rec), onRemove: () => save(list.filter((_, j) => j !== i)) }, `${rec.url}-${i}`))) })) : (list.length > 0 && (_jsx("div", { className: "flex flex-col gap-2", children: list.map((rec, i) => (_jsx(EmbedViewCard, { rec: rec }, `${rec.url}-${i}`))) }))), onChange && (multiple || list.length === 0) && (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { value: url, placeholder: t('embed.pastePlaceholder'), onChange: (e) => setUrl(e.target.value), onKeyDown: (e) => e.key === 'Enter' && (e.preventDefault(), add()) }), _jsx(Button, { type: "button", variant: "outline", disabled: busy || !url.trim(), onClick: add, children: busy ? '…' : '+' })] }), err && _jsx("span", { className: "text-xs text-danger", children: t('embed.resolveFailed') })] })), open && _jsx(EmbedDialog, { rec: open, onClose: () => setOpen(null) })] }));
|
|
87
|
+
}
|
|
88
|
+
function EmbedPage(p) {
|
|
89
|
+
const multiple = p.field.hints['multiple'] === true;
|
|
90
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "none", width: "w-full", bare: p.bare, view: (v) => _jsx(EmbedGallery, { value: v, multiple: multiple }), edit: (v, onChange) => (_jsx(EmbedGallery, { value: v, onChange: onChange, multiple: multiple })) }));
|
|
91
|
+
}
|
|
92
|
+
export function EmbedInline(p) {
|
|
93
|
+
const list = parseEmbeds(p.value);
|
|
94
|
+
if (list.length === 0)
|
|
95
|
+
return null;
|
|
96
|
+
const first = list[0];
|
|
97
|
+
return (_jsxs("span", { className: "inline-flex min-w-0 items-center gap-1", children: [playable(first) ? _jsx(Play, { size: 12 }) : _jsx(Link2, { size: 12 }), _jsx("span", { className: "truncate", children: first.title ?? getHostname(first.url) }), list.length > 1 && _jsxs("span", { className: "text-muted", children: ["+", list.length - 1] })] }));
|
|
98
|
+
}
|
|
99
|
+
registerRenderer({ kinds: ['embed'], layout: 'block', Page: EmbedPage, Inline: EmbedInline, pickerSize: 'large' });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type FileRec } from '../image.tsx';
|
|
2
|
+
export type FilePreviewKind = 'pdf' | 'text';
|
|
3
|
+
export declare function filePreviewKind(rec: FileRec): FilePreviewKind | null;
|
|
4
|
+
export declare function SingleFileWidget({ kind, value, onChange, }: {
|
|
5
|
+
kind: string;
|
|
6
|
+
value: unknown;
|
|
7
|
+
onChange: (v: FileRec | '') => void;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
export declare function ReadOnlyFiles({ kind, value }: {
|
|
10
|
+
kind: string;
|
|
11
|
+
value: unknown;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { FileText, Download, Trash2, Music, Film, Eye } from 'lucide-react';
|
|
4
|
+
import { mediaUrl } from "../../host-services.js";
|
|
5
|
+
import { Button } from "../../components/ui/button.js";
|
|
6
|
+
import { confirmDialog } from "../../components/ui/confirm.js";
|
|
7
|
+
import { Dialog, DialogBody, DialogContent, DialogHeader, DialogTitle } from "../../components/ui/dialog.js";
|
|
8
|
+
import { registerRenderer, multiBlock } from "../../registry.js";
|
|
9
|
+
import { useTranslation, UploadButton, Dash } from "../shared.js";
|
|
10
|
+
import { useFileUpload } from "../use-file-upload.js";
|
|
11
|
+
import { FieldFrame } from "../field-frame.js";
|
|
12
|
+
import { useHybridScope } from "../hybrid-scope.js";
|
|
13
|
+
import { ImageWidget, ImagesWidget, ImageInline, GalleryView, AvatarView, CoverView, AvatarWidget, CoverWidget, AvatarInline, parseFileRecs, } from "../image.js";
|
|
14
|
+
function confirmDeleteFile(t) {
|
|
15
|
+
return confirmDialog({
|
|
16
|
+
title: t('actions.confirmDelete'),
|
|
17
|
+
confirmLabel: t('actions.delete'),
|
|
18
|
+
variant: 'destructive',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function fileSizeLabel(size) {
|
|
22
|
+
if (size == null)
|
|
23
|
+
return null;
|
|
24
|
+
return size >= 1024 * 1024 ? `${(size / 1024 / 1024).toFixed(1)} MB` : `${Math.round(size / 1024)} KB`;
|
|
25
|
+
}
|
|
26
|
+
const TEXT_PREVIEW_EXT = /\.(txt|md|markdown|csv|json|xml|log|yaml|yml|ini|toml|js|jsx|ts|tsx|css|html)$/i;
|
|
27
|
+
export function filePreviewKind(rec) {
|
|
28
|
+
const mime = rec.mime?.toLowerCase().split(';', 1)[0];
|
|
29
|
+
if (mime === 'application/pdf' || /\.pdf$/i.test(rec.name))
|
|
30
|
+
return 'pdf';
|
|
31
|
+
if (mime?.startsWith('text/') ||
|
|
32
|
+
mime === 'application/json' ||
|
|
33
|
+
mime === 'application/xml' ||
|
|
34
|
+
TEXT_PREVIEW_EXT.test(rec.name))
|
|
35
|
+
return 'text';
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const MAX_TEXT_PREVIEW = 1_000_000;
|
|
39
|
+
function FilePreviewDialog({ rec, kind, onClose }) {
|
|
40
|
+
const { t } = useTranslation();
|
|
41
|
+
const [text, setText] = useState(null);
|
|
42
|
+
const [failed, setFailed] = useState(false);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (kind !== 'text')
|
|
45
|
+
return;
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
setText(null);
|
|
48
|
+
setFailed(false);
|
|
49
|
+
fetch(mediaUrl(rec.name), { signal: controller.signal })
|
|
50
|
+
.then((response) => {
|
|
51
|
+
if (!response.ok)
|
|
52
|
+
throw new Error(`preview_${response.status}`);
|
|
53
|
+
return response.text();
|
|
54
|
+
})
|
|
55
|
+
.then((body) => setText(body.length > MAX_TEXT_PREVIEW
|
|
56
|
+
? `${body.slice(0, MAX_TEXT_PREVIEW)}\n\n${t('files.previewTruncated')}`
|
|
57
|
+
: body))
|
|
58
|
+
.catch((error) => {
|
|
59
|
+
if (error?.name !== 'AbortError')
|
|
60
|
+
setFailed(true);
|
|
61
|
+
});
|
|
62
|
+
return () => controller.abort();
|
|
63
|
+
}, [kind, rec.name, t]);
|
|
64
|
+
return (_jsx(Dialog, { open: true, onOpenChange: (open) => !open && onClose(), children: _jsxs(DialogContent, { className: "h-[88dvh] max-w-5xl overflow-hidden md:h-[85dvh]", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { className: "truncate pr-8", children: rec.name }) }), _jsx(DialogBody, { children: kind === 'pdf' ? (_jsx("iframe", { title: rec.name, src: mediaUrl(rec.name), className: "h-full w-full rounded-lg border border-border bg-white" })) : failed ? (_jsx("div", { className: "flex h-full items-center justify-center text-sm text-danger", children: t('files.previewFailed') })) : text === null ? (_jsx("div", { className: "flex h-full items-center justify-center text-sm text-muted", children: t('files.loadingPreview') })) : (_jsx("pre", { className: "h-full overflow-auto whitespace-pre-wrap break-words rounded-lg border border-border bg-bg p-4 font-mono text-xs leading-relaxed", children: text })) })] }) }));
|
|
65
|
+
}
|
|
66
|
+
function FilePreviewButton({ rec }) {
|
|
67
|
+
const { t } = useTranslation();
|
|
68
|
+
const kind = filePreviewKind(rec);
|
|
69
|
+
const [open, setOpen] = useState(false);
|
|
70
|
+
if (!kind)
|
|
71
|
+
return null;
|
|
72
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "iconSm", className: "text-muted hover:text-accent", title: t('files.preview'), "aria-label": t('files.preview'), onClick: () => setOpen(true), children: _jsx(Eye, { size: 15 }) }), open && _jsx(FilePreviewDialog, { rec: rec, kind: kind, onClose: () => setOpen(false) })] }));
|
|
73
|
+
}
|
|
74
|
+
function FileRowContent({ rec }) {
|
|
75
|
+
return (_jsxs(_Fragment, { children: [_jsx(FileText, { size: 16, className: "text-muted shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate text-sm", children: rec.name }), fileSizeLabel(rec.size) && _jsx("span", { className: "text-xs text-muted", children: fileSizeLabel(rec.size) })] }));
|
|
76
|
+
}
|
|
77
|
+
function MediaPlayer({ kind, rec, className }) {
|
|
78
|
+
return kind === 'audio' ? (_jsx("audio", { controls: true, src: mediaUrl(rec.name), className: className })) : (_jsx("video", { controls: true, src: mediaUrl(rec.name), className: className }));
|
|
79
|
+
}
|
|
80
|
+
const ACCEPT = {
|
|
81
|
+
file: '*',
|
|
82
|
+
document: '.pdf,.doc,.docx,.xls,.xlsx,.txt,.csv',
|
|
83
|
+
audio: 'audio/*',
|
|
84
|
+
video: 'video/*',
|
|
85
|
+
image: 'image/*',
|
|
86
|
+
media: 'image/*,video/*',
|
|
87
|
+
avatar: 'image/*',
|
|
88
|
+
cover: 'image/*',
|
|
89
|
+
poster: 'image/*',
|
|
90
|
+
};
|
|
91
|
+
function FileListWidget({ kind, value, onChange, }) {
|
|
92
|
+
const { t } = useTranslation();
|
|
93
|
+
const { busy, error, progress, upload, retry, canRetry } = useFileUpload();
|
|
94
|
+
const list = parseFileRecs(value);
|
|
95
|
+
const save = (next) => onChange(next.length ? next : '');
|
|
96
|
+
async function add(f) {
|
|
97
|
+
if (!f)
|
|
98
|
+
return;
|
|
99
|
+
const rec = await upload(f);
|
|
100
|
+
if (rec)
|
|
101
|
+
save([...list, rec]);
|
|
102
|
+
}
|
|
103
|
+
async function retryAdd() {
|
|
104
|
+
const rec = await retry();
|
|
105
|
+
if (rec)
|
|
106
|
+
save([...list, rec]);
|
|
107
|
+
}
|
|
108
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [list.map((rec, i) => (_jsxs("div", { className: "flex min-h-9 items-center gap-2 rounded-lg border border-border px-2", children: [_jsx(FileRowContent, { rec: rec }), _jsx(FilePreviewButton, { rec: rec }), _jsx("a", { href: mediaUrl(rec.name), download: true, className: "text-muted hover:text-accent", title: t('files.download'), "aria-label": t('files.download'), children: _jsx(Download, { size: 15 }) }), _jsx(Button, { type: "button", variant: "ghost", size: "iconSm", disabled: busy, className: "text-muted hover:text-danger", onClick: async () => {
|
|
109
|
+
if (await confirmDeleteFile(t))
|
|
110
|
+
save(list.filter((_, j) => j !== i));
|
|
111
|
+
}, children: _jsx(Trash2, { size: 13 }) })] }, `${rec.name}-${i}`))), _jsx(UploadButton, { accept: ACCEPT[kind] ?? '*', busy: busy, error: error, progress: progress, onPick: add, onRetry: () => void retryAdd(), canRetry: canRetry })] }));
|
|
112
|
+
}
|
|
113
|
+
export function SingleFileWidget({ kind, value, onChange, }) {
|
|
114
|
+
const { t } = useTranslation();
|
|
115
|
+
const { busy, error, progress, upload, retry, canRetry } = useFileUpload();
|
|
116
|
+
const rec = parseFileRecs(value)[0];
|
|
117
|
+
async function add(f) {
|
|
118
|
+
if (!f)
|
|
119
|
+
return;
|
|
120
|
+
const next = await upload(f);
|
|
121
|
+
if (next)
|
|
122
|
+
onChange(next);
|
|
123
|
+
}
|
|
124
|
+
async function retryAdd() {
|
|
125
|
+
const next = await retry();
|
|
126
|
+
if (next)
|
|
127
|
+
onChange(next);
|
|
128
|
+
}
|
|
129
|
+
if (!rec) {
|
|
130
|
+
return (_jsx(UploadButton, { accept: ACCEPT[kind] ?? '*', busy: busy, error: error, progress: progress, onPick: (f) => void add(f), onRetry: () => void retryAdd(), canRetry: canRetry, compact: true }));
|
|
131
|
+
}
|
|
132
|
+
return (_jsxs("div", { "data-testid": "single-file", className: "flex min-h-9 items-center gap-2 rounded-lg border border-border px-2 hover:border-accent", children: [_jsx(FileRowContent, { rec: rec }), _jsx(FilePreviewButton, { rec: rec }), _jsx("a", { href: mediaUrl(rec.name), download: true, className: "text-muted hover:text-accent", title: t('files.download'), "aria-label": t('files.download'), children: _jsx(Download, { size: 15 }) }), _jsx(Button, { type: "button", variant: "ghost", size: "iconSm", disabled: busy, className: "text-muted hover:text-danger", onClick: async () => {
|
|
133
|
+
if (await confirmDeleteFile(t))
|
|
134
|
+
onChange('');
|
|
135
|
+
}, children: _jsx(Trash2, { size: 13 }) })] }));
|
|
136
|
+
}
|
|
137
|
+
function MediaListWidget({ kind, value, onChange, }) {
|
|
138
|
+
const { t } = useTranslation();
|
|
139
|
+
const { busy, error, progress, upload, retry, canRetry } = useFileUpload();
|
|
140
|
+
const list = parseFileRecs(value);
|
|
141
|
+
const save = (next) => onChange(next.length ? next : '');
|
|
142
|
+
async function add(f) {
|
|
143
|
+
if (!f)
|
|
144
|
+
return;
|
|
145
|
+
const rec = await upload(f);
|
|
146
|
+
if (rec)
|
|
147
|
+
save([...list, rec]);
|
|
148
|
+
}
|
|
149
|
+
async function retryAdd() {
|
|
150
|
+
const rec = await retry();
|
|
151
|
+
if (rec)
|
|
152
|
+
save([...list, rec]);
|
|
153
|
+
}
|
|
154
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [list.map((rec, i) => (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx(MediaPlayer, { kind: kind, rec: rec, className: kind === 'audio' ? 'min-w-0 flex-1' : 'min-w-0 flex-1 max-h-72 rounded-lg' }), _jsx(Button, { type: "button", variant: "ghost", size: "iconSm", disabled: busy, className: "shrink-0 text-muted hover:text-danger", onClick: async () => {
|
|
155
|
+
if (await confirmDeleteFile(t))
|
|
156
|
+
save(list.filter((_, j) => j !== i));
|
|
157
|
+
}, children: _jsx(Trash2, { size: 13 }) })] }, `${rec.name}-${i}`))), _jsx(UploadButton, { accept: ACCEPT[kind] ?? '*', busy: busy, error: error, progress: progress, onPick: add, onRetry: () => void retryAdd(), canRetry: canRetry })] }));
|
|
158
|
+
}
|
|
159
|
+
function widgetFor(kind, value, onChange, multiple) {
|
|
160
|
+
if (kind === 'avatar') {
|
|
161
|
+
return _jsx(AvatarWidget, { value: value, onChange: onChange, accept: ACCEPT[kind], multiple: multiple });
|
|
162
|
+
}
|
|
163
|
+
if (kind === 'cover') {
|
|
164
|
+
return _jsx(CoverWidget, { value: value, onChange: onChange, accept: ACCEPT[kind], multiple: multiple });
|
|
165
|
+
}
|
|
166
|
+
if (kind === 'image' || kind === 'media' || kind === 'poster') {
|
|
167
|
+
const poster = kind === 'poster';
|
|
168
|
+
return multiple ? (_jsx(ImagesWidget, { value: value, onChange: onChange, accept: ACCEPT[kind], poster: poster })) : (_jsx(ImageWidget, { value: value, onChange: onChange, accept: ACCEPT[kind], poster: poster }));
|
|
169
|
+
}
|
|
170
|
+
if (kind === 'audio' || kind === 'video') {
|
|
171
|
+
return _jsx(MediaListWidget, { kind: kind, value: value, onChange: onChange });
|
|
172
|
+
}
|
|
173
|
+
return _jsx(FileListWidget, { kind: kind, value: value, onChange: onChange });
|
|
174
|
+
}
|
|
175
|
+
export function ReadOnlyFiles({ kind, value }) {
|
|
176
|
+
const { t } = useTranslation();
|
|
177
|
+
const list = parseFileRecs(value);
|
|
178
|
+
if (!list.length)
|
|
179
|
+
return _jsx(Dash, {});
|
|
180
|
+
if (kind === 'avatar')
|
|
181
|
+
return _jsx(AvatarView, { value: value });
|
|
182
|
+
if (kind === 'cover')
|
|
183
|
+
return _jsx(CoverView, { value: value });
|
|
184
|
+
if (kind === 'image' || kind === 'media')
|
|
185
|
+
return _jsx(GalleryView, { value: value });
|
|
186
|
+
if (kind === 'poster')
|
|
187
|
+
return _jsx(GalleryView, { value: value, poster: true });
|
|
188
|
+
if (kind === 'audio' || kind === 'video') {
|
|
189
|
+
return (_jsx("div", { className: "flex flex-col gap-2", children: list.map((rec, i) => (_jsx(MediaPlayer, { kind: kind, rec: rec, className: kind === 'audio' ? 'w-full' : 'max-h-72 rounded-lg' }, `${rec.name}-${i}`))) }));
|
|
190
|
+
}
|
|
191
|
+
return (_jsx("div", { className: "flex flex-col gap-2", children: list.map((rec, i) => (_jsxs("div", { className: "flex min-h-9 items-center gap-2 rounded-lg border border-border px-2 hover:border-accent", children: [_jsx(FileRowContent, { rec: rec }), _jsx(FilePreviewButton, { rec: rec }), _jsx("a", { href: mediaUrl(rec.name), download: true, className: "text-muted hover:text-accent", title: t('files.download'), "aria-label": t('files.download'), children: _jsx(Download, { size: 15 }) })] }, `${rec.name}-${i}`))) }));
|
|
192
|
+
}
|
|
193
|
+
function FilePage(p) {
|
|
194
|
+
const kind = p.field.kind;
|
|
195
|
+
const multiple = p.field.hints['multiple'] === true;
|
|
196
|
+
const live = useHybridScope();
|
|
197
|
+
return (_jsx(FieldFrame, { field: p.field, value: p.value, label: p.label, required: p.required, error: p.error, mode: p.mode, geom: "none", width: "w-full", bare: p.bare, view: (v, commit) => commit && live ? (widgetFor(kind, v ?? '', (nv) => void commit(nv), multiple)) : (_jsx(ReadOnlyFiles, { kind: kind, value: v ?? '' })), edit: (v, onChange) => widgetFor(kind, v ?? '', onChange, multiple) }));
|
|
198
|
+
}
|
|
199
|
+
function FileInline(p) {
|
|
200
|
+
const rec = parseFileRecs(p.value)[0];
|
|
201
|
+
if (!rec)
|
|
202
|
+
return _jsx("span", { className: "text-muted", children: "\u2014" });
|
|
203
|
+
const kind = p.field?.kind;
|
|
204
|
+
const Ico = kind === 'audio' ? Music : kind === 'video' ? Film : FileText;
|
|
205
|
+
const sz = fileSizeLabel(rec.size);
|
|
206
|
+
return (_jsxs("span", { className: "inline-flex min-w-0 items-center gap-1.5 text-sm", children: [_jsx(Ico, { size: 14, className: "text-muted shrink-0" }), _jsx("span", { className: "truncate", children: rec.name }), sz && _jsx("span", { className: "shrink-0 text-xs text-muted", children: sz })] }));
|
|
207
|
+
}
|
|
208
|
+
registerRenderer({
|
|
209
|
+
kinds: ['image', 'media', 'poster'],
|
|
210
|
+
layout: multiBlock,
|
|
211
|
+
box: 'none',
|
|
212
|
+
Page: FilePage,
|
|
213
|
+
Inline: ImageInline,
|
|
214
|
+
editor: 'inplace',
|
|
215
|
+
});
|
|
216
|
+
registerRenderer({
|
|
217
|
+
kinds: ['file', 'document', 'audio', 'video'],
|
|
218
|
+
layout: multiBlock,
|
|
219
|
+
box: 'none',
|
|
220
|
+
Page: FilePage,
|
|
221
|
+
Inline: FileInline,
|
|
222
|
+
editor: 'inplace',
|
|
223
|
+
});
|
|
224
|
+
registerRenderer({
|
|
225
|
+
kinds: ['avatar'],
|
|
226
|
+
layout: multiBlock,
|
|
227
|
+
box: 'none',
|
|
228
|
+
Page: FilePage,
|
|
229
|
+
Inline: AvatarInline,
|
|
230
|
+
editor: 'inplace',
|
|
231
|
+
});
|
|
232
|
+
registerRenderer({
|
|
233
|
+
kinds: ['cover'],
|
|
234
|
+
layout: 'block',
|
|
235
|
+
Page: FilePage,
|
|
236
|
+
Inline: ImageInline,
|
|
237
|
+
editor: 'inplace',
|
|
238
|
+
});
|