@keyfunc/keybricks-ds 2.4.13 → 2.5.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/INTEGRATION.md +28 -6
- package/_adherence.oxlintrc.json +10 -4
- package/_ds_bundle.js +20559 -0
- package/_src/icon-index.js +1 -0
- package/_src/unicons-index.js +1 -0
- package/_src/usage-code.js +737 -0
- package/changelog.js +17 -0
- package/components/_cards/accordion.card.html +1 -1
- package/components/_cards/action-chip.card.html +1 -1
- package/components/_cards/activity.card.html +1 -1
- package/components/_cards/alert.card.html +1 -1
- package/components/_cards/app-chrome.card.html +1 -1
- package/components/_cards/banner.card.html +1 -1
- package/components/_cards/breadcrumb.card.html +1 -1
- package/components/_cards/button-1-base.card.html +1 -1
- package/components/_cards/button-2-states.card.html +1 -1
- package/components/_cards/button-3-sizes-icons.card.html +1 -1
- package/components/_cards/button-4-group.card.html +1 -1
- package/components/_cards/cards-indicators.card.html +1 -1
- package/components/_cards/charts-1-sparkline.card.html +1 -1
- package/components/_cards/charts-10-gauge.card.html +1 -1
- package/components/_cards/charts-11-funnel.card.html +1 -1
- package/components/_cards/charts-12-treemap.card.html +1 -1
- 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 +1 -1
- package/components/_cards/charts-17-candlestick.card.html +1 -1
- package/components/_cards/charts-18-sankey.card.html +1 -1
- package/components/_cards/charts-19-chord.card.html +1 -1
- package/components/_cards/charts-2-line.card.html +1 -1
- package/components/_cards/charts-20-sunburst.card.html +1 -1
- package/components/_cards/charts-21-worldmap.card.html +1 -1
- package/components/_cards/charts-22-gantt.card.html +1 -1
- package/components/_cards/charts-23-bar3d.card.html +1 -1
- package/components/_cards/charts-24-pie3d.card.html +1 -1
- package/components/_cards/charts-25-surface3d.card.html +1 -1
- package/components/_cards/charts-26-scatter3d.card.html +1 -1
- package/components/_cards/charts-27-ribbon3d.card.html +1 -1
- package/components/_cards/charts-3-bar.card.html +1 -1
- package/components/_cards/charts-4-barlist.card.html +1 -1
- package/components/_cards/charts-5-donut.card.html +1 -1
- package/components/_cards/charts-6-scatter.card.html +1 -1
- package/components/_cards/charts-7-heatmap.card.html +1 -1
- package/components/_cards/charts-8-waterfall.card.html +1 -1
- package/components/_cards/charts-9-bullet.card.html +1 -1
- package/components/_cards/checkbox.card.html +1 -1
- package/components/_cards/codeblock.card.html +1 -1
- package/components/_cards/combobox.card.html +1 -1
- package/components/_cards/context-menu.card.html +1 -1
- package/components/_cards/date-picker.card.html +1 -1
- package/components/_cards/diffvalue.card.html +1 -1
- package/components/_cards/divider.card.html +1 -1
- package/components/_cards/drawer.card.html +1 -1
- package/components/_cards/dropdown-menu.card.html +1 -1
- package/components/_cards/dropzone.card.html +1 -1
- package/components/_cards/empty-state.card.html +1 -1
- package/components/_cards/icon-button.card.html +1 -1
- package/components/_cards/input.card.html +1 -1
- package/components/_cards/kbd.card.html +1 -1
- package/components/_cards/keyvalue.card.html +1 -1
- package/components/_cards/listrow.card.html +1 -1
- package/components/_cards/loading.card.html +1 -1
- package/components/_cards/log-viewer.card.html +1 -1
- package/components/_cards/multi-select.card.html +1 -1
- package/components/_cards/number-stepper.card.html +1 -1
- package/components/_cards/pagination.card.html +1 -1
- package/components/_cards/people.card.html +1 -1
- package/components/_cards/pipeline-node.card.html +1 -1
- package/components/_cards/pipeline-palette.card.html +1 -1
- package/components/_cards/radio.card.html +1 -1
- package/components/_cards/relative-time-picker.card.html +1 -1
- package/components/_cards/search.card.html +1 -1
- package/components/_cards/segmented-control.card.html +1 -1
- package/components/_cards/select.card.html +1 -1
- package/components/_cards/smart-table.card.html +1 -1
- package/components/_cards/spreadsheet.card.html +1 -1
- package/components/_cards/stepper.card.html +1 -1
- package/components/_cards/table.card.html +7 -2
- package/components/_cards/tabs.card.html +1 -1
- package/components/_cards/tag.card.html +1 -1
- package/components/_cards/textarea.card.html +1 -1
- package/components/_cards/timeline.card.html +1 -1
- package/components/_cards/toast.card.html +1 -1
- package/components/_cards/toggles.card.html +1 -1
- package/components/_cards/tooltip.card.html +1 -1
- package/components/_cards/tree.card.html +1 -1
- package/components/_shared/view-source.js +15 -185
- package/components/charts/BarList.jsx +1 -1
- package/components/charts/BarList.vue +8 -3
- package/components/charts/Ribbon3D.d.ts +2 -0
- package/components/charts/Scatter3D.d.ts +4 -0
- package/components/charts/SunburstChart.jsx +6 -1
- package/components/charts/SunburstChart.vue +7 -1
- package/components/charts/Surface3D.d.ts +2 -0
- package/components/charts/WorldMap.jsx +6 -1
- package/components/charts/WorldMap.vue +7 -1
- package/components/data/ListRow.d.ts +6 -3
- package/components/data/ListRow.jsx +21 -13
- package/components/data/ListRow.vue +25 -15
- package/components/data/Table.jsx +1 -1
- package/components/data/Table.vue +6 -2
- package/components/display/ProgressBar.jsx +1 -1
- package/components/display/ProgressBar.vue +1 -1
- package/components/feedback/Accordion.jsx +1 -1
- package/components/feedback/Accordion.vue +1 -1
- package/components/feedback/ContextMenu.jsx +3 -3
- package/components/feedback/ContextMenu.vue +4 -4
- package/components/feedback/Drawer.jsx +3 -3
- package/components/feedback/Drawer.vue +3 -3
- package/components/feedback/Timeline.vue +4 -1
- package/components/forms/Checkbox.d.ts +2 -0
- package/components/forms/Checkbox.jsx +9 -4
- package/components/forms/Checkbox.vue +6 -2
- package/components/forms/DatePicker.jsx +3 -1
- package/components/forms/DatePicker.vue +2 -1
- package/components/forms/Dropzone.jsx +1 -1
- package/components/forms/Dropzone.vue +1 -1
- package/components/forms/MultiSelect.jsx +5 -3
- package/components/forms/MultiSelect.vue +4 -3
- package/components/forms/Slider.jsx +1 -1
- package/components/forms/Slider.vue +1 -1
- package/components/forms/Switch.jsx +4 -4
- package/components/forms/Switch.vue +4 -4
- package/components/navigation/Sidebar.jsx +2 -2
- package/components/navigation/Sidebar.vue +2 -2
- package/components/navigation/TopBar.jsx +1 -1
- package/components/navigation/TopBar.vue +1 -1
- package/components/navigation/Tree.jsx +2 -2
- package/components/navigation/Tree.vue +14 -5
- package/components/pipeline/DataPreview.jsx +1 -1
- package/components/pipeline/DataPreview.vue +1 -1
- package/components/pipeline/NodeCard.jsx +1 -1
- package/components/pipeline/NodeCard.vue +1 -1
- package/components/pipeline/PaletteItem.vue +1 -1
- package/components/pipeline/PalettePanel.jsx +3 -3
- package/components/pipeline/PalettePanel.vue +2 -2
- package/components/tabs/SegmentedControl.jsx +2 -2
- package/components/tabs/SegmentedControl.vue +2 -2
- package/index.html +6 -56
- package/package.json +4 -2
- package/tokens/dark.css +1 -1
- package/tokens/interaction.css +4 -0
- package/tokens/layout.css +3 -0
- package/tokens/typography.css +2 -1
|
@@ -62,18 +62,18 @@ export function Switch({ checked = false, onChange, disabled, size, tone = "prim
|
|
|
62
62
|
const glyphText = pick(knobText, checked);
|
|
63
63
|
const knobX = checked ? `calc(100% - ${H} + 2px)` : 2;
|
|
64
64
|
|
|
65
|
-
const knob = <span aria-hidden="true" style={{ position: "absolute", top: 2, left: knobX, width: KNOB, height: KNOB, borderRadius: "var(--radius-full)", background: "var(--bg-surface)", boxShadow: "
|
|
65
|
+
const knob = <span aria-hidden="true" style={{ position: "absolute", top: 2, left: knobX, width: KNOB, height: KNOB, borderRadius: "var(--radius-full)", background: "var(--bg-surface)", boxShadow: "var(--shadow-thumb)", transition: "left var(--dur) var(--ease-out)", display: "flex", alignItems: "center", justifyContent: "center", color: track, fontSize: TEXT, lineHeight: 1, fontWeight: "var(--weight-semibold)", fontFamily: "var(--font-sans)", overflow: "hidden" }}>
|
|
66
66
|
{loading
|
|
67
67
|
? <span style={{ width: GLYPH, height: GLYPH, borderRadius: "50%", border: `1.5px solid ${track}`, borderRightColor: "transparent", animation: "kb-spin 720ms linear infinite" }}></span>
|
|
68
68
|
: glyphIcon ? <Icon name={glyphIcon} size={10} strokeWidth={2.5} style={{ width: GLYPH, height: GLYPH }} />
|
|
69
69
|
: glyphText ? <span>{String(glyphText).slice(0, 1)}</span> : null}
|
|
70
70
|
</span>;
|
|
71
71
|
|
|
72
|
-
const insideStyle = (side) => ({ position: "absolute", top: 0, bottom: 0, [side]: 6, display: "flex", alignItems: "center", fontSize: TEXT, lineHeight: 1, fontWeight: "var(--weight-semibold)", fontFamily: "var(--font-sans)", letterSpacing: ".2px", color: "var(--on-accent)", transition: "opacity
|
|
72
|
+
const insideStyle = (side) => ({ position: "absolute", top: 0, bottom: 0, [side]: 6, display: "flex", alignItems: "center", fontSize: TEXT, lineHeight: 1, fontWeight: "var(--weight-semibold)", fontFamily: "var(--font-sans)", letterSpacing: ".2px", color: "var(--on-accent)", transition: "opacity var(--dur) var(--ease-out)", pointerEvents: "none", whiteSpace: "nowrap" });
|
|
73
73
|
|
|
74
74
|
const pill = <span role="switch" aria-checked={checked} aria-busy={loading || undefined} aria-disabled={disabled || undefined} tabIndex={inert ? -1 : 0} onClick={flip}
|
|
75
75
|
onKeyDown={e => { if (e.key === " " || e.key === "Enter") { e.preventDefault(); flip(); } }}
|
|
76
|
-
style={{ position: "relative", display: "inline-block", width: w, height: H, borderRadius: "var(--radius-full)", "--sw-track": track, background: "var(--sw-bg, var(--sw-track))", transition: "background
|
|
76
|
+
style={{ position: "relative", display: "inline-block", width: w, height: H, borderRadius: "var(--radius-full)", "--sw-track": track, background: "var(--sw-bg, var(--sw-track))", transition: "background var(--dur) var(--ease-out)", cursor: inert ? (loading ? "progress" : "not-allowed") : "pointer", flexShrink: 0, verticalAlign: "middle", outline: "none" }}>
|
|
77
77
|
{hasInside && <span style={{ ...insideStyle("left"), opacity: checked ? 1 : 0 }}>{insideOn}</span>}
|
|
78
78
|
{hasInside && <span style={{ ...insideStyle("right"), opacity: checked ? 0 : 1 }}>{insideOff}</span>}
|
|
79
79
|
{knob}
|
|
@@ -83,7 +83,7 @@ export function Switch({ checked = false, onChange, disabled, size, tone = "prim
|
|
|
83
83
|
if (single) return <span data-size={size} data-sw="" style={{ display: "inline-flex", alignItems: "center", verticalAlign: "middle", gap: "var(--control-gap)", opacity: disabled ? .5 : 1, ...style }}>{pill}<span onClick={flip} style={{ ...capBase, fontWeight: "var(--weight-regular)", color: "var(--text-body)" }}>{label}</span></span>;
|
|
84
84
|
if (labelOn == null && labelOff == null) return <span data-size={size} style={{ display: "inline-flex", alignItems: "center", verticalAlign: "middle", opacity: disabled ? .5 : 1, ...style }}>{pill}</span>;
|
|
85
85
|
|
|
86
|
-
const cap = (text, active) => text == null ? null : <span style={{ ...capBase, fontWeight: active ? "var(--weight-medium)" : "var(--weight-regular)", color: active ? "var(--text-heading)" : "var(--text-muted)", transition: "color
|
|
86
|
+
const cap = (text, active) => text == null ? null : <span style={{ ...capBase, fontWeight: active ? "var(--weight-medium)" : "var(--weight-regular)", color: active ? "var(--text-heading)" : "var(--text-muted)", transition: "color var(--dur) var(--ease-out)" }} onClick={flip}>{text}</span>;
|
|
87
87
|
return <span data-size={size} data-sw="" style={{ display: "inline-flex", alignItems: "center", verticalAlign: "middle", gap: "var(--control-gap)", opacity: disabled ? .5 : 1, ...style }}>
|
|
88
88
|
{cap(labelOff, !checked)}{pill}{cap(labelOn, checked)}
|
|
89
89
|
</span>;
|
|
@@ -44,17 +44,17 @@ async function flip() {
|
|
|
44
44
|
}
|
|
45
45
|
emit('update:checked', next); emit('change', next)
|
|
46
46
|
}
|
|
47
|
-
const insideStyle = (side, on) => ({ position: 'absolute', top: 0, bottom: 0, [side]: '6px', display: 'flex', alignItems: 'center', fontSize: TEXT, lineHeight: 1, fontWeight: 'var(--weight-semibold)', fontFamily: 'var(--font-sans)', letterSpacing: '.2px', color: 'var(--on-accent)', transition: 'opacity
|
|
48
|
-
const cap = (active) => ({ fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', fontFamily: 'var(--font-sans)', fontWeight: active ? 'var(--weight-medium)' : 'var(--weight-regular)', color: active ? 'var(--text-heading)' : 'var(--text-muted)', transition: 'color
|
|
47
|
+
const insideStyle = (side, on) => ({ position: 'absolute', top: 0, bottom: 0, [side]: '6px', display: 'flex', alignItems: 'center', fontSize: TEXT, lineHeight: 1, fontWeight: 'var(--weight-semibold)', fontFamily: 'var(--font-sans)', letterSpacing: '.2px', color: 'var(--on-accent)', transition: 'opacity var(--dur) var(--ease-out)', pointerEvents: 'none', whiteSpace: 'nowrap', opacity: on === props.checked ? 1 : 0 })
|
|
48
|
+
const cap = (active) => ({ fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', fontFamily: 'var(--font-sans)', fontWeight: active ? 'var(--weight-medium)' : 'var(--weight-regular)', color: active ? 'var(--text-heading)' : 'var(--text-muted)', transition: 'color var(--dur) var(--ease-out)', cursor: inert.value ? 'default' : 'pointer', userSelect: 'none' })
|
|
49
49
|
</script>
|
|
50
50
|
<template>
|
|
51
51
|
<span :data-size="size" data-sw="" :style="{ display: 'inline-flex', alignItems: 'center', verticalAlign: 'middle', gap: 'var(--control-gap)', opacity: disabled ? .5 : 1, ...style }">
|
|
52
52
|
<span v-if="labelOff != null" :style="cap(!checked)" @click="flip">{{ labelOff }}</span>
|
|
53
53
|
<span role="switch" :aria-checked="checked" :aria-busy="isLoading || undefined" :aria-disabled="disabled || undefined" :tabindex="inert ? -1 : 0" @click="flip" @keydown.space.prevent="flip" @keydown.enter.prevent="flip"
|
|
54
|
-
:style="{ position: 'relative', display: 'inline-block', width: width, height: H, borderRadius: 'var(--radius-full)', '--sw-track': track, background: 'var(--sw-bg, var(--sw-track))', transition: 'background
|
|
54
|
+
:style="{ position: 'relative', display: 'inline-block', width: width, height: H, borderRadius: 'var(--radius-full)', '--sw-track': track, background: 'var(--sw-bg, var(--sw-track))', transition: 'background var(--dur) var(--ease-out)', cursor: inert ? (isLoading ? 'progress' : 'not-allowed') : 'pointer', flexShrink: 0, verticalAlign: 'middle', outline: 'none' }">
|
|
55
55
|
<span v-if="hasInside" :style="insideStyle('left', true)">{{ inOn }}</span>
|
|
56
56
|
<span v-if="hasInside" :style="insideStyle('right', false)">{{ inOff }}</span>
|
|
57
|
-
<span aria-hidden="true" :style="{ position: 'absolute', top: '2px', left: knobX, width: KNOB, height: KNOB, borderRadius: 'var(--radius-full)', background: 'var(--bg-surface)', boxShadow: '
|
|
57
|
+
<span aria-hidden="true" :style="{ position: 'absolute', top: '2px', left: knobX, width: KNOB, height: KNOB, borderRadius: 'var(--radius-full)', background: 'var(--bg-surface)', boxShadow: 'var(--shadow-thumb)', transition: 'left var(--dur) var(--ease-out)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: track, fontSize: TEXT, lineHeight: 1, fontWeight: 'var(--weight-semibold)', fontFamily: 'var(--font-sans)', overflow: 'hidden' }">
|
|
58
58
|
<span v-if="isLoading" :style="{ width: GLYPH, height: GLYPH, borderRadius: '50%', border: '1.5px solid ' + track, borderRightColor: 'transparent', animation: 'kb-spin 720ms linear infinite' }"></span>
|
|
59
59
|
<Icon v-else-if="glyphIcon" :name="glyphIcon" :size="10" :stroke-width="2.5" :style="{ width: GLYPH, height: GLYPH }" />
|
|
60
60
|
<span v-else-if="glyphText">{{ glyphText }}</span>
|
|
@@ -36,10 +36,10 @@ export function Sidebar({ items = KEYBRICKS_NAV, footerItems = KEYBRICKS_NAV_FOO
|
|
|
36
36
|
</div>
|
|
37
37
|
) : <NavItem key={it.label} icon={it.icon} label={it.label} collapsed={collapsed} trailing={it.trailing} active={active === it.label} onClick={() => pick(it.label)} />);
|
|
38
38
|
return (
|
|
39
|
-
<aside data-theme={t.theme} style={{ ...t.vars, width: collapsed ? "var(--rail-w)" : "var(--sidebar-w)", height, background: "var(--sb-bg)", borderRight: "1px solid var(--sb-border)", color: "var(--sb-text)", display: "flex", flexDirection: "column", boxSizing: "border-box", flexShrink: 0, transition: "width
|
|
39
|
+
<aside data-theme={t.theme} style={{ ...t.vars, width: collapsed ? "var(--rail-w)" : "var(--sidebar-w)", height, background: "var(--sb-bg)", borderRight: "1px solid var(--sb-border)", color: "var(--sb-text)", display: "flex", flexDirection: "column", boxSizing: "border-box", flexShrink: 0, transition: "width var(--dur-fast) var(--ease-out)", ...style }}>
|
|
40
40
|
<div style={{ height: "var(--topbar-h)", display: "flex", alignItems: "center", gap: 10, padding: "0 16px", boxSizing: "border-box", justifyContent: collapsed ? "center" : "flex-start", flexShrink: 0 }}>
|
|
41
41
|
<img src={logo} alt="" style={{ width: 24, height: 24, objectFit: "contain", flexShrink: 0 }} />
|
|
42
|
-
{!collapsed ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-md)", lineHeight: "var(--leading-md)", letterSpacing: "var(--tracking-md)", fontWeight: "var(--weight-semibold)", color: "var(--sb-brand)", whiteSpace: "nowrap" }}>{brand}</span> : null}
|
|
42
|
+
{!collapsed ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-md)", lineHeight: "var(--leading-md)", letterSpacing: "var(--tracking-md)", fontWeight: "var(--weight-semibold)", color: "var(--sb-brand)", whiteSpace: "nowrap", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" }}>{brand}</span> : null}
|
|
43
43
|
</div>
|
|
44
44
|
<nav style={{ flex: 1, overflowY: "auto", overflowX: "hidden", padding: 8, display: "flex", flexDirection: "column", gap: 2 }}>
|
|
45
45
|
{onToggleCollapse ? <NavItem icon={collapsed ? "panel-left" : "chevron-left"} label={collapsed ? L.expand : L.collapse} collapsed={collapsed} onClick={onToggleCollapse} /> : null}
|
|
@@ -21,10 +21,10 @@ const groups = computed(() => [{ id: 'main', list: props.items }, { id: 'foot',
|
|
|
21
21
|
const versionText = computed(() => props.collapsed && props.version ? 'v' + String(props.version).split(' ').pop().replace(/^v/i, '') : props.version)
|
|
22
22
|
</script>
|
|
23
23
|
<template>
|
|
24
|
-
<aside :data-theme="t.theme" :style="{ ...t.vars, width: collapsed ? 'var(--rail-w)' : 'var(--sidebar-w)', height, background: 'var(--sb-bg)', borderRight: '1px solid var(--sb-border)', color: 'var(--sb-text)', display: 'flex', flexDirection: 'column', boxSizing: 'border-box', flexShrink: 0, transition: 'width
|
|
24
|
+
<aside :data-theme="t.theme" :style="{ ...t.vars, width: collapsed ? 'var(--rail-w)' : 'var(--sidebar-w)', height, background: 'var(--sb-bg)', borderRight: '1px solid var(--sb-border)', color: 'var(--sb-text)', display: 'flex', flexDirection: 'column', boxSizing: 'border-box', flexShrink: 0, transition: 'width var(--dur-fast) var(--ease-out)', ...style }">
|
|
25
25
|
<div :style="{ height: 'var(--topbar-h)', display: 'flex', alignItems: 'center', gap: '10px', padding: '0 16px', boxSizing: 'border-box', justifyContent: collapsed ? 'center' : 'flex-start', flexShrink: 0 }">
|
|
26
26
|
<img :src="logo" alt="" style="width:24px;height:24px;object-fit:contain;flex-shrink:0" />
|
|
27
|
-
<span v-if="!collapsed" style="font-family:var(--font-sans);font-size:var(--text-md);line-height:var(--leading-md);letter-spacing:var(--tracking-md);font-weight:var(--weight-semibold);color:var(--sb-brand);white-space:nowrap">{{ brand }}</span>
|
|
27
|
+
<span v-if="!collapsed" style="font-family:var(--font-sans);font-size:var(--text-md);line-height:var(--leading-md);letter-spacing:var(--tracking-md);font-weight:var(--weight-semibold);color:var(--sb-brand);white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis">{{ brand }}</span>
|
|
28
28
|
</div>
|
|
29
29
|
<template v-for="g in groups" :key="g.id">
|
|
30
30
|
<component :is="g.id === 'main' ? 'nav' : 'div'" :style="g.id === 'main' ? { flex: 1, overflowY: 'auto', overflowX: 'hidden', padding: '8px', display: 'flex', flexDirection: 'column', gap: '2px' } : { padding: '8px', display: 'flex', flexDirection: 'column', gap: '2px', flexShrink: 0 }">
|
|
@@ -56,7 +56,7 @@ export function TopBar({ placeholder = "Search documents, workflows, data...", o
|
|
|
56
56
|
<IconButton icon="circle-help" label={helpLabel} title={helpLabel} onClick={onHelp} />
|
|
57
57
|
{user ? <div role="button" tabIndex={0} aria-haspopup="menu" aria-expanded={open} onClick={() => setOpen(!open)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setOpen(!open); } }} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
|
|
58
58
|
style={{ height: 32, display: "flex", alignItems: "center", gap: 8, padding: "0 8px 0 10px", borderRadius: "var(--radius-md)", cursor: "pointer", userSelect: "none", background: open ? "var(--bg-active)" : hover ? "var(--bg-hover)" : "transparent", transition: "var(--t-hover)" }}>
|
|
59
|
-
<span style={{ ...TXT, fontWeight: "var(--weight-medium)", color: "var(--text-heading)", whiteSpace: "nowrap" }}>{user.name}</span>
|
|
59
|
+
<span style={{ ...TXT, fontWeight: "var(--weight-medium)", color: "var(--text-heading)", whiteSpace: "nowrap", minWidth: 0, maxWidth: 160, overflow: "hidden", textOverflow: "ellipsis" }}>{user.name}</span>
|
|
60
60
|
<span style={{ display: "inline-flex", color: "var(--text-muted)" }}><Icon name="chevron-down" size={16} /></span>
|
|
61
61
|
</div> : null}
|
|
62
62
|
</>)}
|
|
@@ -27,7 +27,7 @@ function key(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault();
|
|
|
27
27
|
<IconButton icon="circle-help" :label="helpLabel" :title="helpLabel" @click="emit('help')" />
|
|
28
28
|
<div v-if="user" role="button" tabindex="0" aria-haspopup="menu" :aria-expanded="open" @click="open = !open" @keydown="key" @mouseenter="hover = true" @mouseleave="hover = false"
|
|
29
29
|
:style="{ height: '32px', display: 'flex', alignItems: 'center', gap: '8px', padding: '0 8px 0 10px', borderRadius: 'var(--radius-md)', cursor: 'pointer', userSelect: 'none', background: open ? 'var(--bg-active)' : hover ? 'var(--bg-hover)' : 'transparent', transition: 'var(--t-hover)' }">
|
|
30
|
-
<span style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);font-weight:var(--weight-medium);color:var(--text-heading);white-space:nowrap">{{ user.name }}</span>
|
|
30
|
+
<span style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);font-weight:var(--weight-medium);color:var(--text-heading);white-space:nowrap;min-width:0;max-width:160px;overflow:hidden;text-overflow:ellipsis">{{ user.name }}</span>
|
|
31
31
|
<span style="display:inline-flex;color:var(--text-muted)"><Icon name="chevron-down" :size="16" /></span>
|
|
32
32
|
</div>
|
|
33
33
|
</slot>
|
|
@@ -166,7 +166,7 @@ function Row({ item, state, cfg, api }) {
|
|
|
166
166
|
{chevron}
|
|
167
167
|
{cfg.checkable && node.checkable !== false ? (
|
|
168
168
|
<span onClick={(e) => e.stopPropagation()} style={{ display: "inline-flex", flexShrink: 0 }}>
|
|
169
|
-
<Checkbox checked={checked} indeterminate={half} disabled={disabled || node.disableCheckbox} onChange={() => api.check(key)} />
|
|
169
|
+
<Checkbox checked={checked} indeterminate={half} disabled={disabled || node.disableCheckbox} tabIndex={-1} onChange={() => api.check(key)} />
|
|
170
170
|
</span>
|
|
171
171
|
) : null}
|
|
172
172
|
{cfg.showIcon ? (
|
|
@@ -353,7 +353,7 @@ export function Tree({
|
|
|
353
353
|
const cfg = { checkable, checkStrictly, multiple, selectable, showIcon, showLine, draggable, searchValue, onContextMenu, rowActions };
|
|
354
354
|
|
|
355
355
|
return (
|
|
356
|
-
<div role="tree" aria-multiselectable={multiple || undefined} style={{ display: "flex", flexDirection: "column", gap: showLine ? 0 : 1, ...style }}>
|
|
356
|
+
<div role="tree" aria-multiselectable={multiple || undefined} style={{ display: "flex", flexDirection: "column", gap: showLine ? 0 : 1, minWidth: 0, ...style }}>
|
|
357
357
|
<style>{"@keyframes kb-tree-spin{to{transform:rotate(360deg)}}"}</style>
|
|
358
358
|
{rows.length === 0 ? (
|
|
359
359
|
<div style={{ padding: "10px 8px", fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", color: "var(--text-muted)" }}>No matching items</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { ref, computed, watch } from 'vue'
|
|
2
|
+
import { ref, computed, watch, getCurrentInstance } from 'vue'
|
|
3
3
|
import Icon from '../icons/Icon.vue'
|
|
4
4
|
import Checkbox from '../forms/Checkbox.vue'
|
|
5
5
|
// Nested tree view: expand/collapse, single or multiple selection, cascading checkboxes with
|
|
@@ -33,7 +33,12 @@ const props = defineProps({
|
|
|
33
33
|
reorder: Boolean,
|
|
34
34
|
loadData: Function, rowActions: Function, style: Object,
|
|
35
35
|
})
|
|
36
|
-
|
|
36
|
+
// @context-menu(key, event) — like React's onContextMenu; only a listener suppresses the browser menu.
|
|
37
|
+
const emit = defineEmits(['select', 'check', 'expand', 'drop', 'contextMenu'])
|
|
38
|
+
const treeInst = getCurrentInstance()
|
|
39
|
+
function onCtx(e, key) { if (treeInst && treeInst.vnode.props && treeInst.vnode.props.onContextMenu) { e.preventDefault(); emit('contextMenu', key, e) } }
|
|
40
|
+
const Cell = (p) => p.v
|
|
41
|
+
Cell.props = ['v']
|
|
37
42
|
|
|
38
43
|
const data = ref(JSON.parse(JSON.stringify(props.treeData || props.items)))
|
|
39
44
|
// Compare by value: re-seeding on identity alone would wipe async-loaded children.
|
|
@@ -219,7 +224,7 @@ function parts(text) {
|
|
|
219
224
|
const dropAt = k => (drop.value && drop.value.key === k ? drop.value.pos : null)
|
|
220
225
|
</script>
|
|
221
226
|
<template>
|
|
222
|
-
<div role="tree" :style="{ display: 'flex', flexDirection: 'column', gap: showLine ? 0 : '1px', ...style }">
|
|
227
|
+
<div role="tree" :style="{ display: 'flex', flexDirection: 'column', gap: showLine ? 0 : '1px', minWidth: 0, ...style }">
|
|
223
228
|
<div v-for="row in rows" :key="row.key" role="treeitem" class="kb-tree-row"
|
|
224
229
|
:tabindex="focus === row.key || (!focus && row === rows[0]) ? 0 : -1"
|
|
225
230
|
:aria-expanded="isBranch(row.n) ? openKeys.includes(row.key) : undefined"
|
|
@@ -227,7 +232,7 @@ const dropAt = k => (drop.value && drop.value.key === k ? drop.value.pos : null)
|
|
|
227
232
|
:draggable="draggable && !row.n.disabled"
|
|
228
233
|
@dragstart="dragKey = row.key" @dragover="onDragOver($event, row.key)" @dragleave="drop = null"
|
|
229
234
|
@drop.prevent="onDrop(row.key)" @dragend="dragKey = null; drop = null"
|
|
230
|
-
@click="rowClick(row.n)" @keydown="keyDown($event, row)"
|
|
235
|
+
@click="rowClick(row.n)" @contextmenu="onCtx($event, row.key)" @keydown="keyDown($event, row)"
|
|
231
236
|
:class="{ 'is-disabled': row.n.disabled }"
|
|
232
237
|
:style="{
|
|
233
238
|
position: 'relative', minHeight: 'var(--h-tree-item)', borderRadius: 'var(--radius-md)', display: 'flex', alignItems: 'center', gap: '6px',
|
|
@@ -253,7 +258,7 @@ const dropAt = k => (drop.value && drop.value.key === k ? drop.value.pos : null)
|
|
|
253
258
|
<span v-else :style="{ width: showLine ? 0 : '14px', flexShrink: 0 }" />
|
|
254
259
|
<span v-if="checkable && row.n.checkable !== false" @click.stop style="display:inline-flex;flex-shrink:0">
|
|
255
260
|
<Checkbox :checked="view.full.has(row.key)" :indeterminate="view.half.has(row.key)"
|
|
256
|
-
:disabled="row.n.disabled || row.n.disableCheckbox" @change="check(row.key)" />
|
|
261
|
+
:disabled="row.n.disabled || row.n.disableCheckbox" :tabindex="-1" @change="check(row.key)" />
|
|
257
262
|
</span>
|
|
258
263
|
<span v-if="showIcon" :style="{ color: isBranch(row.n) ? 'var(--text-muted)' : docHues[row.n.hue || 'gray'], display: 'inline-flex', flexShrink: 0 }">
|
|
259
264
|
<Icon :name="row.n.icon || (isBranch(row.n) ? (openKeys.includes(row.key) ? 'folder-open' : 'folder') : 'file-text')" :size="15" />
|
|
@@ -263,6 +268,8 @@ const dropAt = k => (drop.value && drop.value.key === k ? drop.value.pos : null)
|
|
|
263
268
|
</span>
|
|
264
269
|
<span v-if="row.n.tag" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:1;padding:2px 5px;border-radius:var(--radius-sm);background:var(--bg-inset);color:var(--text-secondary);flex-shrink:0">{{ row.n.tag }}</span>
|
|
265
270
|
<span v-if="row.n.count != null" style="margin-left:auto;font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-muted);padding-left:8px">{{ row.n.count }}</span>
|
|
271
|
+
<!-- row actions: #row-actions="{ node }" slot, or rowActions(node) returning a VNode; shown on hover / focus -->
|
|
272
|
+
<span v-if="(rowActions || $slots['row-actions']) && !row.n.disabled" class="kb-tree-actions" @click.stop :style="{ marginLeft: row.n.count != null ? '6px' : 'auto', display: 'flex', gap: '2px', flexShrink: 0 }"><slot name="row-actions" :node="row.n"><Cell v-if="rowActions" :v="rowActions(row.n)" /></slot></span>
|
|
266
273
|
</div>
|
|
267
274
|
<div v-if="!rows.length" style="padding:10px 8px;font-family:var(--font-sans);font-size:var(--text-base);color:var(--text-muted)">No matching items</div>
|
|
268
275
|
</div>
|
|
@@ -270,6 +277,8 @@ const dropAt = k => (drop.value && drop.value.key === k ? drop.value.pos : null)
|
|
|
270
277
|
<style scoped>
|
|
271
278
|
.kb-tree-row:not(.is-disabled):hover{background:var(--bg-hover)}
|
|
272
279
|
.kb-tree-row:focus-visible{outline:2px solid var(--focus-border);outline-offset:-2px}
|
|
280
|
+
.kb-tree-actions{opacity:0;transition:var(--t-hover)}
|
|
281
|
+
.kb-tree-row:hover .kb-tree-actions,.kb-tree-row:focus-within .kb-tree-actions{opacity:1}
|
|
273
282
|
.kb-tree-spin{animation:kb-tree-spin 900ms linear infinite}
|
|
274
283
|
@keyframes kb-tree-spin{to{transform:rotate(360deg)}}
|
|
275
284
|
</style>
|
|
@@ -7,7 +7,7 @@ import { Table } from "../data/Table.jsx";
|
|
|
7
7
|
/** Bottom data-preview drawer — 250px: 40px header (title, Debug Mode, collapse) + dense table. An sm size region: the header's controls are 24px and the table has 32px rows with 12px text. */
|
|
8
8
|
export function DataPreview({ title = "Data Preview", columns = [], rows = [], debug = false, onDebug, collapsed, onCollapse, size = "sm", style }) {
|
|
9
9
|
return (
|
|
10
|
-
<div data-size={size} style={{ background: "var(--bg-surface)", borderTop: "1px solid var(--border-card)", display: "flex", flexDirection: "column", height: collapsed ? "var(--bar-card-h)" : "var(--drawer-h)", overflow: "hidden", boxSizing: "border-box", flexShrink: 0, transition: "height
|
|
10
|
+
<div data-size={size} style={{ background: "var(--bg-surface)", borderTop: "1px solid var(--border-card)", display: "flex", flexDirection: "column", height: collapsed ? "var(--bar-card-h)" : "var(--drawer-h)", overflow: "hidden", boxSizing: "border-box", flexShrink: 0, transition: "height var(--dur-fast) var(--ease-out)", ...style }}>
|
|
11
11
|
<div style={{ height: "var(--bar-card-h)", display: "flex", alignItems: "center", gap: "var(--group-gap)", padding: "0 var(--edge-pad)", boxSizing: "border-box", flexShrink: 0 }}>
|
|
12
12
|
<span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-semibold)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: "var(--text-heading)" }}>{title}</span>
|
|
13
13
|
<span style={{ flex: 1 }} />
|
|
@@ -7,7 +7,7 @@ defineProps({ title: { type: String, default: 'Data Preview' }, columns: { type:
|
|
|
7
7
|
const emit = defineEmits(['update:debug', 'toggleCollapse'])
|
|
8
8
|
</script>
|
|
9
9
|
<template>
|
|
10
|
-
<div :data-size="size" :style="{ background: 'var(--bg-surface)', borderTop: '1px solid var(--border-card)', display: 'flex', flexDirection: 'column', height: collapsed ? 'var(--bar-card-h)' : 'var(--drawer-h)', overflow: 'hidden', boxSizing: 'border-box', flexShrink: 0, transition: 'height
|
|
10
|
+
<div :data-size="size" :style="{ background: 'var(--bg-surface)', borderTop: '1px solid var(--border-card)', display: 'flex', flexDirection: 'column', height: collapsed ? 'var(--bar-card-h)' : 'var(--drawer-h)', overflow: 'hidden', boxSizing: 'border-box', flexShrink: 0, transition: 'height var(--dur-fast) var(--ease-out)', ...style }">
|
|
11
11
|
<div style="height:var(--bar-card-h);display:flex;align-items:center;gap:var(--group-gap);padding:0 var(--edge-pad);box-sizing:border-box;flex-shrink:0">
|
|
12
12
|
<span style="font-family:var(--font-sans);font-weight:var(--weight-semibold);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-heading)">{{ title }}</span>
|
|
13
13
|
<span style="flex:1" />
|
|
@@ -51,7 +51,7 @@ export function PortLabel({ label = "A", tone = "default", interactive = false,
|
|
|
51
51
|
export function NodeCard({ title, subtitle, icon = "table", subtitleIcon, hue = "source", selected, badge, inputs = [], output = true, showMenu = true, onClick, onMenu, onOutputDown, onInputUp, onInputEnter, onInputLeave, outputTone = "default", inputTones = [], style }) {
|
|
52
52
|
const live = !!(onOutputDown || onInputUp);
|
|
53
53
|
return (
|
|
54
|
-
<div onClick={onClick} style={{ position: "relative", width: 220, height: 90, borderRadius: "var(--radius-node)", background: "var(--bg-surface)", border: `1px solid ${selected ? "var(--selected-border)" : "var(--border-control)"}`, boxShadow: selected ? "var(--shadow-selected)" : "var(--shadow-node)", boxSizing: "border-box", cursor: "pointer", ...style }}>
|
|
54
|
+
<div onClick={onClick} role={onClick ? "button" : undefined} tabIndex={onClick ? 0 : undefined} onKeyDown={onClick ? (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onClick(e); } } : undefined} style={{ position: "relative", width: 220, height: 90, borderRadius: "var(--radius-node)", background: "var(--bg-surface)", border: `1px solid ${selected ? "var(--selected-border)" : "var(--border-control)"}`, boxShadow: selected ? "var(--shadow-selected)" : "var(--shadow-node)", boxSizing: "border-box", cursor: "pointer", ...style }}>
|
|
55
55
|
<div style={{ display: "flex", alignItems: "center", gap: 12, padding: 16, height: "100%", boxSizing: "border-box" }}>
|
|
56
56
|
<span style={{ position: "relative", flexShrink: 0 }}>
|
|
57
57
|
<IconTile icon={icon} hue={hue} kind="node" />
|
|
@@ -29,7 +29,7 @@ const pillBase = { width: '28px', height: '22px', borderRadius: 'var(--radius-fu
|
|
|
29
29
|
const tone = i => props.inputTones[i] || 'default'
|
|
30
30
|
</script>
|
|
31
31
|
<template>
|
|
32
|
-
<div @click="emit('click')" :style="{ position: 'relative', width: '220px', height: '90px', borderRadius: 'var(--radius-node)', background: 'var(--bg-surface)', border: `1px solid ${selected ? 'var(--selected-border)' : 'var(--border-control)'}`, boxShadow: selected ? 'var(--shadow-selected)' : 'var(--shadow-node)', boxSizing: 'border-box', cursor: 'pointer', ...style }">
|
|
32
|
+
<div role="button" tabindex="0" @keydown.enter.prevent="emit('click')" @keydown.space.prevent="emit('click')" @click="emit('click')" :style="{ position: 'relative', width: '220px', height: '90px', borderRadius: 'var(--radius-node)', background: 'var(--bg-surface)', border: `1px solid ${selected ? 'var(--selected-border)' : 'var(--border-control)'}`, boxShadow: selected ? 'var(--shadow-selected)' : 'var(--shadow-node)', boxSizing: 'border-box', cursor: 'pointer', ...style }">
|
|
33
33
|
<div style="display:flex;align-items:center;gap:12px;padding:16px;height:100%;box-sizing:border-box">
|
|
34
34
|
<span style="position:relative;flex-shrink:0">
|
|
35
35
|
<IconTile :icon="icon" :hue="hue" kind="node" />
|
|
@@ -8,7 +8,7 @@ const hover = ref(false)
|
|
|
8
8
|
</script>
|
|
9
9
|
<template>
|
|
10
10
|
<div role="button" tabindex="0" @click="$emit('click')" @mouseenter="hover = true" @mouseleave="hover = false"
|
|
11
|
-
:style="{ width: '67.664px', borderRadius: 'var(--radius-lg)', padding: '8px', display: 'flex', flexDirection: 'column', gap: '6px', alignItems: 'center', cursor: 'grab', boxSizing: 'border-box', background: hover ? 'var(--bg-hover)' : 'transparent', transition: 'background
|
|
11
|
+
:style="{ width: '67.664px', borderRadius: 'var(--radius-lg)', padding: '8px', display: 'flex', flexDirection: 'column', gap: '6px', alignItems: 'center', cursor: 'grab', boxSizing: 'border-box', background: hover ? 'var(--bg-hover)' : 'transparent', transition: 'background var(--dur-fast) var(--ease-out)', ...style }">
|
|
12
12
|
<IconTile :icon="icon" :hue="hue" kind="palette" />
|
|
13
13
|
<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-body);text-align:center">{{ label }}</span>
|
|
14
14
|
</div>
|
|
@@ -9,7 +9,7 @@ export function PaletteItem({ icon, hue, label, onClick, style }) {
|
|
|
9
9
|
const [hover, setHover] = React.useState(false);
|
|
10
10
|
return (
|
|
11
11
|
<div role="button" tabIndex={0} onClick={onClick} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
|
|
12
|
-
style={{ width: 67.664, borderRadius: "var(--radius-lg)", padding: 8, display: "flex", flexDirection: "column", gap: 6, alignItems: "center", cursor: "grab", boxSizing: "border-box", background: hover ? "var(--bg-hover)" : "transparent", transition: "background
|
|
12
|
+
style={{ width: 67.664, borderRadius: "var(--radius-lg)", padding: 8, display: "flex", flexDirection: "column", gap: 6, alignItems: "center", cursor: "grab", boxSizing: "border-box", background: hover ? "var(--bg-hover)" : "transparent", transition: "background var(--dur-fast) var(--ease-out)", ...style }}>
|
|
13
13
|
<IconTile icon={icon} hue={hue} kind="palette" />
|
|
14
14
|
<span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", letterSpacing: "var(--tracking-xs)", color: "var(--text-body)", textAlign: "center" }}>{label}</span>
|
|
15
15
|
</div>
|
|
@@ -44,8 +44,8 @@ export function PalettePanel({ catalog = PIPELINE_CATALOG, height = "100%", onPi
|
|
|
44
44
|
<div style={{ flex: 1, overflowY: "auto", padding: 8 }}>
|
|
45
45
|
{catalog.map((cat) => (
|
|
46
46
|
<div key={cat.label}>
|
|
47
|
-
<div onClick={() => toggle(cat.label)} data-wash="" style={{ height: "var(--h-subnav-item)", borderRadius: "var(--radius-md)", display: "flex", alignItems: "center", gap: 8, padding: "6px 8px", boxSizing: "border-box", cursor: "pointer", background: "var(--wash, transparent)", transition: "var(--t-hover)" }}>
|
|
48
|
-
<span style={{ color: "var(--text-muted)", display: "inline-flex", transform: closed.includes(cat.label) ? "rotate(-90deg)" : "none", transition: "transform
|
|
47
|
+
<div role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); toggle(cat.label); } }} onClick={() => toggle(cat.label)} data-wash="" style={{ height: "var(--h-subnav-item)", borderRadius: "var(--radius-md)", display: "flex", alignItems: "center", gap: 8, padding: "6px 8px", boxSizing: "border-box", cursor: "pointer", background: "var(--wash, transparent)", transition: "var(--t-hover)" }}>
|
|
48
|
+
<span style={{ color: "var(--text-muted)", display: "inline-flex", transform: closed.includes(cat.label) ? "rotate(-90deg)" : "none", transition: "transform var(--dur-fast) var(--ease-out)" }}><Icon name="chevron-down" size={14} /></span>
|
|
49
49
|
<span style={{ color: `var(--cat-${cat.hue})`, display: "inline-flex" }}><Icon name={cat.icon} size={14} /></span>
|
|
50
50
|
<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-strong)" }}>{cat.label}</span>
|
|
51
51
|
</div>
|
|
@@ -21,8 +21,8 @@ function toggle(l) { closed.value = closed.value.includes(l) ? closed.value.filt
|
|
|
21
21
|
</div>
|
|
22
22
|
<div style="flex:1;overflow-y:auto;padding:8px">
|
|
23
23
|
<div v-for="cat in catalog" :key="cat.label">
|
|
24
|
-
<div @click="toggle(cat.label)" data-wash="" style="height:var(--h-subnav-item);border-radius:var(--radius-md);display:flex;align-items:center;gap:8px;padding:6px 8px;box-sizing:border-box;cursor:pointer;background:var(--wash, transparent);transition:var(--t-hover)">
|
|
25
|
-
<span :style="{ color: 'var(--text-muted)', display: 'inline-flex', transform: closed.includes(cat.label) ? 'rotate(-90deg)' : 'none', transition: 'transform
|
|
24
|
+
<div role="button" tabindex="0" @keydown.enter.prevent="toggle(cat.label)" @keydown.space.prevent="toggle(cat.label)" @click="toggle(cat.label)" data-wash="" style="height:var(--h-subnav-item);border-radius:var(--radius-md);display:flex;align-items:center;gap:8px;padding:6px 8px;box-sizing:border-box;cursor:pointer;background:var(--wash, transparent);transition:var(--t-hover)">
|
|
25
|
+
<span :style="{ color: 'var(--text-muted)', display: 'inline-flex', transform: closed.includes(cat.label) ? 'rotate(-90deg)' : 'none', transition: 'transform var(--dur-fast) var(--ease-out)' }"><Icon name="chevron-down" :size="14" /></span>
|
|
26
26
|
<span :style="{ color: `var(--cat-${cat.hue})`, display: 'inline-flex' }"><Icon :name="cat.icon" :size="14" /></span>
|
|
27
27
|
<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-strong)">{{ cat.label }}</span>
|
|
28
28
|
</div>
|
|
@@ -28,8 +28,8 @@ export function SegmentedControl({ options = [], value, onChange, size, iconOnly
|
|
|
28
28
|
return (
|
|
29
29
|
<button key={o.label} type="button" role="tab" aria-selected={is} aria-label={iconOnly ? o.label : undefined} title={iconOnly ? o.label : undefined} disabled={off} tabIndex={is ? 0 : -1} data-ink={is || off ? undefined : ""}
|
|
30
30
|
onClick={() => !off && onChange && onChange(o.label)}
|
|
31
|
-
style={{ height: "calc(var(--control-h) - 6px)", padding: iconOnly ? "0 calc(var(--control-px) - 4px)" : "0 calc(var(--control-px) - 2px)", flex: fullWidth ? 1 : "none", justifyContent: "center", display: "inline-flex", alignItems: "center", gap: 6, borderRadius: "var(--radius-sm)", border: "none", cursor: off ? "not-allowed" : "pointer", background: is ? "var(--bg-surface)" : "transparent", boxShadow: is ? "
|
|
32
|
-
{o.icon ? <Icon name={o.icon} size={16} style={tierIcon} /> : null}{iconOnly ? null : o.label}{iconOnly ? null : <CountBadge count={o.count} selected={is} showZero={o.showZero ?? showZero} />}
|
|
31
|
+
style={{ height: "calc(var(--control-h) - 6px)", padding: iconOnly ? "0 calc(var(--control-px) - 4px)" : "0 calc(var(--control-px) - 2px)", flex: fullWidth ? 1 : "none", justifyContent: "center", display: "inline-flex", alignItems: "center", gap: 6, borderRadius: "var(--radius-sm)", border: "none", cursor: off ? "not-allowed" : "pointer", background: is ? "var(--bg-surface)" : "transparent", boxShadow: is ? "var(--shadow-raised)" : "none", fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--control-text)", lineHeight: "var(--control-leading)", letterSpacing: "var(--control-tracking)", color: is ? "var(--text-heading)" : "var(--ink, var(--text-secondary))", opacity: o.disabled && !disabled ? .45 : 1, whiteSpace: "nowrap", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", transition: "var(--t-hover)" }}>
|
|
32
|
+
{o.icon ? <Icon name={o.icon} size={16} style={tierIcon} /> : null}{iconOnly ? null : <span style={{ minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" }}>{o.label}</span>}{iconOnly ? null : <CountBadge count={o.count} selected={is} showZero={o.showZero ?? showZero} />}
|
|
33
33
|
</button>
|
|
34
34
|
);
|
|
35
35
|
})}
|
|
@@ -21,8 +21,8 @@ function onKey(e) {
|
|
|
21
21
|
<template>
|
|
22
22
|
<div role="tablist" :data-size="size" :aria-disabled="disabled || undefined" @keydown="onKey" :style="{ display: fullWidth ? 'flex' : 'inline-flex', height: 'var(--control-h)', padding: '2px', gap: '2px', background: 'var(--bg-inset)', border: '1px solid var(--border-control)', borderRadius: 'var(--radius-md)', boxSizing: 'border-box', opacity: disabled ? .5 : 1, ...style }">
|
|
23
23
|
<button v-for="o in opts" :key="o.label" type="button" role="tab" :aria-selected="modelValue === o.label" :aria-label="iconOnly ? o.label : undefined" :title="iconOnly ? o.label : undefined" :disabled="disabled || o.disabled" :tabindex="modelValue === o.label ? 0 : -1" :data-ink="modelValue === o.label || disabled || o.disabled ? undefined : ''" @click="pick(o)"
|
|
24
|
-
:style="{ height: 'calc(var(--control-h) - 6px)', padding: iconOnly ? '0 calc(var(--control-px) - 4px)' : '0 calc(var(--control-px) - 2px)', flex: fullWidth ? 1 : 'none', justifyContent: 'center', display: 'inline-flex', alignItems: 'center', gap: '6px', borderRadius: 'var(--radius-sm)', border: 'none', cursor: (disabled || o.disabled) ? 'not-allowed' : 'pointer', background: modelValue === o.label ? 'var(--bg-surface)' : 'transparent', boxShadow: modelValue === o.label ? '
|
|
25
|
-
<Icon v-if="o.icon" :name="o.icon" :size="16" :style="tierIcon" /><template v-if="!iconOnly">{{ o.label }}
|
|
24
|
+
:style="{ height: 'calc(var(--control-h) - 6px)', padding: iconOnly ? '0 calc(var(--control-px) - 4px)' : '0 calc(var(--control-px) - 2px)', flex: fullWidth ? 1 : 'none', justifyContent: 'center', display: 'inline-flex', alignItems: 'center', gap: '6px', borderRadius: 'var(--radius-sm)', border: 'none', cursor: (disabled || o.disabled) ? 'not-allowed' : 'pointer', background: modelValue === o.label ? 'var(--bg-surface)' : 'transparent', boxShadow: modelValue === o.label ? 'var(--shadow-raised)' : 'none', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', color: modelValue === o.label ? 'var(--text-heading)' : 'var(--ink, var(--text-secondary))', opacity: o.disabled && !disabled ? .45 : 1, whiteSpace: 'nowrap', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', transition: 'var(--t-hover)' }">
|
|
25
|
+
<Icon v-if="o.icon" :name="o.icon" :size="16" :style="tierIcon" /><template v-if="!iconOnly"><span style="min-width:0;overflow:hidden;text-overflow:ellipsis">{{ o.label }}</span><CountBadge :count="o.count" :selected="modelValue === o.label" :show-zero="o.showZero ?? showZero" /></template>
|
|
26
26
|
</button>
|
|
27
27
|
</div>
|
|
28
28
|
</template>
|
package/index.html
CHANGED
|
@@ -59,22 +59,6 @@ section{margin-bottom:52px;scroll-margin-top:78px}
|
|
|
59
59
|
.frame-wrap{position:relative;border:1px solid var(--border-card);border-radius:var(--radius-lg);background:var(--bg-surface);overflow:hidden}
|
|
60
60
|
.frame-wrap iframe{border:0;display:block;transform-origin:top left;background:var(--bg-surface)}
|
|
61
61
|
.frame-ph{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-muted);background:var(--bg-inset);letter-spacing:var(--tracking-xs)}
|
|
62
|
-
.code-area{margin-top:14px}
|
|
63
|
-
.code-btn{display:inline-flex;align-items:center;gap:6px;height:28px;box-sizing:border-box;padding:0 8px;line-height:var(--leading-xs);border-radius:var(--radius-md);border:1px solid var(--border-control);background:var(--bg-surface);color:var(--text-secondary);font-size:var(--text-xs);font-family:var(--font-sans);cursor:pointer;transition:var(--t-hover);letter-spacing:var(--tracking-xs)}
|
|
64
|
-
.code-btn:hover{background:var(--bg-hover);color:var(--text-heading)}
|
|
65
|
-
.code-panel{display:none;margin-top:10px;border:1px solid var(--border-card);border-radius:var(--radius-lg);overflow:hidden;background:var(--bg-inset)}
|
|
66
|
-
.code-panel.open{display:block}
|
|
67
|
-
.code-head{display:flex;align-items:center;justify-content:space-between;background:var(--bg-surface);border-bottom:1px solid var(--border-subtle)}
|
|
68
|
-
.code-tabs{display:flex;flex:1;overflow-x:auto;scrollbar-width:none}
|
|
69
|
-
.code-tabs::-webkit-scrollbar{display:none}
|
|
70
|
-
.code-tab{padding:8px 12px;border:none;background:transparent;font-size:var(--text-xs);font-family:var(--font-sans);color:var(--text-secondary);cursor:pointer;white-space:nowrap;border-bottom:2px solid transparent;margin-bottom:-1px;letter-spacing:var(--tracking-xs)}
|
|
71
|
-
.code-tab.on{color:var(--text-heading);border-bottom-color:var(--action-primary)}
|
|
72
|
-
.code-fw{display:flex;gap:3px;padding:6px 8px;flex-shrink:0}
|
|
73
|
-
.code-fw{display:flex;align-items:center;flex-shrink:0;height:28px;box-sizing:border-box;gap:2px;padding:2px;margin:0 8px;border-radius:var(--radius-md);background:var(--bg-inset);border:1px solid var(--border-control)}.fw-btn{display:inline-flex;align-items:center;height:22px;padding:0 6px;font-size:var(--text-xs);line-height:var(--leading-xs);border-radius:var(--radius-sm);border:none;background:transparent;color:var(--text-secondary);font-family:var(--font-sans);font-weight:var(--weight-medium);white-space:nowrap;cursor:pointer;transition:var(--t-hover);letter-spacing:var(--tracking-xs)}.fw-btn:not(.on):hover{color:var(--text-heading)}
|
|
74
|
-
.fw-btn.on{background:var(--bg-surface);color:var(--text-heading);box-shadow:0 1px 2px rgba(0,0,0,.08)}
|
|
75
|
-
.code-body{position:relative}
|
|
76
|
-
.code-body pre{margin:0;padding:14px 70px 14px 16px;overflow:auto;max-height:420px;font-family:var(--font-mono);font-size:var(--text-code);line-height:var(--leading-code);color:var(--text-body);white-space:pre}
|
|
77
|
-
.copy-btn{position:absolute;top:8px;right:10px;display:inline-flex;align-items:center;height:28px;box-sizing:border-box;padding:0 8px;border-radius:var(--radius-md);border:1px solid var(--border-control);background:var(--bg-surface);color:var(--text-secondary);font-size:var(--text-xs);line-height:var(--leading-xs);font-family:var(--font-sans);cursor:pointer;letter-spacing:var(--tracking-xs)}
|
|
78
62
|
|
|
79
63
|
/* overview */
|
|
80
64
|
#overview{margin-bottom:64px}
|
|
@@ -192,7 +176,7 @@ section{margin-bottom:52px;scroll-margin-top:78px}
|
|
|
192
176
|
const GROUPS=[["Brand","品牌","ブランド"],["Colors","颜色","カラー"],["Type","字体排印","タイポグラフィ"],["Foundations","基础规范","基盤仕様"],["Components","组件","コンポーネント"],["Charts","图表","チャート"],["Icons","图标库","アイコン"]];
|
|
193
177
|
|
|
194
178
|
const S={
|
|
195
|
-
brandsub:["Design System · v2.
|
|
179
|
+
brandsub:["Design System · v2.5.1","设计系统 · v2.5.1","デザインシステム · v2.5.1"],
|
|
196
180
|
doc_readme:["Readme","说明文档","ドキュメント"],
|
|
197
181
|
doc_design:["DESIGN.md","下载 DESIGN.md","DESIGN.md"],
|
|
198
182
|
doc_integration:["Integration","接入指南","導入ガイド"],
|
|
@@ -258,6 +242,7 @@ function setLang(l){
|
|
|
258
242
|
document.getElementById("filter").placeholder=pick(S.filter_ph);
|
|
259
243
|
document.querySelectorAll('a[data-readme]').forEach(a=>{a.href=(l==="zh"||l==="both")?"readme.zh-CN.md":"readme.md";});
|
|
260
244
|
if(typeof fitChangelogTags==="function")fitChangelogTags();
|
|
245
|
+
syncFrames();
|
|
261
246
|
}
|
|
262
247
|
|
|
263
248
|
/* ---------- overview ---------- */
|
|
@@ -451,6 +436,8 @@ function renderNav(){
|
|
|
451
436
|
let followSystem=true;
|
|
452
437
|
const themeOf=()=>html.getAttribute("data-theme")||"light";
|
|
453
438
|
function pushTheme(f){
|
|
439
|
+
/* Cards also need the site language: the "Show code" bars under each example follow it. */
|
|
440
|
+
try{f.contentWindow&&f.contentWindow.postMessage({kb:"lang",lang},"*");}catch(e){}
|
|
454
441
|
if(f.dataset.alwaysdark==="1"||f.dataset.selfmanaged==="1")return;
|
|
455
442
|
const t=themeOf();
|
|
456
443
|
try{f.contentWindow&&f.contentWindow.postMessage({kb:"theme",theme:t},"*");}catch(e){}
|
|
@@ -473,49 +460,12 @@ if(mq&&mq.addEventListener)mq.addEventListener("change",e=>{if(followSystem)setT
|
|
|
473
460
|
document.getElementById("themeToggle").addEventListener("click",()=>setTheme(themeOf()==="dark"?"light":"dark",true));
|
|
474
461
|
|
|
475
462
|
/* ---------- frames: lazy load, fit to width, grow with content ---------- */
|
|
476
|
-
/* Cards
|
|
477
|
-
cannot read the frame's DOM); the View Code UI is rendered here, below the box. */
|
|
478
|
-
const CODE_ICON='<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg>';
|
|
479
|
-
function buildCode(f,files){
|
|
480
|
-
if(f.__code||!files||!files.length)return;
|
|
481
|
-
const area=document.createElement("div");area.className="code-area";
|
|
482
|
-
const btn=document.createElement("button");btn.className="code-btn";
|
|
483
|
-
btn.innerHTML=CODE_ICON+'<span>View Code</span>';
|
|
484
|
-
const panel=document.createElement("div");panel.className="code-panel";
|
|
485
|
-
const head=document.createElement("div");head.className="code-head";
|
|
486
|
-
const tabs=document.createElement("div");tabs.className="code-tabs";
|
|
487
|
-
const fwb=document.createElement("div");fwb.className="code-fw";
|
|
488
|
-
head.appendChild(tabs);head.appendChild(fwb);
|
|
489
|
-
const body=document.createElement("div");body.className="code-body";
|
|
490
|
-
const pre=document.createElement("pre");
|
|
491
|
-
const copy=document.createElement("button");copy.className="copy-btn";copy.textContent="Copy";
|
|
492
|
-
body.appendChild(pre);body.appendChild(copy);
|
|
493
|
-
panel.appendChild(head);panel.appendChild(body);
|
|
494
|
-
area.appendChild(btn);area.appendChild(panel);
|
|
495
|
-
f.__section.appendChild(area);
|
|
496
|
-
let framework="react",active=0,open=false;const cache={};
|
|
497
|
-
files.forEach((t,i)=>{const b=document.createElement("button");b.className="code-tab";b.textContent=t.name;b.onclick=()=>{active=i;render();};tabs.appendChild(b);});
|
|
498
|
-
[["react","React"],["vue","Vue 3"]].forEach(([k,lab])=>{const b=document.createElement("button");b.className="fw-btn";b.textContent=lab;b.onclick=()=>{framework=k;render();};fwb.appendChild(b);});
|
|
499
|
-
const hidden=(t)=>(framework==="vue"&&t.reactOnly)||(framework==="react"&&t.vueOnly);
|
|
500
|
-
function render(){
|
|
501
|
-
if(hidden(files[active])){const k=files.findIndex(t=>!hidden(t));if(k>=0)active=k;}
|
|
502
|
-
[...tabs.children].forEach((el,i)=>{el.classList.toggle("on",i===active);el.style.display=hidden(files[i])?"none":"";el.textContent=framework==="vue"?(files[i].vueName||files[i].name.replace(/\.jsx$/,".vue")):files[i].name;});
|
|
503
|
-
[...fwb.children].forEach((el,i)=>el.classList.toggle("on",i===(framework==="react"?0:1)));
|
|
504
|
-
const key=framework+"|"+files[active].path;
|
|
505
|
-
if(cache[key]!=null)pre.textContent=cache[key];
|
|
506
|
-
else{pre.textContent="Loading…";try{f.contentWindow.postMessage({kb:"src-get",path:files[active].path,framework},"*");}catch(e){}}
|
|
507
|
-
}
|
|
508
|
-
copy.onclick=()=>{const t=pre.textContent;try{navigator.clipboard.writeText(t);}catch(e){}copy.textContent="Copied";setTimeout(()=>{copy.textContent="Copy";},1200);};
|
|
509
|
-
btn.onclick=()=>{open=!open;panel.classList.toggle("open",open);btn.querySelector("span").textContent=open?"Hide Code":"View Code";if(open)render();};
|
|
510
|
-
f.__code={receive(m){cache[m.framework+"|"+m.path]=m.text;if(open&&files[active].path===m.path&&framework===m.framework)pre.textContent=m.text;}};
|
|
511
|
-
}
|
|
463
|
+
/* Cards ask for the site theme over postMessage (works from file:// too). Usage code ("Show code") lives inside each card. */
|
|
512
464
|
addEventListener("message",e=>{
|
|
513
465
|
const m=e.data;if(!m||!m.kb)return;
|
|
514
466
|
const f=[...document.querySelectorAll("iframe")].find(x=>{try{return x.contentWindow===e.source;}catch(err){return false;}});
|
|
515
467
|
if(!f)return;
|
|
516
|
-
if(m.kb==="
|
|
517
|
-
else if(m.kb==="src-text"&&f.__code)f.__code.receive(m);
|
|
518
|
-
else if(m.kb==="theme-ask")pushTheme(f);
|
|
468
|
+
if(m.kb==="theme-ask")pushTheme(f);
|
|
519
469
|
});
|
|
520
470
|
function fit(f){
|
|
521
471
|
const wrap=f.__wrap,avail=wrap.clientWidth;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyfunc/keybricks-ds",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Keybricks design system — design tokens, three size tiers and 121 UI components in React and Vue 3, source-only.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -45,7 +45,9 @@
|
|
|
45
45
|
"guidelines",
|
|
46
46
|
"changelog.js",
|
|
47
47
|
"site-nav.js",
|
|
48
|
-
"covers.js"
|
|
48
|
+
"covers.js",
|
|
49
|
+
"_ds_bundle.js",
|
|
50
|
+
"_src"
|
|
49
51
|
],
|
|
50
52
|
"scripts": {
|
|
51
53
|
"build:tokens": "node _scripts/build-tokens.mjs",
|
package/tokens/dark.css
CHANGED
|
@@ -48,7 +48,7 @@ color-scheme:dark;
|
|
|
48
48
|
--shadow-node:0 1px 3px 0 rgba(0,0,0,.6),0 1px 2px -1px rgba(0,0,0,.5);
|
|
49
49
|
--shadow-selected:0 0 0 2px rgba(43,127,255,.32),0 1px 3px 0 rgba(0,0,0,.6);
|
|
50
50
|
--shadow-menu:0 4px 16px rgba(0,0,0,.55),0 1px 3px rgba(0,0,0,.4);
|
|
51
|
-
--shadow-modal:0 24px 64px rgba(0,0,0,.7),0 2px 8px rgba(0,0,0,.5);
|
|
51
|
+
--shadow-modal:0 24px 64px rgba(0,0,0,.7),0 2px 8px rgba(0,0,0,.5);--shadow-raised:0 1px 2px rgba(0,0,0,.4);--shadow-thumb:0 1px 2px rgba(0,0,0,.5);--shadow-drawer:-8px 0 24px rgba(0,0,0,.5);
|
|
52
52
|
}
|
|
53
53
|
[data-theme="dark"] body{background:var(--bg-app);color:var(--text-primary)}
|
|
54
54
|
[data-theme="dark"] img[src$="keybricks-logo.png"]{filter:none}
|
package/tokens/interaction.css
CHANGED
|
@@ -47,3 +47,7 @@ summary:focus-visible,
|
|
|
47
47
|
[role="tab"]:focus-visible,
|
|
48
48
|
[role="switch"]:focus-visible,
|
|
49
49
|
[role="treeitem"]:focus-visible{outline:2px solid var(--focus-border);outline-offset:2px}
|
|
50
|
+
|
|
51
|
+
/* A field box (Select, Combobox, DatePicker, MultiSelect …) is one line: its value text and tags truncate with an ellipsis
|
|
52
|
+
instead of spilling past the border. Icons and the chevron are svg or aria-hidden, so they are not affected. */
|
|
53
|
+
[data-field]>span:not([aria-hidden]),[role="combobox"]>span:not([aria-hidden]){min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
package/tokens/layout.css
CHANGED
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
--shadow-selected:0 0 0 2px var(--focus-ring),0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);
|
|
13
13
|
--shadow-menu:0 4px 12px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.08);
|
|
14
14
|
--shadow-modal:0 24px 64px rgba(0,0,0,.18),0 2px 8px rgba(0,0,0,.08);
|
|
15
|
+
--shadow-raised:0 1px 2px rgba(0,0,0,.08); /* a selected segment lifted off its track */
|
|
16
|
+
--shadow-thumb:0 1px 2px rgba(0,0,0,.15); /* slider thumb, switch knob */
|
|
17
|
+
--shadow-drawer:-8px 0 24px rgba(0,0,0,.12); /* right-hand drawer edge */
|
|
15
18
|
--ring-surface:0 0 0 2px var(--bg-surface);
|
|
16
19
|
--ring-focus:0 0 0 2px var(--bg-surface),0 0 0 4px var(--focus-border);
|
|
17
20
|
/* Chrome sizes */
|
package/tokens/typography.css
CHANGED
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
[lang|="zh"]{--font-sans:'Inter','PingFang SC','Hiragino Sans GB','Microsoft YaHei','Noto Sans SC',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}
|
|
40
40
|
[lang|="ja"]{--font-sans:'Inter','Hiragino Kaku Gothic ProN','Hiragino Sans','Yu Gothic UI','Meiryo','Noto Sans JP',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}
|
|
41
41
|
[lang|="ko"]{--font-sans:'Inter','Apple SD Gothic Neo','Malgun Gothic','Noto Sans KR',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}
|
|
42
|
-
|
|
42
|
+
/* Set the font only where a language is declared; plain text inherits it, so descendant icon and code fonts keep their own family (e.g. AG Grid selection icons). */
|
|
43
|
+
:where([lang|="zh"],[lang|="ja"],[lang|="ko"]){font-family:var(--font-sans)}
|
|
43
44
|
[lang|="zh"],[lang|="ja"],[lang|="ko"]{--weight-medium:500;--weight-semibold:500;
|
|
44
45
|
--text-glyph-min:12px;--text-2xs:12px;--text-xs:13px;--leading-xs:18px;--text-sm:13px;--leading-sm:18px;
|
|
45
46
|
--tracking-2xs:0;--tracking-xs:0;--tracking-sm:0;--tracking-base:0;--tracking-md:0;--tracking-lg:0;--tracking-xl:0;--tracking-num:0;--tracking-2xl:0;--tracking-3xl:0;--tracking-4xl:0;--tracking-display:0}
|