@keyfunc/keybricks-ds 2.5.0 → 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.
Files changed (60) hide show
  1. package/DEPLOY.md +3 -3
  2. package/INTEGRATION.md +12 -1
  3. package/_adherence.oxlintrc.json +10 -4
  4. package/_ds_bundle.js +20559 -0
  5. package/_src/icon-index.js +1 -0
  6. package/_src/unicons-index.js +1 -0
  7. package/_src/usage-code.js +737 -0
  8. package/changelog.js +7 -0
  9. package/components/charts/BarList.jsx +1 -1
  10. package/components/charts/BarList.vue +8 -3
  11. package/components/charts/Ribbon3D.d.ts +2 -0
  12. package/components/charts/Scatter3D.d.ts +4 -0
  13. package/components/charts/SunburstChart.jsx +6 -1
  14. package/components/charts/SunburstChart.vue +7 -1
  15. package/components/charts/Surface3D.d.ts +2 -0
  16. package/components/charts/WorldMap.jsx +6 -1
  17. package/components/charts/WorldMap.vue +7 -1
  18. package/components/data/Table.jsx +1 -1
  19. package/components/data/Table.vue +1 -1
  20. package/components/display/ProgressBar.jsx +1 -1
  21. package/components/display/ProgressBar.vue +1 -1
  22. package/components/feedback/Accordion.jsx +1 -1
  23. package/components/feedback/Accordion.vue +1 -1
  24. package/components/feedback/ContextMenu.jsx +3 -3
  25. package/components/feedback/ContextMenu.vue +4 -4
  26. package/components/feedback/Drawer.jsx +3 -3
  27. package/components/feedback/Drawer.vue +3 -3
  28. package/components/forms/Checkbox.d.ts +2 -0
  29. package/components/forms/Checkbox.jsx +9 -4
  30. package/components/forms/Checkbox.vue +6 -2
  31. package/components/forms/DatePicker.jsx +3 -1
  32. package/components/forms/DatePicker.vue +2 -1
  33. package/components/forms/Dropzone.jsx +1 -1
  34. package/components/forms/Dropzone.vue +1 -1
  35. package/components/forms/MultiSelect.jsx +5 -3
  36. package/components/forms/MultiSelect.vue +4 -3
  37. package/components/forms/Slider.jsx +1 -1
  38. package/components/forms/Slider.vue +1 -1
  39. package/components/forms/Switch.jsx +4 -4
  40. package/components/forms/Switch.vue +4 -4
  41. package/components/navigation/Sidebar.jsx +2 -2
  42. package/components/navigation/Sidebar.vue +2 -2
  43. package/components/navigation/TopBar.jsx +1 -1
  44. package/components/navigation/TopBar.vue +1 -1
  45. package/components/navigation/Tree.jsx +2 -2
  46. package/components/navigation/Tree.vue +8 -5
  47. package/components/pipeline/DataPreview.jsx +1 -1
  48. package/components/pipeline/DataPreview.vue +1 -1
  49. package/components/pipeline/NodeCard.jsx +1 -1
  50. package/components/pipeline/NodeCard.vue +1 -1
  51. package/components/pipeline/PaletteItem.vue +1 -1
  52. package/components/pipeline/PalettePanel.jsx +3 -3
  53. package/components/pipeline/PalettePanel.vue +2 -2
  54. package/components/tabs/SegmentedControl.jsx +2 -2
  55. package/components/tabs/SegmentedControl.vue +2 -2
  56. package/index.html +1 -1
  57. package/package.json +4 -2
  58. package/tokens/dark.css +1 -1
  59. package/tokens/interaction.css +4 -0
  60. package/tokens/layout.css +3 -0
@@ -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: "0 1px 2px rgba(0,0,0,.18)", transition: "left 160ms ease-out", display: "flex", alignItems: "center", justifyContent: "center", color: track, fontSize: TEXT, lineHeight: 1, fontWeight: "var(--weight-semibold)", fontFamily: "var(--font-sans)", overflow: "hidden" }}>
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 160ms ease-out", pointerEvents: "none", whiteSpace: "nowrap" });
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 160ms ease-out", cursor: inert ? (loading ? "progress" : "not-allowed") : "pointer", flexShrink: 0, verticalAlign: "middle", outline: "none" }}>
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 160ms ease-out" }} onClick={flip}>{text}</span>;
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 160ms 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 160ms ease-out', cursor: inert.value ? 'default' : 'pointer', userSelect: 'none' })
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 160ms ease-out', cursor: inert ? (isLoading ? 'progress' : 'not-allowed') : 'pointer', flexShrink: 0, verticalAlign: 'middle', outline: 'none' }">
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: '0 1px 2px rgba(0,0,0,.18)', transition: 'left 160ms ease-out', display: 'flex', alignItems: 'center', justifyContent: 'center', color: track, fontSize: TEXT, lineHeight: 1, fontWeight: 'var(--weight-semibold)', fontFamily: 'var(--font-sans)', overflow: 'hidden' }">
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 .15s ease", ...style }}>
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 .15s ease', ...style }">
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,10 @@ const props = defineProps({
33
33
  reorder: Boolean,
34
34
  loadData: Function, rowActions: Function, style: Object,
35
35
  })
