@keyfunc/keybricks-ds 1.0.4 → 2.1.1
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/DEPLOY.md +4 -4
- package/DESIGN.md +154 -53
- package/INTEGRATION.md +174 -35
- package/SKILL.md +6 -4
- package/_adherence.oxlintrc.json +164 -68
- package/_scripts/build-index.mjs +9 -0
- package/_scripts/build-tokens.mjs +5 -2
- package/assets/icons/icon-index.js +1 -1
- package/assets/icons/icon-index.json +1 -1
- package/components/_cards/accordion.card.html +2 -2
- package/components/_cards/action-chip.card.html +8 -3
- package/components/_cards/activity.card.html +2 -2
- package/components/_cards/alert.card.html +2 -2
- package/components/_cards/app-chrome.card.html +4 -4
- package/components/_cards/banner.card.html +1 -1
- package/components/_cards/breadcrumb.card.html +2 -2
- package/components/_cards/button-1-base.card.html +2 -2
- package/components/_cards/button-2-states.card.html +14 -14
- package/components/_cards/button-3-sizes-icons.card.html +1 -1
- package/components/_cards/button-4-group.card.html +5 -5
- package/components/_cards/canvas-zoom-controls.card.html +1 -1
- package/components/_cards/cards-indicators.card.html +2 -2
- package/components/_cards/charts-1-sparkline.card.html +2 -2
- package/components/_cards/charts-10-gauge.card.html +4 -4
- package/components/_cards/charts-11-funnel.card.html +2 -2
- package/components/_cards/charts-12-treemap.card.html +2 -2
- package/components/_cards/charts-14-radar.card.html +1 -1
- package/components/_cards/charts-15-distribution.card.html +1 -1
- package/components/_cards/charts-16-pareto.card.html +2 -2
- package/components/_cards/charts-17-candlestick.card.html +2 -2
- package/components/_cards/charts-18-sankey.card.html +2 -2
- package/components/_cards/charts-19-chord.card.html +2 -2
- package/components/_cards/charts-2-line.card.html +2 -2
- package/components/_cards/charts-20-sunburst.card.html +2 -2
- package/components/_cards/charts-21-worldmap.card.html +2 -2
- package/components/_cards/charts-22-gantt.card.html +2 -2
- package/components/_cards/charts-23-bar3d.card.html +2 -2
- package/components/_cards/charts-24-pie3d.card.html +2 -2
- package/components/_cards/charts-25-surface3d.card.html +2 -2
- package/components/_cards/charts-26-scatter3d.card.html +2 -2
- package/components/_cards/charts-27-ribbon3d.card.html +2 -2
- package/components/_cards/charts-3-bar.card.html +2 -2
- package/components/_cards/charts-4-barlist.card.html +2 -2
- package/components/_cards/charts-5-donut.card.html +3 -3
- package/components/_cards/charts-6-scatter.card.html +2 -2
- package/components/_cards/charts-7-heatmap.card.html +2 -2
- package/components/_cards/charts-8-waterfall.card.html +2 -2
- package/components/_cards/charts-9-bullet.card.html +2 -2
- package/components/_cards/charts-99-fullpage.card.html +5 -5
- package/components/_cards/checkbox.card.html +4 -4
- package/components/_cards/codeblock.card.html +1 -1
- package/components/_cards/combobox.card.html +3 -2
- package/components/_cards/context-menu.card.html +3 -2
- package/components/_cards/date-picker.card.html +5 -2
- package/components/_cards/diffvalue.card.html +2 -2
- package/components/_cards/divider.card.html +3 -3
- package/components/_cards/drawer.card.html +2 -2
- package/components/_cards/dropdown-menu.card.html +6 -6
- package/components/_cards/dropzone.card.html +1 -1
- package/components/_cards/empty-state.card.html +5 -5
- package/components/_cards/filter-bar.card.html +5 -5
- package/components/_cards/icon-button.card.html +2 -2
- package/components/_cards/icons-1-core.card.html +2 -2
- package/components/_cards/icons-2-feather.card.html +3 -3
- package/components/_cards/icons-3-unicons.card.html +3 -3
- package/components/_cards/input.card.html +5 -2
- package/components/_cards/kbd.card.html +2 -2
- package/components/_cards/keyvalue.card.html +2 -2
- package/components/_cards/listrow.card.html +2 -2
- package/components/_cards/loading.card.html +2 -2
- package/components/_cards/log-viewer.card.html +2 -2
- package/components/_cards/modal.card.html +9 -9
- package/components/_cards/multi-select.card.html +3 -2
- package/components/_cards/number-stepper.card.html +3 -2
- package/components/_cards/pagination.card.html +3 -2
- package/components/_cards/people.card.html +3 -3
- package/components/_cards/pipeline-node.card.html +2 -2
- package/components/_cards/pipeline-palette.card.html +4 -4
- package/components/_cards/radio.card.html +2 -2
- package/components/_cards/search.card.html +5 -2
- package/components/_cards/segmented-control.card.html +2 -2
- package/components/_cards/select.card.html +4 -4
- package/components/_cards/slider.card.html +1 -1
- package/components/_cards/smart-table.card.html +1 -1
- package/components/_cards/spreadsheet.card.html +2 -2
- package/components/_cards/stepper.card.html +2 -2
- package/components/_cards/table.card.html +103 -0
- package/components/_cards/tabs.card.html +3 -2
- package/components/_cards/tag.card.html +5 -2
- package/components/_cards/textarea.card.html +2 -2
- package/components/_cards/timeline.card.html +1 -1
- package/components/_cards/toast.card.html +4 -4
- package/components/_cards/toggles.card.html +3 -3
- package/components/_cards/tooltip.card.html +5 -5
- package/components/_cards/tree.card.html +2 -2
- package/components/_shared/src-buttons.js +1 -1
- package/components/_shared/src-charts.js +1 -1
- package/components/_shared/src-data.js +1 -1
- package/components/_shared/src-display.js +1 -1
- package/components/_shared/src-feedback.js +1 -1
- package/components/_shared/src-forms.js +1 -1
- package/components/_shared/src-navigation.js +1 -1
- package/components/_shared/src-pipeline.js +1 -1
- package/components/_shared/src-tabs.js +1 -1
- package/components/_vue/index.mjs +9 -5
- package/components/buttons/ActionChip.d.ts +3 -1
- package/components/buttons/ActionChip.jsx +4 -4
- package/components/buttons/ActionChip.vue +4 -4
- package/components/buttons/Button.d.ts +5 -2
- package/components/buttons/Button.jsx +15 -18
- package/components/buttons/Button.vue +17 -18
- package/components/buttons/ButtonGroup.vue +1 -1
- package/components/buttons/IconButton.d.ts +2 -1
- package/components/buttons/IconButton.jsx +9 -7
- package/components/buttons/IconButton.vue +10 -8
- package/components/buttons/buttons.prompt.md +6 -5
- package/components/charts/Bar3D.jsx +2 -2
- package/components/charts/Bar3D.vue +2 -2
- package/components/charts/BarChart.jsx +4 -4
- package/components/charts/BarChart.vue +4 -4
- package/components/charts/BarList.jsx +1 -1
- package/components/charts/BarList.vue +1 -1
- package/components/charts/BoxPlot.jsx +1 -1
- package/components/charts/BoxPlot.vue +2 -2
- package/components/charts/BulletChart.jsx +1 -1
- package/components/charts/BulletChart.vue +1 -1
- package/components/charts/CandlestickChart.jsx +2 -2
- package/components/charts/CandlestickChart.vue +3 -3
- package/components/charts/ChartKit.jsx +5 -5
- package/components/charts/ChartLegend.vue +16 -0
- package/components/charts/ChartTooltip.vue +16 -0
- package/components/charts/ChordChart.jsx +2 -2
- package/components/charts/ChordChart.vue +2 -2
- package/components/charts/GanttChart.jsx +2 -2
- package/components/charts/GanttChart.vue +2 -2
- package/components/charts/GaugeChart.jsx +2 -2
- package/components/charts/GaugeChart.vue +2 -2
- package/components/charts/HeatmapChart.jsx +2 -2
- package/components/charts/HeatmapChart.vue +2 -2
- package/components/charts/Histogram.jsx +2 -2
- package/components/charts/Histogram.vue +3 -3
- package/components/charts/LineChart.jsx +1 -1
- package/components/charts/LineChart.vue +3 -3
- package/components/charts/ParetoChart.jsx +3 -3
- package/components/charts/ParetoChart.vue +4 -4
- package/components/charts/PlotGrid.vue +18 -0
- package/components/charts/RadarChart.jsx +1 -1
- package/components/charts/RadarChart.vue +1 -1
- package/components/charts/Ribbon3D.jsx +2 -2
- package/components/charts/Ribbon3D.vue +2 -2
- package/components/charts/SankeyChart.jsx +2 -2
- package/components/charts/SankeyChart.vue +2 -2
- package/components/charts/Scatter3D.jsx +1 -1
- package/components/charts/Scatter3D.vue +1 -1
- package/components/charts/ScatterChart.jsx +3 -3
- package/components/charts/ScatterChart.vue +4 -4
- package/components/charts/Sparkline.jsx +1 -1
- package/components/charts/Sparkline.vue +1 -1
- package/components/charts/SunburstChart.jsx +1 -1
- package/components/charts/SunburstChart.vue +1 -1
- package/components/charts/Surface3D.jsx +2 -2
- package/components/charts/Surface3D.vue +2 -2
- package/components/charts/TreemapChart.jsx +2 -2
- package/components/charts/TreemapChart.vue +2 -2
- package/components/charts/WaterfallChart.jsx +2 -2
- package/components/charts/WaterfallChart.vue +3 -3
- package/components/charts/WorldMap.jsx +2 -2
- package/components/charts/WorldMap.vue +2 -2
- package/components/charts/XLabels.vue +11 -0
- package/components/data/CodeBlock.jsx +1 -1
- package/components/data/CodeBlock.vue +1 -1
- package/components/data/DiffValue.jsx +1 -1
- package/components/data/DiffValue.vue +1 -1
- package/components/data/FilterBar.d.ts +2 -0
- package/components/data/FilterBar.jsx +10 -9
- package/components/data/FilterBar.vue +9 -8
- package/components/data/KeyValue.jsx +3 -3
- package/components/data/KeyValue.vue +3 -3
- package/components/data/ListRow.jsx +2 -2
- package/components/data/ListRow.vue +2 -2
- package/components/data/Pagination.d.ts +5 -1
- package/components/data/Pagination.jsx +16 -12
- package/components/data/Pagination.vue +14 -9
- package/components/data/SmartTable.d.ts +4 -1
- package/components/data/SmartTable.jsx +16 -14
- package/components/data/SmartTable.vue +25 -22
- package/components/data/Spreadsheet.d.ts +2 -0
- package/components/data/Spreadsheet.jsx +12 -11
- package/components/data/Spreadsheet.vue +14 -13
- package/components/data/Table.d.ts +47 -0
- package/components/data/Table.jsx +85 -0
- package/components/data/Table.vue +63 -0
- package/components/data/data.prompt.md +3 -3
- package/components/display/Avatar.vue +3 -2
- package/components/display/AvatarGroup.vue +3 -2
- package/components/display/Badge.vue +1 -1
- package/components/display/Divider.vue +3 -3
- package/components/display/Donut.vue +3 -2
- package/components/display/IconTile.vue +2 -1
- package/components/display/Kbd.vue +1 -1
- package/components/display/ProgressBar.jsx +1 -1
- package/components/display/ProgressBar.vue +1 -1
- package/components/display/StatCard.vue +1 -1
- package/components/display/Tag.d.ts +3 -1
- package/components/display/Tag.jsx +4 -4
- package/components/display/Tag.vue +4 -4
- package/components/feedback/Accordion.jsx +2 -2
- package/components/feedback/Accordion.vue +2 -2
- package/components/feedback/ActivityItem.d.ts +3 -1
- package/components/feedback/ActivityItem.jsx +5 -5
- package/components/feedback/ActivityItem.vue +5 -4
- package/components/feedback/Alert.jsx +4 -4
- package/components/feedback/Alert.vue +3 -3
- package/components/feedback/Banner.jsx +5 -5
- package/components/feedback/Banner.vue +5 -5
- package/components/feedback/ContextMenu.d.ts +3 -1
- package/components/feedback/ContextMenu.jsx +5 -5
- package/components/feedback/ContextMenu.vue +5 -5
- package/components/feedback/DotActivity.vue +3 -3
- package/components/feedback/Drawer.d.ts +2 -0
- package/components/feedback/Drawer.jsx +6 -6
- package/components/feedback/Drawer.vue +22 -15
- package/components/feedback/EmptyState.vue +2 -2
- package/components/feedback/LogViewer.vue +1 -1
- package/components/feedback/Modal.d.ts +2 -0
- package/components/feedback/Modal.jsx +12 -11
- package/components/feedback/Modal.vue +10 -8
- package/components/feedback/Skeleton.vue +2 -1
- package/components/feedback/Spinner.vue +2 -1
- package/components/feedback/Stepper.vue +1 -1
- package/components/feedback/Timeline.jsx +1 -1
- package/components/feedback/Timeline.vue +1 -1
- package/components/feedback/Toast.vue +4 -4
- package/components/feedback/ToastStack.vue +11 -0
- package/components/feedback/Tooltip.vue +19 -0
- package/components/feedback/feedback.prompt.md +4 -4
- package/components/forms/Checkbox.d.ts +3 -2
- package/components/forms/Checkbox.jsx +7 -7
- package/components/forms/Checkbox.vue +8 -8
- package/components/forms/Combobox.d.ts +2 -0
- package/components/forms/Combobox.jsx +12 -10
- package/components/forms/Combobox.vue +11 -10
- package/components/forms/DatePicker.d.ts +4 -0
- package/components/forms/DatePicker.jsx +16 -12
- package/components/forms/DatePicker.vue +15 -11
- package/components/forms/Dropzone.jsx +2 -2
- package/components/forms/Dropzone.vue +5 -5
- package/components/forms/Field.d.ts +5 -1
- package/components/forms/Field.jsx +4 -4
- package/components/forms/Field.vue +4 -4
- package/components/forms/Input.d.ts +5 -1
- package/components/forms/Input.jsx +7 -7
- package/components/forms/Input.vue +3 -3
- package/components/forms/MultiSelect.d.ts +2 -0
- package/components/forms/MultiSelect.jsx +13 -11
- package/components/forms/MultiSelect.vue +12 -11
- package/components/forms/NumberStepper.d.ts +3 -1
- package/components/forms/NumberStepper.jsx +7 -7
- package/components/forms/NumberStepper.vue +8 -7
- package/components/forms/Radio.d.ts +5 -3
- package/components/forms/Radio.jsx +13 -15
- package/components/forms/Radio.vue +9 -10
- package/components/forms/RadioGroup.vue +5 -5
- package/components/forms/SearchInput.d.ts +3 -1
- package/components/forms/SearchInput.jsx +6 -6
- package/components/forms/SearchInput.vue +7 -6
- package/components/forms/Select.d.ts +3 -2
- package/components/forms/Select.jsx +6 -7
- package/components/forms/Select.vue +6 -6
- package/components/forms/Slider.jsx +3 -3
- package/components/forms/Slider.vue +3 -3
- package/components/forms/Switch.d.ts +2 -2
- package/components/forms/Switch.jsx +20 -22
- package/components/forms/Switch.vue +15 -14
- package/components/forms/Textarea.vue +4 -4
- package/components/forms/forms.prompt.md +1 -1
- package/components/forms/input-base.js +2 -1
- package/components/index.d.ts +1 -2
- package/components/index.js +3 -4
- package/components/navigation/Breadcrumb.jsx +3 -3
- package/components/navigation/Breadcrumb.vue +4 -4
- package/components/navigation/DocTree.jsx +3 -3
- package/components/navigation/DocTree.vue +3 -3
- package/components/navigation/PageHeading.vue +1 -1
- package/components/navigation/PageToolbar.vue +7 -6
- package/components/navigation/Sidebar.jsx +1 -1
- package/components/navigation/Sidebar.vue +3 -3
- package/components/navigation/ThemeToggle.vue +2 -1
- package/components/navigation/TopBar.d.ts +3 -1
- package/components/navigation/TopBar.jsx +6 -6
- package/components/navigation/Tree.jsx +5 -5
- package/components/navigation/Tree.vue +5 -5
- package/components/pipeline/ConfigPanel.d.ts +2 -0
- package/components/pipeline/ConfigPanel.jsx +9 -9
- package/components/pipeline/ConfigPanel.vue +9 -9
- package/components/pipeline/DataPreview.d.ts +3 -1
- package/components/pipeline/DataPreview.jsx +7 -7
- package/components/pipeline/DataPreview.vue +7 -7
- package/components/pipeline/NodeCard.jsx +3 -3
- package/components/pipeline/NodeCard.vue +3 -3
- package/components/pipeline/PaletteItem.vue +2 -2
- package/components/pipeline/PaletteListItem.vue +1 -1
- package/components/pipeline/PalettePanel.jsx +2 -2
- package/components/pipeline/PalettePanel.vue +1 -1
- package/components/pipeline/Port.vue +15 -0
- package/components/pipeline/PortLabel.vue +15 -0
- package/components/pipeline/ZoomControls.vue +1 -1
- package/components/pipeline/port-skin.js +13 -0
- package/components/px.js +4 -0
- package/components/tabs/PanelTabs.vue +5 -4
- package/components/tabs/PillTabs.vue +5 -4
- package/components/tabs/SegmentedControl.d.ts +2 -2
- package/components/tabs/SegmentedControl.jsx +7 -7
- package/components/tabs/SegmentedControl.vue +7 -7
- package/components/tabs/Tabs.d.ts +5 -3
- package/components/tabs/Tabs.jsx +15 -15
- package/components/tabs/Tabs.vue +5 -5
- package/components/tabs/ViewToggle.d.ts +3 -1
- package/components/tabs/ViewToggle.jsx +5 -5
- package/components/tabs/ViewToggle.vue +5 -5
- package/components/tabs/tabs.prompt.md +1 -1
- package/keybricks.css +4 -0
- package/package.json +6 -3
- package/readme.md +90 -122
- package/readme.zh-CN.md +120 -0
- package/tokens/charts.css +20 -17
- package/tokens/colors.css +2 -2
- package/tokens/dark.css +2 -2
- package/tokens/hover.css +32 -0
- package/tokens/interaction.css +3 -1
- package/tokens/layout.css +29 -7
- package/tokens/tokens.d.ts +36 -0
- package/tokens/tokens.js +143 -89
- package/tokens/tokens.json +240 -106
- package/tokens/typography.css +27 -14
- package/components/_cards/brand-visual.card.html +0 -43
- package/components/_cards/datatable.card.html +0 -48
- package/components/_shared/src-brand.js +0 -1
- package/components/brand/BrandVisual.d.ts +0 -17
- package/components/brand/BrandVisual.jsx +0 -29
- package/components/brand/BrandVisual.vue +0 -34
- package/components/brand/_dir.card.html +0 -13
- package/components/brand/brandMotifs.js +0 -210
- package/components/buttons/_dir.card.html +0 -13
- package/components/charts/ChartKit.vue +0 -47
- package/components/charts/_dir.card.html +0 -7
- package/components/data/DataTable.d.ts +0 -28
- package/components/data/DataTable.jsx +0 -32
- package/components/data/DataTable.vue +0 -25
- package/components/data/_dir.card.html +0 -13
- package/components/display/_dir.card.html +0 -13
- package/components/feedback/_dir.card.html +0 -13
- package/components/forms/_dir.card.html +0 -13
- package/components/icons/_dir.card.html +0 -13
- package/components/navigation/_dir.card.html +0 -13
- package/components/pipeline/_dir.card.html +0 -13
- package/components/tabs/_dir.card.html +0 -13
|
@@ -8,11 +8,11 @@ export function Accordion({ title, subtitle, defaultOpen, open: openProp, onTogg
|
|
|
8
8
|
const toggle = () => (onToggle ? onToggle(!open) : setOpenState(!open));
|
|
9
9
|
return (
|
|
10
10
|
<div style={{ borderBottom: "1px solid var(--border-card)", ...style }}>
|
|
11
|
-
<div onClick={toggle} style={{ display: "flex", alignItems: "center", gap: 10, padding: "14px 4px", cursor: "pointer" }}>
|
|
11
|
+
<div onClick={toggle} data-wash="" style={{ display: "flex", alignItems: "center", gap: 10, padding: "14px 8px", margin: "0 -4px", borderRadius: "var(--radius-md)", background: "var(--wash, transparent)", transition: "var(--t-hover)", cursor: "pointer" }}>
|
|
12
12
|
<span style={{ color: "var(--text-muted)", display: "inline-flex", transform: open ? "rotate(90deg)" : "none", transition: "transform var(--dur-fast) var(--ease-out)" }}><Icon name="chevron-right" size={14} /></span>
|
|
13
13
|
<div style={{ display: "flex", flexDirection: "column", flex: 1 }}>
|
|
14
14
|
<span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", letterSpacing: "var(--tracking-base)", color: "var(--text-heading)" }}>{title}</span>
|
|
15
|
-
{subtitle ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
15
|
+
{subtitle ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-secondary)" }}>{subtitle}</span> : null}
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
|
18
18
|
{open ? <div style={{ padding: "0 4px 16px 28px" }}>{children}</div> : null}
|
|
@@ -10,11 +10,11 @@ function toggle() { if (props.open != null) emit('toggle', !open.value); else op
|
|
|
10
10
|
</script>
|
|
11
11
|
<template>
|
|
12
12
|
<div :style="{ borderBottom: '1px solid var(--border-card)', ...style }">
|
|
13
|
-
<div @click="toggle" style="display:flex;align-items:center;gap:10px;padding:14px 4px;cursor:pointer">
|
|
13
|
+
<div @click="toggle" data-wash="" style="display:flex;align-items:center;gap:10px;padding:14px 8px;margin:0 -4px;border-radius:var(--radius-md);background:var(--wash, transparent);transition:var(--t-hover);cursor:pointer">
|
|
14
14
|
<span :style="{ color: 'var(--text-muted)', display: 'inline-flex', transform: open ? 'rotate(90deg)' : 'none', transition: 'transform var(--dur-fast) var(--ease-out)' }"><Icon name="chevron-right" :size="14" /></span>
|
|
15
15
|
<div style="display:flex;flex-direction:column;flex:1">
|
|
16
16
|
<span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);letter-spacing:var(--tracking-base);color:var(--text-heading)">{{ title }}</span>
|
|
17
|
-
<span v-if="subtitle" style="font-family:var(--font-sans);font-size:var(--text-
|
|
17
|
+
<span v-if="subtitle" style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ subtitle }}</span>
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
20
20
|
<div v-if="open" style="padding:0 4px 16px 28px"><slot /></div>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
/** Feed row: 32px blue-tint icon tile,
|
|
2
|
+
/** Feed row: 32px blue-tint icon tile, 14px/500 title, 12px meta, right action text ("Open"). */
|
|
3
3
|
export interface ActivityItemProps {
|
|
4
4
|
icon?: string;
|
|
5
5
|
title?: string;
|
|
6
6
|
meta?: string;
|
|
7
7
|
action?: React.ReactNode;
|
|
8
|
+
/** Fires when the action text is clicked. Vue: `@action`. */
|
|
9
|
+
onAction?: () => void;
|
|
8
10
|
style?: React.CSSProperties;
|
|
9
11
|
}
|
|
10
12
|
export declare function ActivityItem(props: ActivityItemProps): JSX.Element;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { IconTile } from "../display/Badge.jsx";
|
|
3
3
|
|
|
4
|
-
/** Activity feed row: 32px icon tile +
|
|
5
|
-
export function ActivityItem({ icon = "file-text", title, meta, action, style }) {
|
|
4
|
+
/** Activity feed row: 32px icon tile + 14px/500 title + 12px meta + optional right text action (a text link: ink darkens on hover). */
|
|
5
|
+
export function ActivityItem({ icon = "file-text", title, meta, action, onAction, style }) {
|
|
6
6
|
return (
|
|
7
7
|
<div style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 16px", ...style }}>
|
|
8
8
|
<IconTile icon={icon} kind="row" style={{ background: "var(--info-bg)", border: "none", color: "var(--info-fg)" }} />
|
|
9
9
|
<div style={{ display: "flex", flexDirection: "column", flex: 1, minWidth: 0 }}>
|
|
10
10
|
<span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: "var(--text-heading)" }}>{title}</span>
|
|
11
|
-
{meta ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
11
|
+
{meta ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", color: "var(--text-secondary)" }}>{meta}</span> : null}
|
|
12
12
|
</div>
|
|
13
|
-
{action ? <
|
|
13
|
+
{action ? <button type="button" onClick={onAction} data-ink="" style={{ border: "none", background: "transparent", padding: 0, fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: "var(--ink, var(--text-secondary))", cursor: "pointer", whiteSpace: "nowrap" }}>{action}</button> : null}
|
|
14
14
|
</div>
|
|
15
15
|
);
|
|
16
16
|
}
|
|
@@ -22,7 +22,7 @@ export function DotActivity({ tone = "success", text, time, style }) {
|
|
|
22
22
|
<div style={{ display: "flex", gap: 8, alignItems: "flex-start", ...style }}>
|
|
23
23
|
<span style={{ width: 8, height: 8, borderRadius: "var(--radius-full)", background: tones[tone], marginTop: 5, flexShrink: 0 }} />
|
|
24
24
|
<div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
|
|
25
|
-
<span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
25
|
+
<span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", color: "var(--text-heading)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{text}</span>
|
|
26
26
|
<span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", letterSpacing: "var(--tracking-xs)", color: "var(--text-muted)" }}>{time}</span>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import IconTile from '../display/Badge.vue'
|
|
3
|
-
// Activity feed row: 32px icon tile +
|
|
3
|
+
// Activity feed row: 32px icon tile + 14px/500 title + 12px meta + optional right text action (emits `action`).
|
|
4
4
|
defineProps({ icon: { type: String, default: 'file-text' }, title: String, meta: String, action: String, style: Object })
|
|
5
|
+
const emit = defineEmits(['action'])
|
|
5
6
|
</script>
|
|
6
7
|
<template>
|
|
7
|
-
<div :style="{ display: 'flex', alignItems: 'center', gap:
|
|
8
|
+
<div :style="{ display: 'flex', alignItems: 'center', gap: '12px', padding: '12px 16px', ...style }">
|
|
8
9
|
<IconTile :icon="icon" kind="row" style="background:var(--info-bg);border:none;color:var(--info-fg)" />
|
|
9
10
|
<div style="display:flex;flex-direction:column;flex:1;min-width:0">
|
|
10
11
|
<span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-heading)">{{ title }}</span>
|
|
11
|
-
<span v-if="meta" style="font-family:var(--font-sans);font-size:var(--text-
|
|
12
|
+
<span v-if="meta" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);color:var(--text-secondary)">{{ meta }}</span>
|
|
12
13
|
</div>
|
|
13
|
-
<
|
|
14
|
+
<button v-if="action" type="button" data-ink="" @click="emit('action')" style="border:none;background:transparent;padding:0;font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--ink, var(--text-secondary));cursor:pointer;white-space:nowrap">{{ action }}</button>
|
|
14
15
|
</div>
|
|
15
16
|
</template>
|
|
@@ -8,15 +8,15 @@ const alertTones = {
|
|
|
8
8
|
danger: { bg: "var(--danger-bg)", fg: "var(--danger-text)", icon: "triangle-alert" },
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
/** Inline banner — tinted, no border
|
|
11
|
+
/** Inline banner — tinted, no border; title and body at the base size. The icon is centred on the first text line, never on the block. */
|
|
12
12
|
export function Alert({ tone = "info", title, children, action, style }) {
|
|
13
13
|
const t = alertTones[tone];
|
|
14
14
|
return (
|
|
15
15
|
<div style={{ display: "flex", gap: 10, alignItems: "flex-start", background: t.bg, borderRadius: "var(--radius-md)", padding: "10px 12px", ...style }}>
|
|
16
|
-
<span style={{ color: t.fg, display: "inline-flex",
|
|
16
|
+
<span style={{ color: t.fg, display: "inline-flex", alignItems: "center", flexShrink: 0, height: "var(--leading-base)" }}><Icon name={t.icon} size={15} /></span>
|
|
17
17
|
<div style={{ display: "flex", flexDirection: "column", gap: 2, flex: 1, minWidth: 0 }}>
|
|
18
18
|
{title ? <span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: t.fg }}>{title}</span> : null}
|
|
19
|
-
{children ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
19
|
+
{children ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", color: "var(--text-body)" }}>{children}</span> : null}
|
|
20
20
|
</div>
|
|
21
21
|
{action}
|
|
22
22
|
</div>
|
|
@@ -29,7 +29,7 @@ export function EmptyState({ icon = "file-text", title, hint, action, style }) {
|
|
|
29
29
|
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 6, padding: "40px 24px", textAlign: "center", ...style }}>
|
|
30
30
|
<span style={{ color: "var(--text-muted)", marginBottom: 6 }}><Icon name={icon} size={22} strokeWidth={1.5} /></span>
|
|
31
31
|
<span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: "var(--text-heading)" }}>{title}</span>
|
|
32
|
-
{hint ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
32
|
+
{hint ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", color: "var(--text-secondary)", maxWidth: 320 }}>{hint}</span> : null}
|
|
33
33
|
{action ? <span style={{ paddingTop: 10 }}>{action}</span> : null}
|
|
34
34
|
</div>
|
|
35
35
|
);
|
|
@@ -12,11 +12,11 @@ const props = defineProps({ tone: { type: String, default: 'info' }, title: Stri
|
|
|
12
12
|
const t = computed(() => alertTones[props.tone])
|
|
13
13
|
</script>
|
|
14
14
|
<template>
|
|
15
|
-
<div :style="{ display: 'flex', gap:
|
|
16
|
-
<span :style="{ color: t.fg, display: 'inline-flex',
|
|
15
|
+
<div :style="{ display: 'flex', gap: '10px', alignItems: 'flex-start', background: t.bg, borderRadius: 'var(--radius-md)', padding: '10px 12px', ...style }">
|
|
16
|
+
<span :style="{ color: t.fg, display: 'inline-flex', alignItems: 'center', flexShrink: 0, height: 'var(--leading-base)' }"><Icon :name="t.icon" :size="15" /></span>
|
|
17
17
|
<div style="display:flex;flex-direction:column;gap:2px;flex:1;min-width:0">
|
|
18
18
|
<span v-if="title" :style="{ fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-base)', lineHeight: 'var(--leading-base)', letterSpacing: 'var(--tracking-base)', color: t.fg }">{{ title }}</span>
|
|
19
|
-
<span style="font-family:var(--font-sans);font-size:var(--text-
|
|
19
|
+
<span style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-body)"><slot /></span>
|
|
20
20
|
</div>
|
|
21
21
|
<slot name="action" />
|
|
22
22
|
</div>
|
|
@@ -13,13 +13,13 @@ const bannerTones = {
|
|
|
13
13
|
export function Banner({ tone = "info", title, action, onAction, dismissible, onDismiss, children, style }) {
|
|
14
14
|
const t = bannerTones[tone];
|
|
15
15
|
return (
|
|
16
|
-
<div style={{ display: "flex", alignItems: "
|
|
17
|
-
<span style={{ color: t.fg, display: "inline-flex", flexShrink: 0 }}><Icon name={t.icon} size={16} /></span>
|
|
18
|
-
<span style={{ flex: 1, minWidth: 0, fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
16
|
+
<div style={{ display: "flex", alignItems: "flex-start", gap: 10, background: t.bg, borderLeft: `3px solid ${t.fg}`, padding: "10px 12px 10px 14px", boxSizing: "border-box", ...style }}>
|
|
17
|
+
<span style={{ color: t.fg, display: "inline-flex", alignItems: "center", flexShrink: 0, height: "var(--leading-base)" }}><Icon name={t.icon} size={16} /></span>
|
|
18
|
+
<span style={{ flex: 1, minWidth: 0, fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", color: "var(--text-body)" }}>
|
|
19
19
|
{title && <strong style={{ fontWeight: "var(--weight-medium)", color: t.fg, paddingRight: 6 }}>{title}</strong>}{children}
|
|
20
20
|
</span>
|
|
21
|
-
{action && <button type="button" onClick={onAction} style={{ border: "none", background: "transparent", cursor: "pointer", padding: 0, fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-
|
|
22
|
-
{dismissible && <IconButton icon="x" size="
|
|
21
|
+
{action && <button type="button" onClick={onAction} data-link="" style={{ border: "none", background: "transparent", cursor: "pointer", padding: 0, height: "var(--leading-base)", fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", color: t.fg, textDecorationLine: "underline", textDecorationThickness: "var(--link-thick, 1px)", textUnderlineOffset: 2, whiteSpace: "nowrap", flexShrink: 0 }}>{action}</button>}
|
|
22
|
+
{dismissible && <IconButton icon="x" size="sm" muted label="Dismiss" onClick={onDismiss} style={{ margin: "-2px 0" }} />}
|
|
23
23
|
</div>
|
|
24
24
|
);
|
|
25
25
|
}
|
|
@@ -14,12 +14,12 @@ const emit = defineEmits(['action', 'dismiss'])
|
|
|
14
14
|
const t = computed(() => bannerTones[props.tone])
|
|
15
15
|
</script>
|
|
16
16
|
<template>
|
|
17
|
-
<div :style="{ display: 'flex', alignItems: '
|
|
18
|
-
<span :style="{ color: t.fg, display: 'inline-flex', flexShrink: 0 }"><Icon :name="t.icon" :size="16" /></span>
|
|
19
|
-
<span style="flex:1;min-width:0;font-family:var(--font-sans);font-size:var(--text-
|
|
17
|
+
<div :style="{ display: 'flex', alignItems: 'flex-start', gap: '10px', background: t.bg, borderLeft: `3px solid ${t.fg}`, padding: '10px 12px 10px 14px', boxSizing: 'border-box', ...style }">
|
|
18
|
+
<span :style="{ color: t.fg, display: 'inline-flex', alignItems: 'center', flexShrink: 0, height: 'var(--leading-base)' }"><Icon :name="t.icon" :size="16" /></span>
|
|
19
|
+
<span style="flex:1;min-width:0;font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-body)">
|
|
20
20
|
<strong v-if="title" :style="{ fontWeight: 'var(--weight-medium)', color: t.fg, paddingRight: '6px' }">{{ title }}</strong><slot />
|
|
21
21
|
</span>
|
|
22
|
-
<button v-if="action" type="button" @click="emit('action')" :style="{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 0, fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-
|
|
23
|
-
<IconButton v-if="dismissible" icon="x" size="
|
|
22
|
+
<button v-if="action" type="button" data-link="" @click="emit('action')" :style="{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 0, height: 'var(--leading-base)', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-base)', color: t.fg, textDecorationLine: 'underline', textDecorationThickness: 'var(--link-thick, 1px)', textUnderlineOffset: '2px', whiteSpace: 'nowrap', flexShrink: 0 }">{{ action }}</button>
|
|
23
|
+
<IconButton v-if="dismissible" icon="x" size="sm" muted label="Dismiss" :style="{ margin: '-2px 0' }" @click="emit('dismiss')" />
|
|
24
24
|
</div>
|
|
25
25
|
</template>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
/** Floating context menu — r10, soft shadow,
|
|
2
|
+
/** Floating context menu — r10, soft shadow, control-height rows at the region's tier (32px / 14px at md), red danger row. */
|
|
3
3
|
export interface ContextMenuProps {
|
|
4
4
|
items?: { label?: string; icon?: string; danger?: boolean; divider?: boolean }[];
|
|
5
5
|
onSelect?: (label: string) => void;
|
|
6
|
+
/** Size tier of the rows. Omit to inherit the tier of the control that opened the menu (the default). */
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
6
8
|
style?: React.CSSProperties;
|
|
7
9
|
}
|
|
8
10
|
export declare function ContextMenu(props: ContextMenuProps): JSX.Element;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Icon } from "../icons/Icon.jsx";
|
|
3
3
|
|
|
4
|
-
/** Right-click context menu (Preview / Duplicate / Delete). items: [{label, icon, danger, divider}]. */
|
|
5
|
-
export function ContextMenu({ items = [], onSelect, style }) {
|
|
4
|
+
/** Right-click context menu (Preview / Duplicate / Delete). items: [{label, icon, danger, divider}]. Rows are control-height at the region's tier (a menu inherits the tier of the control that opened it); `size` overrides. */
|
|
5
|
+
export function ContextMenu({ items = [], onSelect, size, style }) {
|
|
6
6
|
const [hover, setHover] = React.useState(-1);
|
|
7
7
|
return (
|
|
8
|
-
<div style={{ background: "var(--bg-surface)", borderRadius: "var(--radius-lg)", border: "1px solid var(--border-card)", boxShadow: "var(--shadow-menu)", padding: 4, minWidth: 148, display: "inline-flex", flexDirection: "column", ...style }}>
|
|
8
|
+
<div role="menu" data-size={size} style={{ background: "var(--bg-surface)", borderRadius: "var(--radius-lg)", border: "1px solid var(--border-card)", boxShadow: "var(--shadow-menu)", padding: 4, minWidth: 148, display: "inline-flex", flexDirection: "column", ...style }}>
|
|
9
9
|
{items.map((it, i) => it.divider ? <div key={i} style={{ height: 1, background: "var(--border-subtle)", margin: "4px 0" }} /> : (
|
|
10
10
|
<button key={i} type="button" onClick={() => onSelect && onSelect(it.label)}
|
|
11
11
|
onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(-1)}
|
|
12
|
-
style={{ display: "flex", alignItems: "center", gap: 10, padding: "
|
|
13
|
-
{it.icon ? <Icon name={it.icon} size={
|
|
12
|
+
role="menuitem" style={{ display: "flex", alignItems: "center", gap: 10, height: "var(--control-h)", padding: "0 10px", boxSizing: "border-box", borderRadius: "var(--radius-md)", border: "none", cursor: "pointer", background: hover === i ? "var(--bg-hover)" : "transparent", fontFamily: "var(--font-sans)", fontSize: "var(--control-text)", lineHeight: "var(--control-leading)", letterSpacing: "var(--control-tracking)", color: it.danger ? "var(--danger)" : "var(--text-heading)", textAlign: "left", whiteSpace: "nowrap" }}>
|
|
13
|
+
{it.icon ? <Icon name={it.icon} size={16} style={{ width: "var(--control-icon)", height: "var(--control-icon)" }} /> : null}{it.label}
|
|
14
14
|
</button>
|
|
15
15
|
))}
|
|
16
16
|
</div>
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref } from 'vue'
|
|
3
3
|
import Icon from '../icons/Icon.vue'
|
|
4
|
-
// Right-click context menu (Preview / Duplicate / Delete). items: [{label, icon, danger, divider}].
|
|
5
|
-
defineProps({ items: { type: Array, default: () => [] }, style: Object })
|
|
4
|
+
// Right-click context menu (Preview / Duplicate / Delete). items: [{label, icon, danger, divider}]. Rows are control-height at the region's tier (a menu inherits the tier of the control that opened it); `size` overrides.
|
|
5
|
+
defineProps({ items: { type: Array, default: () => [] }, size: String, style: Object })
|
|
6
6
|
const emit = defineEmits(['select'])
|
|
7
7
|
const hover = ref(-1)
|
|
8
8
|
</script>
|
|
9
9
|
<template>
|
|
10
|
-
<div :style="{ background: 'var(--bg-surface)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-card)', boxShadow: 'var(--shadow-menu)', padding:
|
|
10
|
+
<div role="menu" :data-size="size" :style="{ background: 'var(--bg-surface)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-card)', boxShadow: 'var(--shadow-menu)', padding: '4px', minWidth: '148px', display: 'inline-flex', flexDirection: 'column', ...style }">
|
|
11
11
|
<template v-for="(it, i) in items" :key="i">
|
|
12
12
|
<div v-if="it.divider" style="height:1px;background:var(--border-subtle);margin:4px 0" />
|
|
13
13
|
<button v-else type="button" @click="emit('select', it.label)" @mouseenter="hover = i" @mouseleave="hover = -1"
|
|
14
|
-
:style="{ display: 'flex', alignItems: 'center', gap:
|
|
15
|
-
<Icon v-if="it.icon" :name="it.icon" :size="
|
|
14
|
+
role="menuitem" :style="{ display: 'flex', alignItems: 'center', gap: '10px', height: 'var(--control-h)', padding: '0 10px', boxSizing: 'border-box', borderRadius: 'var(--radius-md)', border: 'none', cursor: 'pointer', background: hover === i ? 'var(--bg-hover)' : 'transparent', fontFamily: 'var(--font-sans)', fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', color: it.danger ? 'var(--danger)' : 'var(--text-heading)', textAlign: 'left', whiteSpace: 'nowrap' }">
|
|
15
|
+
<Icon v-if="it.icon" :name="it.icon" :size="16" :style="{ width: 'var(--control-icon)', height: 'var(--control-icon)' }" />{{ it.label }}
|
|
16
16
|
</button>
|
|
17
17
|
</template>
|
|
18
18
|
</div>
|
|
@@ -5,10 +5,10 @@ const props = defineProps({ tone: { type: String, default: 'success' }, text: St
|
|
|
5
5
|
const dot = computed(() => tonesMap[props.tone])
|
|
6
6
|
</script>
|
|
7
7
|
<template>
|
|
8
|
-
<div :style="{ display: 'flex', gap:
|
|
9
|
-
<span :style="{ width:
|
|
8
|
+
<div :style="{ display: 'flex', gap: '8px', alignItems: 'flex-start', ...style }">
|
|
9
|
+
<span :style="{ width: '8px', height: '8px', borderRadius: 'var(--radius-full)', background: dot, marginTop: '5px', flexShrink: 0 }" />
|
|
10
10
|
<div style="display:flex;flex-direction:column;min-width:0">
|
|
11
|
-
<span style="font-family:var(--font-sans);font-size:var(--text-
|
|
11
|
+
<span style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-heading);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ text }}</span>
|
|
12
12
|
<span style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);letter-spacing:var(--tracking-xs);color:var(--text-muted)">{{ time }}</span>
|
|
13
13
|
</div>
|
|
14
14
|
</div>
|
|
@@ -11,6 +11,8 @@ export interface DrawerProps {
|
|
|
11
11
|
inline?: boolean;
|
|
12
12
|
/** Inline height (default 420) */
|
|
13
13
|
height?: number | string;
|
|
14
|
+
/** Size tier of the drawer region. Omit to inherit (→ md): drawers are working surfaces, not dialogs. */
|
|
15
|
+
size?: "sm" | "md" | "lg";
|
|
14
16
|
footer?: React.ReactNode;
|
|
15
17
|
onClose?: () => void;
|
|
16
18
|
children?: React.ReactNode;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { IconButton } from "../buttons/IconButton.jsx";
|
|
3
3
|
|
|
4
|
-
/** Right slide-out panel over a scrim:
|
|
5
|
-
export function Drawer({ open = true, title, subtitle, width = 400, inline, height, footer, onClose, children, style }) {
|
|
4
|
+
/** Right slide-out panel over a scrim: title + subtitle, scrollable body, right-aligned footer slot. inline=true renders it in-flow for embedding in cards/demos. A size region (default inherit → md: drawers are working surfaces, not dialogs); pass `size` to override. */
|
|
5
|
+
export function Drawer({ open = true, title, subtitle, width = 400, inline, height, size, footer, onClose, children, style }) {
|
|
6
6
|
if (!open) return null;
|
|
7
7
|
const panel = (
|
|
8
|
-
<div style={{ width, maxWidth: "100%", height: inline ? (height || 420) : "100%", background: "var(--bg-raised)", borderLeft: "1px solid var(--border-card)", boxShadow: inline ? "var(--shadow-menu)" : "-8px 0 24px rgba(0,0,0,.12)", display: "flex", flexDirection: "column", boxSizing: "border-box", ...(inline ? { border: "1px solid var(--border-card)", borderRadius: "var(--radius-lg)" } : null), ...style }}>
|
|
8
|
+
<div role="dialog" aria-label={title} data-size={size} style={{ width, maxWidth: "100%", height: inline ? (height || 420) : "100%", background: "var(--bg-raised)", borderLeft: "1px solid var(--border-card)", boxShadow: inline ? "var(--shadow-menu)" : "-8px 0 24px rgba(0,0,0,.12)", display: "flex", flexDirection: "column", boxSizing: "border-box", ...(inline ? { border: "1px solid var(--border-card)", borderRadius: "var(--radius-lg)" } : null), ...style }}>
|
|
9
9
|
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", padding: "16px 16px 12px", borderBottom: "1px solid var(--border-card)", flexShrink: 0 }}>
|
|
10
10
|
<div style={{ display: "flex", flexDirection: "column", gap: 3 }}>
|
|
11
11
|
<span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-semibold)", fontSize: "var(--text-md)", lineHeight: "var(--leading-md)", letterSpacing: "var(--tracking-md)", color: "var(--text-heading)" }}>{title}</span>
|
|
12
|
-
{subtitle && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
12
|
+
{subtitle && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", color: "var(--text-secondary)" }}>{subtitle}</span>}
|
|
13
13
|
</div>
|
|
14
|
-
<IconButton icon="x"
|
|
14
|
+
<IconButton icon="x" label="Close" onClick={onClose} style={{ margin: "calc((var(--leading-md) - var(--control-h)) / 2) -6px 0 0" }} />
|
|
15
15
|
</div>
|
|
16
16
|
<div style={{ flex: 1, overflowY: "auto", padding: 16 }}>{children}</div>
|
|
17
|
-
{footer && <div style={{ display: "flex", justifyContent: "flex-end", gap:
|
|
17
|
+
{footer && <div style={{ display: "flex", justifyContent: "flex-end", gap: "var(--control-gap)", padding: "12px 16px", borderTop: "1px solid var(--border-card)", flexShrink: 0 }}>{footer}</div>}
|
|
18
18
|
</div>
|
|
19
19
|
);
|
|
20
20
|
if (inline) return panel;
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { px } from '../px.js'
|
|
2
3
|
import IconButton from '../buttons/IconButton.vue'
|
|
3
|
-
// Right slide-out panel over a scrim
|
|
4
|
-
|
|
4
|
+
// Right slide-out panel over a scrim: title + subtitle, scrollable body, right-aligned footer slot.
|
|
5
|
+
// inline=true renders it in-flow (cards, demos); otherwise it teleports to <body> over a scrim. Click the scrim or × to emit close.
|
|
6
|
+
// A size region (default inherit → md: drawers are working surfaces, not dialogs); pass `size` to override.
|
|
7
|
+
const props = defineProps({ open: { type: Boolean, default: true }, title: String, subtitle: String, width: { type: [Number, String], default: 400 }, inline: Boolean, height: { type: [Number, String], default: 420 }, size: String, style: Object })
|
|
5
8
|
const emit = defineEmits(['close'])
|
|
9
|
+
const contents = { display: 'contents' }
|
|
6
10
|
</script>
|
|
7
11
|
<template>
|
|
8
|
-
<
|
|
9
|
-
<div v-if="
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
<Teleport to="body" :disabled="inline">
|
|
13
|
+
<div v-if="open" @click="!inline && emit('close')"
|
|
14
|
+
:style="inline ? contents : { position: 'fixed', inset: 0, zIndex: 'var(--z-modal)', background: 'var(--bg-scrim)', display: 'flex', justifyContent: 'flex-end', animation: 'kb-fade-in var(--dur-fast) var(--ease-out)' }">
|
|
15
|
+
<div @click.stop :style="inline ? contents : { height: '100%', animation: 'kb-slide-in var(--dur) var(--ease-out)' }">
|
|
16
|
+
<div role="dialog" :aria-label="title" :data-size="size"
|
|
17
|
+
:style="{ width: px(width), maxWidth: '100%', height: inline ? px(height) : '100%', background: 'var(--bg-raised)', borderLeft: '1px solid var(--border-card)', boxShadow: inline ? 'var(--shadow-menu)' : '-8px 0 24px rgba(0,0,0,.12)', display: 'flex', flexDirection: 'column', boxSizing: 'border-box', ...(inline ? { border: '1px solid var(--border-card)', borderRadius: 'var(--radius-lg)' } : null), ...style }">
|
|
18
|
+
<div style="display:flex;align-items:flex-start;justify-content:space-between;padding:16px 16px 12px;border-bottom:1px solid var(--border-card);flex-shrink:0">
|
|
19
|
+
<div style="display:flex;flex-direction:column;gap:3px">
|
|
20
|
+
<span style="font-family:var(--font-sans);font-weight:var(--weight-semibold);font-size:var(--text-md);line-height:var(--leading-md);letter-spacing:var(--tracking-md);color:var(--text-heading)">{{ title }}</span>
|
|
21
|
+
<span v-if="subtitle" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);color:var(--text-secondary)">{{ subtitle }}</span>
|
|
22
|
+
</div>
|
|
23
|
+
<IconButton icon="x" label="Close" :style="{ margin: 'calc((var(--leading-md) - var(--control-h)) / 2) -6px 0 0' }" @click="emit('close')" />
|
|
24
|
+
</div>
|
|
25
|
+
<div style="flex:1;overflow-y:auto;padding:16px"><slot /></div>
|
|
26
|
+
<div v-if="$slots.footer" style="display:flex;justify-content:flex-end;gap:var(--control-gap);padding:12px 16px;border-top:1px solid var(--border-card);flex-shrink:0"><slot name="footer" /></div>
|
|
17
27
|
</div>
|
|
18
|
-
<IconButton icon="x" size="md" label="Close" @click="emit('close')" />
|
|
19
28
|
</div>
|
|
20
|
-
<div style="flex:1;overflow-y:auto;padding:16px"><slot /></div>
|
|
21
|
-
<div v-if="$slots.footer" style="display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--border-card);flex-shrink:0"><slot name="footer" /></div>
|
|
22
29
|
</div>
|
|
23
|
-
</
|
|
30
|
+
</Teleport>
|
|
24
31
|
</template>
|
|
@@ -3,10 +3,10 @@ import Icon from '../icons/Icon.vue'
|
|
|
3
3
|
defineProps({ icon: { type: String, default: 'file-text' }, title: String, hint: String, style: Object })
|
|
4
4
|
</script>
|
|
5
5
|
<template>
|
|
6
|
-
<div :style="{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap:
|
|
6
|
+
<div :style="{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '6px', padding: '40px 24px', textAlign: 'center', ...style }">
|
|
7
7
|
<span style="color:var(--text-muted);margin-bottom:6px"><Icon :name="icon" :size="22" :stroke-width="1.5" /></span>
|
|
8
8
|
<span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-heading)">{{ title }}</span>
|
|
9
|
-
<span v-if="hint" style="font-family:var(--font-sans);font-size:var(--text-
|
|
9
|
+
<span v-if="hint" style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-secondary);max-width:320px">{{ hint }}</span>
|
|
10
10
|
<span v-if="$slots.action" style="padding-top:10px"><slot name="action" /></span>
|
|
11
11
|
</div>
|
|
12
12
|
</template>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
defineProps({ lines: { type: Array, default: () => [] }, height: [Number, String], style: Object })
|
|
3
3
|
</script>
|
|
4
4
|
<template>
|
|
5
|
-
<div :style="{ background: 'var(--bg-inset)', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-subtle)', padding: '10px 12px', display: 'flex', flexDirection: 'column', gap:
|
|
5
|
+
<div :style="{ background: 'var(--bg-inset)', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-subtle)', padding: '10px 12px', display: 'flex', flexDirection: 'column', gap: '6px', overflowY: 'auto', height, boxSizing: 'border-box', ...style }">
|
|
6
6
|
<span v-for="(l, i) in lines" :key="i" :style="{ fontFamily: 'var(--font-mono)', fontSize: 'var(--text-xs)', lineHeight: 'var(--leading-xs)', color: typeof l === 'object' ? (l.color || 'var(--text-body)') : 'var(--text-body)', whiteSpace: 'pre' }">{{ typeof l === 'object' ? l.text : l }}</span>
|
|
7
7
|
</div>
|
|
8
8
|
</template>
|
|
@@ -16,6 +16,8 @@ export interface ModalProps {
|
|
|
16
16
|
/** Right-aligned footer buttons */
|
|
17
17
|
footer?: React.ReactNode;
|
|
18
18
|
width?: number;
|
|
19
|
+
/** Size tier of the dialog region. Defaults to "lg" (36px controls) regardless of the page behind it — a dialog does not compete for space. */
|
|
20
|
+
size?: "sm" | "md" | "lg";
|
|
19
21
|
children?: React.ReactNode;
|
|
20
22
|
style?: React.CSSProperties;
|
|
21
23
|
}
|
|
@@ -22,21 +22,22 @@ export function Tooltip({ label, placement = "top", children, style }) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** Centered modal dialog with scrim. width defaults to 440. */
|
|
25
|
-
|
|
25
|
+
/** Modal dialog. A size region that defaults to lg (36px controls) whatever the page behind it uses — a dialog does not compete for space; pass `size` to override. 24px padding, footer buttons 8px apart. */
|
|
26
|
+
export function Modal({ open, title, subtitle, onClose, footer, width = 440, size = "lg", children, style }) {
|
|
26
27
|
if (!open) return null;
|
|
27
28
|
return (
|
|
28
29
|
<div style={{ position: "fixed", inset: 0, zIndex: "var(--z-modal)", background: "var(--bg-scrim)", display: "flex", alignItems: "center", justifyContent: "center", animation: "kb-fade-in var(--dur-fast) var(--ease-out)" }} onClick={onClose}>
|
|
29
|
-
<div onClick={(e) => e.stopPropagation()}
|
|
30
|
+
<div role="dialog" aria-modal="true" data-size={size} onClick={(e) => e.stopPropagation()}
|
|
30
31
|
style={{ width, maxWidth: "92vw", background: "var(--bg-raised)", borderRadius: "var(--radius-lg)", border: "1px solid var(--border-card)", boxShadow: "var(--shadow-modal)", animation: "kb-rise var(--dur) var(--ease-out)", ...style }}>
|
|
31
|
-
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", padding: "
|
|
32
|
-
<div style={{ display: "flex", flexDirection: "column", gap:
|
|
32
|
+
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 16, padding: "24px 24px 0" }}>
|
|
33
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
|
33
34
|
<span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-semibold)", fontSize: "var(--text-md)", lineHeight: "var(--leading-md)", letterSpacing: "var(--tracking-md)", color: "var(--text-heading)" }}>{title}</span>
|
|
34
|
-
{subtitle ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
35
|
+
{subtitle ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", color: "var(--text-secondary)" }}>{subtitle}</span> : null}
|
|
35
36
|
</div>
|
|
36
|
-
<IconButton icon="x"
|
|
37
|
+
<IconButton icon="x" onClick={onClose} label="Close" style={{ margin: "calc((var(--leading-md) - var(--control-h)) / 2) -6px 0 0" }} />
|
|
37
38
|
</div>
|
|
38
|
-
{children ? <div style={{ padding: "
|
|
39
|
-
<div style={{ display: "flex", justifyContent: "flex-end", gap:
|
|
39
|
+
{children ? <div style={{ padding: "20px 24px 0" }}>{children}</div> : null}
|
|
40
|
+
<div style={{ display: "flex", justifyContent: "flex-end", gap: "var(--control-gap)", padding: 24 }}>{footer}</div>
|
|
40
41
|
</div>
|
|
41
42
|
</div>
|
|
42
43
|
);
|
|
@@ -49,12 +50,12 @@ export function Toast({ tone = "success", title, meta, onClose, style }) {
|
|
|
49
50
|
const t = toastTones[tone];
|
|
50
51
|
return (
|
|
51
52
|
<div style={{ display: "flex", alignItems: "flex-start", gap: 10, minWidth: 280, maxWidth: 380, background: "var(--bg-raised)", border: "1px solid var(--border-card)", borderRadius: "var(--radius-lg)", boxShadow: "var(--shadow-menu)", padding: "12px 12px 12px 14px", animation: "kb-toast-in var(--dur) var(--ease-out)", ...style }}>
|
|
52
|
-
<span style={{ color: t.color, display: "inline-flex",
|
|
53
|
+
<span style={{ color: t.color, display: "inline-flex", alignItems: "center", flexShrink: 0, height: "var(--leading-base)" }}><Icon name={t.icon} size={16} /></span>
|
|
53
54
|
<div style={{ display: "flex", flexDirection: "column", flex: 1, minWidth: 0 }}>
|
|
54
55
|
<span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: "var(--text-heading)" }}>{title}</span>
|
|
55
|
-
{meta ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
56
|
+
{meta ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", color: "var(--text-secondary)" }}>{meta}</span> : null}
|
|
56
57
|
</div>
|
|
57
|
-
{onClose ? <IconButton icon="x" size="
|
|
58
|
+
{onClose ? <IconButton icon="x" size="sm" muted onClick={onClose} label="Dismiss" style={{ margin: "-2px 0" }} /> : null}
|
|
58
59
|
</div>
|
|
59
60
|
);
|
|
60
61
|
}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { px } from '../px.js'
|
|
2
3
|
import IconButton from '../buttons/IconButton.vue'
|
|
3
|
-
|
|
4
|
+
// Modal dialog. A size region that defaults to lg (36px controls) whatever the page behind it uses — a dialog does not compete for space; pass `size` to override. 24px padding, footer buttons 8px apart.
|
|
5
|
+
defineProps({ open: Boolean, title: String, subtitle: String, width: { type: [Number, String], default: 440 }, size: { type: String, default: 'lg' }, style: Object })
|
|
4
6
|
const emit = defineEmits(['close'])
|
|
5
7
|
</script>
|
|
6
8
|
<template>
|
|
7
9
|
<div v-if="open" style="position:fixed;inset:0;z-index:var(--z-modal);background:var(--bg-scrim);display:flex;align-items:center;justify-content:center;animation:kb-fade-in var(--dur-fast) var(--ease-out)" @click="emit('close')">
|
|
8
|
-
<div @click.stop :style="{ width, maxWidth: '92vw', background: 'var(--bg-raised)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-card)', boxShadow: 'var(--shadow-modal)', animation: 'kb-rise var(--dur) var(--ease-out)', ...style }">
|
|
9
|
-
<div style="display:flex;align-items:flex-start;justify-content:space-between;padding:
|
|
10
|
-
<div style="display:flex;flex-direction:column;gap:
|
|
10
|
+
<div role="dialog" aria-modal="true" :data-size="size" @click.stop :style="{ width: px(width), maxWidth: '92vw', background: 'var(--bg-raised)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-card)', boxShadow: 'var(--shadow-modal)', animation: 'kb-rise var(--dur) var(--ease-out)', ...style }">
|
|
11
|
+
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:24px 24px 0">
|
|
12
|
+
<div style="display:flex;flex-direction:column;gap:2px">
|
|
11
13
|
<span style="font-family:var(--font-sans);font-weight:var(--weight-semibold);font-size:var(--text-md);line-height:var(--leading-md);letter-spacing:var(--tracking-md);color:var(--text-heading)">{{ title }}</span>
|
|
12
|
-
<span v-if="subtitle" style="font-family:var(--font-sans);font-size:var(--text-
|
|
14
|
+
<span v-if="subtitle" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);color:var(--text-secondary)">{{ subtitle }}</span>
|
|
13
15
|
</div>
|
|
14
|
-
<IconButton icon="x"
|
|
16
|
+
<IconButton icon="x" label="Close" :style="{ margin: 'calc((var(--leading-md) - var(--control-h)) / 2) -6px 0 0' }" @click="emit('close')" />
|
|
15
17
|
</div>
|
|
16
|
-
<div v-if="$slots.default" style="padding:
|
|
17
|
-
<div style="display:flex;justify-content:flex-end;gap:
|
|
18
|
+
<div v-if="$slots.default" style="padding:20px 24px 0"><slot /></div>
|
|
19
|
+
<div style="display:flex;justify-content:flex-end;gap:var(--control-gap);padding:24px"><slot name="footer" /></div>
|
|
18
20
|
</div>
|
|
19
21
|
</div>
|
|
20
22
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { px } from '../px.js'
|
|
2
3
|
defineProps({ width: { type: [String, Number], default: '100%' }, height: { type: [String, Number], default: 12 }, radius: { type: String, default: 'var(--radius-sm)' }, style: Object })
|
|
3
4
|
</script>
|
|
4
5
|
<template>
|
|
5
|
-
<span :style="{ display: 'block', width, height, borderRadius: radius, background: 'var(--skeleton)', animation: 'kb-pulse 1.4s var(--ease-in-out) infinite', ...style }" />
|
|
6
|
+
<span :style="{ display: 'block', width, height, borderRadius: px(radius), background: 'var(--skeleton)', animation: 'kb-pulse 1.4s var(--ease-in-out) infinite', ...style }" />
|
|
6
7
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { px } from '../px.js'
|
|
2
3
|
defineProps({ size: { type: Number, default: 16 }, color: { type: String, default: 'var(--action-primary)' }, style: Object })
|
|
3
4
|
</script>
|
|
4
5
|
<template>
|
|
5
|
-
<span :style="{ display: 'inline-block', width: size, height: size, borderRadius: 'var(--radius-full)', border: '2px solid var(--track)', borderTopColor: color, animation: 'kb-spin .7s linear infinite', ...style }" />
|
|
6
|
+
<span :style="{ display: 'inline-block', width: px(size), height: px(size), borderRadius: 'var(--radius-full)', border: '2px solid var(--track)', borderTopColor: color, animation: 'kb-spin .7s linear infinite', ...style }" />
|
|
6
7
|
</template>
|
|
@@ -8,7 +8,7 @@ defineProps({ steps: { type: Array, default: () => [] }, current: { type: Number
|
|
|
8
8
|
<template v-for="(s, i) in steps" :key="s.label">
|
|
9
9
|
<div v-if="i > 0" style="flex:1;height:2px;background:var(--track);margin-top:15.25px;margin-left:12px;margin-right:12px;min-width:24px" />
|
|
10
10
|
<div style="display:flex;flex-direction:column;align-items:center;gap:8px;flex-shrink:0">
|
|
11
|
-
<span :style="{ width:
|
|
11
|
+
<span :style="{ width: '32px', height: '32px', borderRadius: 'var(--radius-full)', boxSizing: 'border-box', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', background: i < current ? 'var(--success)' : i === current ? 'var(--action-primary)' : 'var(--bg-surface)', border: (i < current || i === current) ? 'none' : '2px solid var(--track)', color: (i < current || i === current) ? 'var(--on-accent)' : 'var(--text-muted)' }">
|
|
12
12
|
<Icon :name="i < current ? 'check' : (s.icon || 'file')" :size="16" />
|
|
13
13
|
</span>
|
|
14
14
|
<span :style="{ fontFamily: 'var(--font-sans)', fontSize: 'var(--text-xs)', lineHeight: 'var(--leading-xs)', letterSpacing: 'var(--tracking-xs)', fontWeight: i === current ? 'var(--weight-medium)' : 'var(--weight-regular)', color: i === current ? 'var(--text-heading)' : 'var(--text-secondary)', whiteSpace: 'nowrap' }">{{ s.label }}</span>
|
|
@@ -18,7 +18,7 @@ export function Timeline({ items = [], style }) {
|
|
|
18
18
|
<div style={{ display: "flex", flexDirection: "column", gap: 2, paddingBottom: i < items.length - 1 ? 18 : 0, minWidth: 0, paddingTop: 3 }}>
|
|
19
19
|
<span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: "var(--text-heading)" }}>{it.title}</span>
|
|
20
20
|
{it.meta && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", letterSpacing: "var(--tracking-xs)", color: "var(--text-muted)" }}>{it.meta}</span>}
|
|
21
|
-
{it.body && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-
|
|
21
|
+
{it.body && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", color: "var(--text-body)", paddingTop: 4 }}>{it.body}</span>}
|
|
22
22
|
</div>
|
|
23
23
|
</div>
|
|
24
24
|
))}
|
|
@@ -14,7 +14,7 @@ defineProps({ items: { type: Array, default: () => [] }, style: Object })
|
|
|
14
14
|
<div :style="{ display: 'flex', flexDirection: 'column', gap: '2px', paddingBottom: i < items.length - 1 ? '18px' : 0, minWidth: 0, paddingTop: '3px' }">
|
|
15
15
|
<span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);color:var(--text-heading)">{{ it.title }}</span>
|
|
16
16
|
<span v-if="it.meta" style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-muted)">{{ it.meta }}</span>
|
|
17
|
-
<span v-if="it.body" style="font-family:var(--font-sans);font-size:var(--text-
|
|
17
|
+
<span v-if="it.body" style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-body);padding-top:4px">{{ it.body }}</span>
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|