36
- const emit = defineEmits(['select', 'check', 'expand', 'drop'])
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) } }
37
40
  const Cell = (p) => p.v
38
41
  Cell.props = ['v']
39
42
 
@@ -221,7 +224,7 @@ function parts(text) {
221
224
  const dropAt = k => (drop.value && drop.value.key === k ? drop.value.pos : null)
222
225
  </script>
223
226
  <template>
224
- <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 }">
225
228
  <div v-for="row in rows" :key="row.key" role="treeitem" class="kb-tree-row"
226
229
  :tabindex="focus === row.key || (!focus && row === rows[0]) ? 0 : -1"
227
230
  :aria-expanded="isBranch(row.n) ? openKeys.includes(row.key) : undefined"
@@ -229,7 +232,7 @@ const dropAt = k => (drop.value && drop.value.key === k ? drop.value.pos : null)
229
232
  :draggable="draggable && !row.n.disabled"
230
233
  @dragstart="dragKey = row.key" @dragover="onDragOver($event, row.key)" @dragleave="drop = null"
231
234
  @drop.prevent="onDrop(row.key)" @dragend="dragKey = null; drop = null"
232
- @click="rowClick(row.n)" @keydown="keyDown($event, row)"
235
+ @click="rowClick(row.n)" @contextmenu="onCtx($event, row.key)" @keydown="keyDown($event, row)"
233
236
  :class="{ 'is-disabled': row.n.disabled }"
234
237
  :style="{
235
238
  position: 'relative', minHeight: 'var(--h-tree-item)', borderRadius: 'var(--radius-md)', display: 'flex', alignItems: 'center', gap: '6px',
@@ -255,7 +258,7 @@ const dropAt = k => (drop.value && drop.value.key === k ? drop.value.pos : null)
255
258
  <span v-else :style="{ width: showLine ? 0 : '14px', flexShrink: 0 }" />
256
259
  <span v-if="checkable && row.n.checkable !== false" @click.stop style="display:inline-flex;flex-shrink:0">
257
260
  <Checkbox :checked="view.full.has(row.key)" :indeterminate="view.half.has(row.key)"
258
- :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)" />
259
262
  </span>
260
263
  <span v-if="showIcon" :style="{ color: isBranch(row.n) ? 'var(--text-muted)' : docHues[row.n.hue || 'gray'], display: 'inline-flex', flexShrink: 0 }">
261
264
  <Icon :name="row.n.icon || (isBranch(row.n) ? (openKeys.includes(row.key) ? 'folder-open' : 'folder') : 'file-text')" :size="15" />
@@ -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 .15s ease", ...style }}>
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 .15s ease', ...style }">
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 .12s ease', ...style }">
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 .12s ease", ...style }}>
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 .15s ease" }}><Icon name="chevron-down" size={14} /></span>
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 .15s ease' }"><Icon name="chevron-down" :size="14" /></span>
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 ? "0 1px 2px rgba(0,0,0,.08)" : "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", transition: "var(--t-hover)" }}>
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 ? '0 1px 2px rgba(0,0,0,.08)' : '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', transition: 'var(--t-hover)' }">
25
- <Icon v-if="o.icon" :name="o.icon" :size="16" :style="tierIcon" /><template v-if="!iconOnly">{{ o.label }}<CountBadge :count="o.count" :selected="modelValue === o.label" :show-zero="o.showZero ?? showZero" /></template>
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
@@ -176,7 +176,7 @@ section{margin-bottom:52px;scroll-margin-top:78px}
176
176
  const GROUPS=[["Brand","品牌","ブランド"],["Colors","颜色","カラー"],["Type","字体排印","タイポグラフィ"],["Foundations","基础规范","基盤仕様"],["Components","组件","コンポーネント"],["Charts","图表","チャート"],["Icons","图标库","アイコン"]];
177
177
 
178
178
  const S={
179
- brandsub:["Design System · v2.5.0","设计系统 · v2.5.0","デザインシステム · v2.5.0"],
179
+ brandsub:["Design System · v2.5.1","设计系统 · v2.5.1","デザインシステム · v2.5.1"],
180
180
  doc_readme:["Readme","说明文档","ドキュメント"],
181
181
  doc_design:["DESIGN.md","下载 DESIGN.md","DESIGN.md"],
182
182
  doc_integration:["Integration","接入指南","導入ガイド"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keyfunc/keybricks-ds",
3
- "version": "2.5.0",
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}
@@ -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 */