@keyfunc/keybricks-ds 1.0.3 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/DEPLOY.md +4 -4
  2. package/DESIGN.md +154 -53
  3. package/INTEGRATION.md +176 -36
  4. package/SKILL.md +6 -4
  5. package/_adherence.oxlintrc.json +183 -72
  6. package/_scripts/build-index.mjs +9 -0
  7. package/_scripts/build-tokens.mjs +5 -2
  8. package/assets/icons/icon-index.js +1 -1
  9. package/assets/icons/icon-index.json +1 -1
  10. package/components/_cards/accordion.card.html +2 -2
  11. package/components/_cards/action-chip.card.html +8 -3
  12. package/components/_cards/activity.card.html +2 -2
  13. package/components/_cards/alert.card.html +2 -2
  14. package/components/_cards/app-chrome.card.html +4 -4
  15. package/components/_cards/banner.card.html +1 -1
  16. package/components/_cards/breadcrumb.card.html +2 -2
  17. package/components/_cards/button-1-base.card.html +2 -2
  18. package/components/_cards/button-2-states.card.html +14 -14
  19. package/components/_cards/button-3-sizes-icons.card.html +1 -1
  20. package/components/_cards/button-4-group.card.html +5 -5
  21. package/components/_cards/canvas-zoom-controls.card.html +1 -1
  22. package/components/_cards/cards-indicators.card.html +2 -2
  23. package/components/_cards/charts-1-sparkline.card.html +2 -2
  24. package/components/_cards/charts-10-gauge.card.html +4 -4
  25. package/components/_cards/charts-11-funnel.card.html +2 -2
  26. package/components/_cards/charts-12-treemap.card.html +2 -2
  27. package/components/_cards/charts-14-radar.card.html +1 -1
  28. package/components/_cards/charts-15-distribution.card.html +1 -1
  29. package/components/_cards/charts-16-pareto.card.html +2 -2
  30. package/components/_cards/charts-17-candlestick.card.html +2 -2
  31. package/components/_cards/charts-18-sankey.card.html +2 -2
  32. package/components/_cards/charts-19-chord.card.html +2 -2
  33. package/components/_cards/charts-2-line.card.html +2 -2
  34. package/components/_cards/charts-20-sunburst.card.html +2 -2
  35. package/components/_cards/charts-21-worldmap.card.html +2 -2
  36. package/components/_cards/charts-22-gantt.card.html +2 -2
  37. package/components/_cards/charts-23-bar3d.card.html +2 -2
  38. package/components/_cards/charts-24-pie3d.card.html +2 -2
  39. package/components/_cards/charts-25-surface3d.card.html +2 -2
  40. package/components/_cards/charts-26-scatter3d.card.html +2 -2
  41. package/components/_cards/charts-27-ribbon3d.card.html +2 -2
  42. package/components/_cards/charts-3-bar.card.html +2 -2
  43. package/components/_cards/charts-4-barlist.card.html +2 -2
  44. package/components/_cards/charts-5-donut.card.html +3 -3
  45. package/components/_cards/charts-6-scatter.card.html +2 -2
  46. package/components/_cards/charts-7-heatmap.card.html +2 -2
  47. package/components/_cards/charts-8-waterfall.card.html +2 -2
  48. package/components/_cards/charts-9-bullet.card.html +2 -2
  49. package/components/_cards/charts-99-fullpage.card.html +5 -5
  50. package/components/_cards/checkbox.card.html +4 -4
  51. package/components/_cards/codeblock.card.html +1 -1
  52. package/components/_cards/combobox.card.html +3 -2
  53. package/components/_cards/context-menu.card.html +3 -2
  54. package/components/_cards/date-picker.card.html +5 -2
  55. package/components/_cards/diffvalue.card.html +2 -2
  56. package/components/_cards/divider.card.html +3 -3
  57. package/components/_cards/drawer.card.html +2 -2
  58. package/components/_cards/dropdown-menu.card.html +6 -6
  59. package/components/_cards/dropzone.card.html +1 -1
  60. package/components/_cards/empty-state.card.html +5 -5
  61. package/components/_cards/filter-bar.card.html +5 -5
  62. package/components/_cards/icon-button.card.html +2 -2
  63. package/components/_cards/icons-1-core.card.html +2 -2
  64. package/components/_cards/icons-2-feather.card.html +3 -3
  65. package/components/_cards/icons-3-unicons.card.html +3 -3
  66. package/components/_cards/input.card.html +5 -2
  67. package/components/_cards/kbd.card.html +2 -2
  68. package/components/_cards/keyvalue.card.html +2 -2
  69. package/components/_cards/listrow.card.html +2 -2
  70. package/components/_cards/loading.card.html +2 -2
  71. package/components/_cards/log-viewer.card.html +2 -2
  72. package/components/_cards/modal.card.html +9 -9
  73. package/components/_cards/multi-select.card.html +3 -2
  74. package/components/_cards/number-stepper.card.html +3 -2
  75. package/components/_cards/pagination.card.html +3 -2
  76. package/components/_cards/people.card.html +3 -3
  77. package/components/_cards/pipeline-node.card.html +2 -2
  78. package/components/_cards/pipeline-palette.card.html +4 -4
  79. package/components/_cards/radio.card.html +46 -0
  80. package/components/_cards/search.card.html +5 -2
  81. package/components/_cards/segmented-control.card.html +39 -0
  82. package/components/_cards/select.card.html +4 -4
  83. package/components/_cards/slider.card.html +1 -1
  84. package/components/_cards/smart-table.card.html +1 -1
  85. package/components/_cards/spreadsheet.card.html +2 -2
  86. package/components/_cards/stepper.card.html +2 -2
  87. package/components/_cards/table.card.html +103 -0
  88. package/components/_cards/tabs.card.html +3 -2
  89. package/components/_cards/tag.card.html +5 -2
  90. package/components/_cards/textarea.card.html +2 -2
  91. package/components/_cards/timeline.card.html +1 -1
  92. package/components/_cards/toast.card.html +4 -4
  93. package/components/_cards/toggles.card.html +45 -56
  94. package/components/_cards/tooltip.card.html +5 -5
  95. package/components/_cards/tree.card.html +2 -2
  96. package/components/_shared/src-buttons.js +1 -1
  97. package/components/_shared/src-charts.js +1 -1
  98. package/components/_shared/src-data.js +1 -1
  99. package/components/_shared/src-display.js +1 -1
  100. package/components/_shared/src-feedback.js +1 -1
  101. package/components/_shared/src-forms.js +1 -1
  102. package/components/_shared/src-navigation.js +1 -1
  103. package/components/_shared/src-pipeline.js +1 -1
  104. package/components/_shared/src-tabs.js +1 -1
  105. package/components/_shared/view-source.js +9 -4
  106. package/components/_vue/index.mjs +9 -5
  107. package/components/buttons/ActionChip.d.ts +3 -1
  108. package/components/buttons/ActionChip.jsx +4 -4
  109. package/components/buttons/ActionChip.vue +4 -4
  110. package/components/buttons/Button.d.ts +5 -2
  111. package/components/buttons/Button.jsx +15 -18
  112. package/components/buttons/Button.vue +17 -18
  113. package/components/buttons/ButtonGroup.vue +1 -1
  114. package/components/buttons/IconButton.d.ts +2 -1
  115. package/components/buttons/IconButton.jsx +9 -7
  116. package/components/buttons/IconButton.vue +10 -8
  117. package/components/buttons/buttons.prompt.md +6 -5
  118. package/components/charts/Bar3D.jsx +2 -2
  119. package/components/charts/Bar3D.vue +2 -2
  120. package/components/charts/BarChart.jsx +4 -4
  121. package/components/charts/BarChart.vue +4 -4
  122. package/components/charts/BarList.jsx +1 -1
  123. package/components/charts/BarList.vue +1 -1
  124. package/components/charts/BoxPlot.jsx +1 -1
  125. package/components/charts/BoxPlot.vue +2 -2
  126. package/components/charts/BulletChart.jsx +1 -1
  127. package/components/charts/BulletChart.vue +1 -1
  128. package/components/charts/CandlestickChart.jsx +2 -2
  129. package/components/charts/CandlestickChart.vue +3 -3
  130. package/components/charts/ChartKit.jsx +5 -5
  131. package/components/charts/ChartLegend.vue +16 -0
  132. package/components/charts/ChartTooltip.vue +16 -0
  133. package/components/charts/ChordChart.jsx +2 -2
  134. package/components/charts/ChordChart.vue +2 -2
  135. package/components/charts/GanttChart.jsx +2 -2
  136. package/components/charts/GanttChart.vue +2 -2
  137. package/components/charts/GaugeChart.jsx +2 -2
  138. package/components/charts/GaugeChart.vue +2 -2
  139. package/components/charts/HeatmapChart.jsx +2 -2
  140. package/components/charts/HeatmapChart.vue +2 -2
  141. package/components/charts/Histogram.jsx +2 -2
  142. package/components/charts/Histogram.vue +3 -3
  143. package/components/charts/LineChart.jsx +1 -1
  144. package/components/charts/LineChart.vue +3 -3
  145. package/components/charts/ParetoChart.jsx +3 -3
  146. package/components/charts/ParetoChart.vue +4 -4
  147. package/components/charts/PlotGrid.vue +18 -0
  148. package/components/charts/RadarChart.jsx +1 -1
  149. package/components/charts/RadarChart.vue +1 -1
  150. package/components/charts/Ribbon3D.jsx +2 -2
  151. package/components/charts/Ribbon3D.vue +2 -2
  152. package/components/charts/SankeyChart.jsx +2 -2
  153. package/components/charts/SankeyChart.vue +2 -2
  154. package/components/charts/Scatter3D.jsx +1 -1
  155. package/components/charts/Scatter3D.vue +1 -1
  156. package/components/charts/ScatterChart.jsx +3 -3
  157. package/components/charts/ScatterChart.vue +4 -4
  158. package/components/charts/Sparkline.jsx +1 -1
  159. package/components/charts/Sparkline.vue +1 -1
  160. package/components/charts/SunburstChart.jsx +1 -1
  161. package/components/charts/SunburstChart.vue +1 -1
  162. package/components/charts/Surface3D.jsx +2 -2
  163. package/components/charts/Surface3D.vue +2 -2
  164. package/components/charts/TreemapChart.jsx +2 -2
  165. package/components/charts/TreemapChart.vue +2 -2
  166. package/components/charts/WaterfallChart.jsx +2 -2
  167. package/components/charts/WaterfallChart.vue +3 -3
  168. package/components/charts/WorldMap.jsx +2 -2
  169. package/components/charts/WorldMap.vue +2 -2
  170. package/components/charts/XLabels.vue +11 -0
  171. package/components/data/CodeBlock.jsx +1 -1
  172. package/components/data/CodeBlock.vue +1 -1
  173. package/components/data/DiffValue.jsx +1 -1
  174. package/components/data/DiffValue.vue +1 -1
  175. package/components/data/FilterBar.d.ts +2 -0
  176. package/components/data/FilterBar.jsx +10 -9
  177. package/components/data/FilterBar.vue +9 -8
  178. package/components/data/KeyValue.jsx +3 -3
  179. package/components/data/KeyValue.vue +3 -3
  180. package/components/data/ListRow.jsx +2 -2
  181. package/components/data/ListRow.vue +2 -2
  182. package/components/data/Pagination.d.ts +5 -1
  183. package/components/data/Pagination.jsx +16 -12
  184. package/components/data/Pagination.vue +14 -9
  185. package/components/data/SmartTable.d.ts +4 -1
  186. package/components/data/SmartTable.jsx +16 -14
  187. package/components/data/SmartTable.vue +25 -22
  188. package/components/data/Spreadsheet.d.ts +2 -0
  189. package/components/data/Spreadsheet.jsx +12 -11
  190. package/components/data/Spreadsheet.vue +14 -13
  191. package/components/data/Table.d.ts +47 -0
  192. package/components/data/Table.jsx +85 -0
  193. package/components/data/Table.vue +63 -0
  194. package/components/data/data.prompt.md +3 -3
  195. package/components/display/Avatar.vue +3 -2
  196. package/components/display/AvatarGroup.vue +3 -2
  197. package/components/display/Badge.vue +1 -1
  198. package/components/display/Divider.vue +3 -3
  199. package/components/display/Donut.vue +3 -2
  200. package/components/display/IconTile.vue +2 -1
  201. package/components/display/Kbd.vue +1 -1
  202. package/components/display/ProgressBar.jsx +1 -1
  203. package/components/display/ProgressBar.vue +1 -1
  204. package/components/display/StatCard.vue +1 -1
  205. package/components/display/Tag.d.ts +3 -1
  206. package/components/display/Tag.jsx +4 -4
  207. package/components/display/Tag.vue +4 -4
  208. package/components/feedback/Accordion.jsx +2 -2
  209. package/components/feedback/Accordion.vue +2 -2
  210. package/components/feedback/ActivityItem.d.ts +3 -1
  211. package/components/feedback/ActivityItem.jsx +5 -5
  212. package/components/feedback/ActivityItem.vue +5 -4
  213. package/components/feedback/Alert.jsx +4 -4
  214. package/components/feedback/Alert.vue +3 -3
  215. package/components/feedback/Banner.jsx +5 -5
  216. package/components/feedback/Banner.vue +5 -5
  217. package/components/feedback/ContextMenu.d.ts +3 -1
  218. package/components/feedback/ContextMenu.jsx +5 -5
  219. package/components/feedback/ContextMenu.vue +5 -5
  220. package/components/feedback/DotActivity.vue +3 -3
  221. package/components/feedback/Drawer.d.ts +2 -0
  222. package/components/feedback/Drawer.jsx +6 -6
  223. package/components/feedback/Drawer.vue +22 -15
  224. package/components/feedback/EmptyState.vue +2 -2
  225. package/components/feedback/LogViewer.vue +1 -1
  226. package/components/feedback/Modal.d.ts +2 -0
  227. package/components/feedback/Modal.jsx +12 -11
  228. package/components/feedback/Modal.vue +10 -8
  229. package/components/feedback/Skeleton.vue +2 -1
  230. package/components/feedback/Spinner.vue +2 -1
  231. package/components/feedback/Stepper.vue +1 -1
  232. package/components/feedback/Timeline.jsx +1 -1
  233. package/components/feedback/Timeline.vue +1 -1
  234. package/components/feedback/Toast.vue +4 -4
  235. package/components/feedback/ToastStack.vue +11 -0
  236. package/components/feedback/Tooltip.vue +19 -0
  237. package/components/feedback/feedback.prompt.md +4 -4
  238. package/components/forms/Checkbox.d.ts +3 -2
  239. package/components/forms/Checkbox.jsx +7 -7
  240. package/components/forms/Checkbox.vue +8 -8
  241. package/components/forms/Combobox.d.ts +2 -0
  242. package/components/forms/Combobox.jsx +12 -10
  243. package/components/forms/Combobox.vue +11 -10
  244. package/components/forms/DatePicker.d.ts +4 -0
  245. package/components/forms/DatePicker.jsx +16 -12
  246. package/components/forms/DatePicker.vue +15 -11
  247. package/components/forms/Dropzone.jsx +2 -2
  248. package/components/forms/Dropzone.vue +5 -5
  249. package/components/forms/Field.d.ts +5 -1
  250. package/components/forms/Field.jsx +4 -4
  251. package/components/forms/Field.vue +4 -4
  252. package/components/forms/Input.d.ts +5 -1
  253. package/components/forms/Input.jsx +7 -7
  254. package/components/forms/Input.vue +3 -3
  255. package/components/forms/MultiSelect.d.ts +2 -0
  256. package/components/forms/MultiSelect.jsx +13 -11
  257. package/components/forms/MultiSelect.vue +12 -11
  258. package/components/forms/NumberStepper.d.ts +3 -1
  259. package/components/forms/NumberStepper.jsx +7 -7
  260. package/components/forms/NumberStepper.vue +8 -7
  261. package/components/forms/Radio.d.ts +30 -0
  262. package/components/forms/Radio.jsx +65 -0
  263. package/components/forms/Radio.vue +17 -9
  264. package/components/forms/RadioGroup.vue +22 -4
  265. package/components/forms/SearchInput.d.ts +3 -1
  266. package/components/forms/SearchInput.jsx +6 -6
  267. package/components/forms/SearchInput.vue +7 -6
  268. package/components/forms/Select.d.ts +3 -2
  269. package/components/forms/Select.jsx +6 -7
  270. package/components/forms/Select.vue +6 -6
  271. package/components/forms/Slider.jsx +3 -3
  272. package/components/forms/Slider.vue +3 -3
  273. package/components/forms/Switch.d.ts +32 -24
  274. package/components/forms/Switch.jsx +85 -35
  275. package/components/forms/Switch.vue +57 -11
  276. package/components/forms/Textarea.vue +4 -4
  277. package/components/forms/forms.prompt.md +4 -1
  278. package/components/forms/input-base.js +2 -1
  279. package/components/index.d.ts +2 -2
  280. package/components/index.js +5 -5
  281. package/components/navigation/Breadcrumb.jsx +3 -3
  282. package/components/navigation/Breadcrumb.vue +4 -4
  283. package/components/navigation/DocTree.jsx +3 -3
  284. package/components/navigation/DocTree.vue +3 -3
  285. package/components/navigation/PageHeading.vue +1 -1
  286. package/components/navigation/PageToolbar.vue +7 -6
  287. package/components/navigation/Sidebar.jsx +1 -1
  288. package/components/navigation/Sidebar.vue +3 -3
  289. package/components/navigation/ThemeToggle.vue +2 -1
  290. package/components/navigation/TopBar.d.ts +3 -1
  291. package/components/navigation/TopBar.jsx +6 -6
  292. package/components/navigation/Tree.jsx +5 -5
  293. package/components/navigation/Tree.vue +5 -5
  294. package/components/pipeline/ConfigPanel.d.ts +2 -0
  295. package/components/pipeline/ConfigPanel.jsx +9 -9
  296. package/components/pipeline/ConfigPanel.vue +9 -9
  297. package/components/pipeline/DataPreview.d.ts +3 -1
  298. package/components/pipeline/DataPreview.jsx +7 -7
  299. package/components/pipeline/DataPreview.vue +7 -7
  300. package/components/pipeline/NodeCard.jsx +3 -3
  301. package/components/pipeline/NodeCard.vue +3 -3
  302. package/components/pipeline/PaletteItem.vue +2 -2
  303. package/components/pipeline/PaletteListItem.vue +1 -1
  304. package/components/pipeline/PalettePanel.jsx +2 -2
  305. package/components/pipeline/PalettePanel.vue +1 -1
  306. package/components/pipeline/Port.vue +15 -0
  307. package/components/pipeline/PortLabel.vue +15 -0
  308. package/components/pipeline/ZoomControls.vue +1 -1
  309. package/components/pipeline/port-skin.js +13 -0
  310. package/components/px.js +4 -0
  311. package/components/tabs/PanelTabs.vue +5 -4
  312. package/components/tabs/PillTabs.vue +5 -4
  313. package/components/tabs/SegmentedControl.d.ts +11 -4
  314. package/components/tabs/SegmentedControl.jsx +25 -10
  315. package/components/tabs/SegmentedControl.vue +19 -7
  316. package/components/tabs/Tabs.d.ts +5 -3
  317. package/components/tabs/Tabs.jsx +15 -15
  318. package/components/tabs/Tabs.vue +5 -5
  319. package/components/tabs/ViewToggle.d.ts +3 -1
  320. package/components/tabs/ViewToggle.jsx +5 -5
  321. package/components/tabs/ViewToggle.vue +5 -5
  322. package/components/tabs/tabs.prompt.md +4 -2
  323. package/keybricks.css +4 -0
  324. package/package.json +6 -3
  325. package/readme.md +90 -123
  326. package/readme.zh-CN.md +120 -0
  327. package/tokens/charts.css +20 -17
  328. package/tokens/colors.css +2 -2
  329. package/tokens/dark.css +2 -2
  330. package/tokens/hover.css +32 -0
  331. package/tokens/interaction.css +3 -1
  332. package/tokens/layout.css +29 -7
  333. package/tokens/tokens.d.ts +36 -0
  334. package/tokens/tokens.js +143 -89
  335. package/tokens/tokens.json +240 -106
  336. package/tokens/typography.css +27 -14
  337. package/components/_cards/brand-visual.card.html +0 -43
  338. package/components/_cards/datatable.card.html +0 -48
  339. package/components/_shared/src-brand.js +0 -1
  340. package/components/brand/BrandVisual.d.ts +0 -17
  341. package/components/brand/BrandVisual.jsx +0 -29
  342. package/components/brand/BrandVisual.vue +0 -34
  343. package/components/brand/_dir.card.html +0 -13
  344. package/components/brand/brandMotifs.js +0 -210
  345. package/components/buttons/_dir.card.html +0 -13
  346. package/components/charts/ChartKit.vue +0 -47
  347. package/components/charts/_dir.card.html +0 -7
  348. package/components/data/DataTable.d.ts +0 -28
  349. package/components/data/DataTable.jsx +0 -32
  350. package/components/data/DataTable.vue +0 -25
  351. package/components/data/_dir.card.html +0 -13
  352. package/components/display/_dir.card.html +0 -13
  353. package/components/feedback/_dir.card.html +0 -13
  354. package/components/forms/_dir.card.html +0 -13
  355. package/components/icons/_dir.card.html +0 -13
  356. package/components/navigation/_dir.card.html +0 -13
  357. package/components/pipeline/_dir.card.html +0 -13
  358. package/components/tabs/_dir.card.html +0 -13
@@ -0,0 +1,15 @@
1
+ <script setup>
2
+ import { ref } from 'vue'
3
+ import Icon from '../icons/Icon.vue'
4
+ import { portSkin } from './port-skin.js'
5
+ // Labeled input port pill ("A >", "B >") — 28×22, same three tones as Port.
6
+ defineProps({ label: { type: String, default: 'A' }, tone: { type: String, default: 'default' }, interactive: Boolean, style: Object })
7
+ const hover = ref(false)
8
+ </script>
9
+ <template>
10
+ <span @mouseenter="hover = true" @mouseleave="hover = false"
11
+ :style="{ width: '28px', height: '22px', borderRadius: 'var(--radius-full)', boxSizing: 'border-box', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '2px', fontFamily: 'var(--font-sans)', fontSize: 'var(--text-xs)', letterSpacing: 'var(--tracking-xs)', lineHeight: 1, ...portSkin(tone, hover, interactive), ...style }">
12
+ <Icon v-if="tone === 'target'" name="plus" :size="12" :stroke-width="2.2" />
13
+ <template v-else>{{ label }}<Icon name="chevron-right" :size="9" :stroke-width="2.5" /></template>
14
+ </span>
15
+ </template>
@@ -5,7 +5,7 @@ const props = defineProps({ modelValue: { type: Number, default: 100 }, style: O
5
5
  const emit = defineEmits(['update:modelValue', 'fit'])
6
6
  </script>
7
7
  <template>
8
- <div :style="{ display: 'inline-flex', alignItems: 'center', gap: 2, background: 'var(--bg-surface)', border: '1px solid var(--border-control)', borderRadius: 'var(--radius-md)', boxShadow: 'var(--shadow-menu)', padding: 3, boxSizing: 'border-box', ...style }">
8
+ <div :style="{ display: 'inline-flex', alignItems: 'center', gap: '2px', background: 'var(--bg-surface)', border: '1px solid var(--border-control)', borderRadius: 'var(--radius-md)', boxShadow: 'var(--shadow-menu)', padding: '3px', boxSizing: 'border-box', ...style }">
9
9
  <IconButton icon="minus" size="sm" label="Zoom out" @click="emit('update:modelValue', Math.max(25, modelValue - 10))" />
10
10
  <span style="min-width:40px;text-align:center;font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ modelValue }}%</span>
11
11
  <IconButton icon="plus" size="sm" label="Zoom in" @click="emit('update:modelValue', Math.min(200, modelValue + 10))" />
@@ -0,0 +1,13 @@
1
+ // Shared port skin for Port.vue / PortLabel.vue / NodeCard.vue — mirrors portSkin() in NodeCard.jsx.
2
+ // tone: "default" | "active" (a wire is being dragged from it) | "target" (valid drop here).
3
+ export function portSkin(tone, hover, interactive) {
4
+ const hot = tone === 'active' || tone === 'target' || (interactive && hover)
5
+ return {
6
+ background: tone === 'target' ? 'var(--soft-bg)' : 'var(--bg-surface)',
7
+ border: `2px solid ${hot ? 'var(--action-primary)' : 'var(--port-border)'}`,
8
+ boxShadow: tone === 'active' || tone === 'target' ? '0 0 0 3px var(--focus-ring)' : 'none',
9
+ color: hot ? 'var(--action-primary)' : 'var(--text-muted)',
10
+ cursor: interactive ? (tone === 'active' ? 'grabbing' : 'grab') : 'default',
11
+ transition: 'var(--t-hover)'
12
+ }
13
+ }
@@ -0,0 +1,4 @@
1
+ // Vue's :style binding does not append "px" to numbers the way React does —
2
+ // { height: 30 } is silently dropped by the browser. Wrap any numeric length in px().
3
+ // Strings ('100%', 'var(--radius-md)', '12px') pass through untouched.
4
+ export const px = (v) => (typeof v === 'number' ? v + 'px' : v)
@@ -1,10 +1,11 @@
1
1
  <script setup>
2
- defineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })
2
+ // Panel tabs (Node Info · Calc Rules · …): a lighter underline tab bar at the region's bar height with regular-weight control text. Put the panel in an sm region for the compact 32px/12px look.
3
+ defineProps({ tabs: { type: Array, default: () => [] }, active: String, size: String, style: Object })
3
4
  const emit = defineEmits(['change'])
4
5
  </script>
5
6
  <template>
6
- <div :style="{ display: 'flex', borderBottom: '1px solid var(--border-subtle)', ...style }">
7
- <button v-for="t in tabs" :key="t" type="button" @click="emit('change', t)"
8
- :style="{ background: 'transparent', border: 'none', cursor: 'pointer', padding: '8px 12px', marginBottom: -1, borderBottom: t === active ? '2px solid var(--action-primary)' : '2px solid transparent', fontFamily: 'var(--font-sans)', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', color: t === active ? 'var(--action-primary)' : 'var(--text-secondary)' }">{{ t }}</button>
7
+ <div role="tablist" :data-size="size" :style="{ display: 'flex', borderBottom: '1px solid var(--border-subtle)', ...style }">
8
+ <button v-for="t in tabs" :key="t" type="button" role="tab" :aria-selected="t === active" :data-ink="t === active ? undefined : ''" @click="emit('change', t)"
9
+ :style="{ background: 'transparent', border: 'none', cursor: 'pointer', height: 'var(--bar-h)', boxSizing: 'border-box', padding: '0 var(--control-px)', marginBottom: '-1px', borderBottom: t === active ? '2px solid var(--action-primary)' : '2px solid transparent', fontFamily: 'var(--font-sans)', fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', color: t === active ? 'var(--action-primary)' : 'var(--ink, var(--text-secondary))', whiteSpace: 'nowrap' }">{{ t }}</button>
9
10
  </div>
10
11
  </template>
@@ -1,10 +1,11 @@
1
1
  <script setup>
2
- defineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })
2
+ // Text pill filter tabs. Active = tinted pill. Pills are control-height (sm 24 · md 32 · lg 36); omit `size` to inherit.
3
+ defineProps({ tabs: { type: Array, default: () => [] }, active: String, size: String, style: Object })
3
4
  const emit = defineEmits(['change'])
4
5
  </script>
5
6
  <template>
6
- <div :style="{ display: 'flex', gap: 8, ...style }">
7
- <button v-for="t in tabs" :key="t" type="button" @click="emit('change', t)"
8
- :style="{ background: t === active ? 'var(--bg-active)' : 'transparent', border: 'none', cursor: 'pointer', height: 28, padding: '0 11px', borderRadius: 'var(--radius-md)', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', color: t === active ? 'var(--text-heading)' : 'var(--text-secondary)' }">{{ t }}</button>
7
+ <div role="tablist" :data-size="size" :style="{ display: 'flex', gap: 'var(--control-gap)', ...style }">
8
+ <button v-for="t in tabs" :key="t" type="button" role="tab" :aria-selected="t === active" :data-ink="t === active ? undefined : ''" :data-wash="t === active ? undefined : ''" @click="emit('change', t)"
9
+ :style="{ background: t === active ? 'var(--bg-active)' : 'var(--wash, transparent)', border: 'none', cursor: 'pointer', height: 'var(--control-h)', padding: '0 var(--control-px)', borderRadius: 'var(--radius-md)', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', color: t === active ? 'var(--text-heading)' : 'var(--ink, var(--text-secondary))', whiteSpace: 'nowrap' }">{{ t }}</button>
9
10
  </div>
10
11
  </template>
@@ -1,11 +1,18 @@
1
1
  import * as React from "react";
2
- /** Inset segmented control with a raised active segment. Addition — not in the source file. */
2
+ /** Inset segmented control with a raised active segment — switches views of the same data, applies instantly. ← → move between enabled segments. */
3
3
  export interface SegmentedControlProps {
4
- /** Plain labels, or {label, icon} entries */
5
- options?: (string | { label: string; icon?: string })[];
4
+ /** Plain labels, or { label, icon?, disabled? } entries. Value is matched by label. */
5
+ options?: (string | { label: string; icon?: string; disabled?: boolean })[];
6
6
  value?: string;
7
7
  onChange?: (value: string) => void;
8
- size?: "sm" | "md";
8
+ /** Size tier: sm 24 · md 32 · lg 36. Omit to inherit the enclosing region's tier (the default). */
9
+ size?: "sm" | "md" | "lg";
10
+ /** Hide labels; each label becomes the segment's aria-label and tooltip */
11
+ iconOnly?: boolean;
12
+ /** Greys the whole control */
13
+ disabled?: boolean;
14
+ /** Stretch to the container, segments share the width evenly */
15
+ fullWidth?: boolean;
9
16
  style?: React.CSSProperties;
10
17
  }
11
18
  export declare function SegmentedControl(props: SegmentedControlProps): JSX.Element;
@@ -1,18 +1,33 @@
1
1
  import React from "react";
2
2
  import { Icon } from "../icons/Icon.jsx";
3
3
 
4
- /** Segmented control — inset track, raised active segment. options: string[] or [{label,icon}]. */
5
- export function SegmentedControl({ options = [], value, onChange, size = "md", style }) {
6
- const h = size === "sm" ? 26 : 32;
4
+ /**
5
+ * Segmented control — inset track, raised active segment. Switches views of the same data, applies instantly.
6
+ * options: string[] | { label, icon?, disabled? }[] value/onChange by label
7
+ * size sm 24 | md 32 | lg 36 — omit it to inherit the region's tier · iconOnly hides labels (label becomes aria-label / title)
8
+ * disabled greys the whole control · fullWidth stretches segments evenly
9
+ * Keyboard: ← → move between enabled segments.
10
+ */
11
+ const tierIcon = { width: "var(--control-icon)", height: "var(--control-icon)" };
12
+ export function SegmentedControl({ options = [], value, onChange, size, iconOnly, disabled, fullWidth, style }) {
13
+ const opts = options.map((o) => (typeof o === "string" ? { label: o } : o));
14
+ const enabled = opts.filter((o) => !o.disabled);
15
+ const onKey = (e) => {
16
+ if (e.key !== "ArrowLeft" && e.key !== "ArrowRight" || disabled || !enabled.length) return;
17
+ e.preventDefault();
18
+ const i = enabled.findIndex((o) => o.label === value);
19
+ const n = e.key === "ArrowRight" ? enabled[(i + 1) % enabled.length] : enabled[(i - 1 + enabled.length) % enabled.length];
20
+ onChange && onChange(n.label);
21
+ };
7
22
  return (
8
- <div style={{ display: "inline-flex", padding: 2, gap: 2, background: "var(--bg-inset)", border: "1px solid var(--border-control)", borderRadius: "var(--radius-md)", boxSizing: "border-box", ...style }}>
9
- {options.map((o) => {
10
- const label = typeof o === "string" ? o : o.label;
11
- const is = value === label;
23
+ <div role="tablist" data-size={size} aria-disabled={disabled || undefined} onKeyDown={onKey} style={{ display: fullWidth ? "flex" : "inline-flex", height: "var(--control-h)", padding: 2, gap: 2, background: "var(--bg-inset)", border: "1px solid var(--border-control)", borderRadius: "var(--radius-md)", boxSizing: "border-box", opacity: disabled ? .5 : 1, ...style }}>
24
+ {opts.map((o) => {
25
+ const is = value === o.label, off = disabled || o.disabled;
12
26
  return (
13
- <button key={label} type="button" onClick={() => onChange && onChange(label)}
14
- style={{ height: h - 6, padding: "0 10px", display: "inline-flex", alignItems: "center", gap: 6, borderRadius: "var(--radius-sm)", border: "none", cursor: "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: size === "sm" ? "var(--text-xs)" : "var(--text-sm)", color: is ? "var(--text-heading)" : "var(--text-secondary)", whiteSpace: "nowrap", transition: "var(--t-hover)" }}>
15
- {typeof o === "object" && o.icon ? <Icon name={o.icon} size={13} /> : null}{label}
27
+ <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 : ""}
28
+ onClick={() => !off && onChange && onChange(o.label)}
29
+ 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)" }}>
30
+ {o.icon ? <Icon name={o.icon} size={16} style={tierIcon} /> : null}{iconOnly ? null : o.label}
16
31
  </button>
17
32
  );
18
33
  })}
@@ -1,15 +1,27 @@
1
1
  <script setup>
2
+ import { computed } from 'vue'
2
3
  import Icon from '../icons/Icon.vue'
3
- // Segmented control — inset track, raised active segment. options: string[] or [{label,icon}].
4
- const props = defineProps({ options: { type: Array, default: () => [] }, modelValue: String, size: { type: String, default: 'md' }, style: Object })
4
+ // Segmented control — inset track, raised active segment. Switches views of the same data, applies instantly.
5
+ // options: string[] | { label, icon?, disabled? }[] · v-model by label · size sm 24 | md 32 | lg 36 — omit it to inherit the region's tier
6
+ // iconOnly hides labels (label → aria-label/title) · disabled greys the control · fullWidth stretches segments · ← → keyboard.
7
+ const tierIcon = { width: 'var(--control-icon)', height: 'var(--control-icon)' }
8
+ const props = defineProps({ options: { type: Array, default: () => [] }, modelValue: String, size: String, iconOnly: Boolean, disabled: Boolean, fullWidth: Boolean, style: Object })
5
9
  const emit = defineEmits(['update:modelValue'])
6
- function label(o) { return typeof o === 'string' ? o : o.label }
10
+ const opts = computed(() => props.options.map(o => typeof o === 'string' ? { label: o } : o))
11
+ const enabled = computed(() => opts.value.filter(o => !o.disabled))
12
+ const pick = (o) => { if (!(props.disabled || o.disabled)) emit('update:modelValue', o.label) }
13
+ function onKey(e) {
14
+ if ((e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') || props.disabled || !enabled.value.length) return
15
+ e.preventDefault()
16
+ const i = enabled.value.findIndex(o => o.label === props.modelValue), n = enabled.value.length
17
+ emit('update:modelValue', (e.key === 'ArrowRight' ? enabled.value[(i + 1) % n] : enabled.value[(i - 1 + n) % n]).label)
18
+ }
7
19
  </script>
8
20
  <template>
9
- <div :style="{ display: 'inline-flex', padding: 2, gap: 2, background: 'var(--bg-inset)', border: '1px solid var(--border-control)', borderRadius: 'var(--radius-md)', boxSizing: 'border-box', ...style }">
10
- <button v-for="o in options" :key="label(o)" type="button" @click="emit('update:modelValue', label(o))"
11
- :style="{ height: (size === 'sm' ? 26 : 32) - 6, padding: '0 10px', display: 'inline-flex', alignItems: 'center', gap: 6, borderRadius: 'var(--radius-sm)', border: 'none', cursor: 'pointer', background: modelValue === label(o) ? 'var(--bg-surface)' : 'transparent', boxShadow: modelValue === label(o) ? '0 1px 2px rgba(0,0,0,.08)' : 'none', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: size === 'sm' ? 'var(--text-xs)' : 'var(--text-sm)', color: modelValue === label(o) ? 'var(--text-heading)' : 'var(--text-secondary)', whiteSpace: 'nowrap', transition: 'var(--t-hover)' }">
12
- <Icon v-if="typeof o === 'object' && o.icon" :name="o.icon" :size="13" />{{ label(o) }}
21
+ <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 }">
22
+ <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)"
23
+ :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)' }">
24
+ <Icon v-if="o.icon" :name="o.icon" :size="16" :style="tierIcon" /><template v-if="!iconOnly">{{ o.label }}</template>
13
25
  </button>
14
26
  </div>
15
27
  </template>
@@ -1,13 +1,15 @@
1
1
  import * as React from "react";
2
- /** Underline tabs (Import/Export). Active: blue text + 2px blue underline on hairline baseline. */
2
+ /** Underline tabs (Import/Export). Active: blue text + 2px blue underline on hairline baseline. The bar is the region's bar height (sm 32 · md 40 · lg 44). */
3
3
  export interface TabsProps {
4
4
  tabs?: string[];
5
5
  active?: string;
6
6
  onChange?: (tab: string) => void;
7
+ /** Size tier. Omit to inherit the enclosing region's tier (the default). */
8
+ size?: "sm" | "md" | "lg";
7
9
  style?: React.CSSProperties;
8
10
  }
9
11
  export declare function Tabs(props: TabsProps): JSX.Element;
10
- /** 12px compact underline tabs for panels (Node Info / Calc Rules / ...). */
12
+ /** Lighter underline tabs for panels (Node Info / Calc Rules / ...), regular weight; an sm region gives the compact 32px/12px look. */
11
13
  export declare function PanelTabs(props: TabsProps): JSX.Element;
12
- /** Text pill filter tabs; active gets a #f5f5f5 pill. */
14
+ /** Text pill filter tabs at control height; active gets a tinted pill. */
13
15
  export declare function PillTabs(props: TabsProps): JSX.Element;
@@ -1,14 +1,14 @@
1
1
  import React from "react";
2
2
 
3
- /** Underline tabs on a hairline baseline (Import/Export). Active = blue text + 2px blue underline. */
4
- export function Tabs({ tabs = [], active, onChange, style }) {
3
+ /** Underline tabs on a hairline baseline (Import/Export). Active = blue text + 2px blue underline. The bar is the region's bar height (sm 32 · md 40 · lg 44) with the tier's control text; omit `size` to inherit. */
4
+ export function Tabs({ tabs = [], active, onChange, size, style }) {
5
5
  return (
6
- <div style={{ display: "flex", gap: 4, borderBottom: "1px solid var(--border-control)", ...style }}>
6
+ <div role="tablist" data-size={size} style={{ display: "flex", gap: 4, borderBottom: "1px solid var(--border-control)", ...style }}>
7
7
  {tabs.map((t) => {
8
8
  const is = t === active;
9
9
  return (
10
- <button key={t} type="button" onClick={() => onChange && onChange(t)}
11
- style={{ background: "transparent", border: "none", cursor: "pointer", padding: "9px 16px", marginBottom: -1, borderBottom: is ? "2px solid var(--action-primary)" : "2px solid transparent", fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: is ? "var(--action-primary)" : "var(--text-body)" }}>
10
+ <button key={t} type="button" role="tab" aria-selected={is} data-ink={is ? undefined : ""} onClick={() => onChange && onChange(t)}
11
+ style={{ background: "transparent", border: "none", cursor: "pointer", height: "var(--bar-h)", boxSizing: "border-box", padding: "0 var(--control-px)", marginBottom: -1, borderBottom: is ? "2px solid var(--action-primary)" : "2px solid transparent", fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--control-text)", lineHeight: "var(--control-leading)", letterSpacing: "var(--control-tracking)", color: is ? "var(--action-primary)" : "var(--ink, var(--text-body))", whiteSpace: "nowrap" }}>
12
12
  {t}
13
13
  </button>
14
14
  );
@@ -17,15 +17,15 @@ export function Tabs({ tabs = [], active, onChange, style }) {
17
17
  );
18
18
  }
19
19
 
20
- /** Compact panel tabs (Node Info · Calc Rules · Output Fields · SQL Preview), 12px. */
21
- export function PanelTabs({ tabs = [], active, onChange, style }) {
20
+ /** Panel tabs (Node Info · Calc Rules · Output Fields · SQL Preview): a lighter underline tab bar at the region's bar height with regular-weight control text. Put the panel in an sm region for the compact 32px/12px look. */
21
+ export function PanelTabs({ tabs = [], active, onChange, size, style }) {
22
22
  return (
23
- <div style={{ display: "flex", borderBottom: "1px solid var(--border-subtle)", ...style }}>
23
+ <div role="tablist" data-size={size} style={{ display: "flex", borderBottom: "1px solid var(--border-subtle)", ...style }}>
24
24
  {tabs.map((t) => {
25
25
  const is = t === active;
26
26
  return (
27
- <button key={t} type="button" onClick={() => onChange && onChange(t)}
28
- style={{ background: "transparent", border: "none", cursor: "pointer", padding: "8px 12px", marginBottom: -1, borderBottom: is ? "2px solid var(--action-primary)" : "2px solid transparent", fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", color: is ? "var(--action-primary)" : "var(--text-secondary)" }}>
27
+ <button key={t} type="button" role="tab" aria-selected={is} data-ink={is ? undefined : ""} onClick={() => onChange && onChange(t)}
28
+ style={{ background: "transparent", border: "none", cursor: "pointer", height: "var(--bar-h)", boxSizing: "border-box", padding: "0 var(--control-px)", marginBottom: -1, borderBottom: is ? "2px solid var(--action-primary)" : "2px solid transparent", fontFamily: "var(--font-sans)", fontSize: "var(--control-text)", lineHeight: "var(--control-leading)", letterSpacing: "var(--control-tracking)", color: is ? "var(--action-primary)" : "var(--ink, var(--text-secondary))", whiteSpace: "nowrap" }}>
29
29
  {t}
30
30
  </button>
31
31
  );
@@ -34,15 +34,15 @@ export function PanelTabs({ tabs = [], active, onChange, style }) {
34
34
  );
35
35
  }
36
36
 
37
- /** Text pill filter tabs (For Review · To be Submitted · For Approval). Active = #f5f5f5 pill. */
38
- export function PillTabs({ tabs = [], active, onChange, style }) {
37
+ /** Text pill filter tabs (For Review · To be Submitted · For Approval). Active = tinted pill. Pills are control-height (sm 24 · md 32 · lg 36); omit `size` to inherit. */
38
+ export function PillTabs({ tabs = [], active, onChange, size, style }) {
39
39
  return (
40
- <div style={{ display: "flex", gap: 8, ...style }}>
40
+ <div role="tablist" data-size={size} style={{ display: "flex", gap: "var(--control-gap)", ...style }}>
41
41
  {tabs.map((t) => {
42
42
  const is = t === active;
43
43
  return (
44
- <button key={t} type="button" onClick={() => onChange && onChange(t)}
45
- style={{ background: is ? "var(--bg-active)" : "transparent", border: "none", cursor: "pointer", height: 28, padding: "0 11px", borderRadius: "var(--radius-md)", fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", color: is ? "var(--text-heading)" : "var(--text-secondary)" }}>
44
+ <button key={t} type="button" role="tab" aria-selected={is} data-ink={is ? undefined : ""} data-wash={is ? undefined : ""} onClick={() => onChange && onChange(t)}
45
+ style={{ background: is ? "var(--bg-active)" : "var(--wash, transparent)", border: "none", cursor: "pointer", height: "var(--control-h)", padding: "0 var(--control-px)", borderRadius: "var(--radius-md)", 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))", whiteSpace: "nowrap" }}>
46
46
  {t}
47
47
  </button>
48
48
  );
@@ -1,11 +1,11 @@
1
1
  <script setup>
2
- // Underline tabs on a hairline baseline (Import/Export). Active = blue text + 2px blue underline.
3
- defineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })
2
+ // Underline tabs on a hairline baseline (Import/Export). Active = blue text + 2px blue underline. The bar is the region's bar height (sm 32 · md 40 · lg 44) with the tier's control text; omit `size` to inherit.
3
+ defineProps({ tabs: { type: Array, default: () => [] }, active: String, size: String, style: Object })
4
4
  const emit = defineEmits(['change'])
5
5
  </script>
6
6
  <template>
7
- <div :style="{ display: 'flex', gap: 4, borderBottom: '1px solid var(--border-control)', ...style }">
8
- <button v-for="t in tabs" :key="t" type="button" @click="emit('change', t)"
9
- :style="{ background: 'transparent', border: 'none', cursor: 'pointer', padding: '9px 16px', marginBottom: -1, borderBottom: t === active ? '2px solid var(--action-primary)' : '2px solid transparent', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-base)', lineHeight: 'var(--leading-base)', letterSpacing: 'var(--tracking-base)', color: t === active ? 'var(--action-primary)' : 'var(--text-body)' }">{{ t }}</button>
7
+ <div role="tablist" :data-size="size" :style="{ display: 'flex', gap: '4px', borderBottom: '1px solid var(--border-control)', ...style }">
8
+ <button v-for="t in tabs" :key="t" type="button" role="tab" :aria-selected="t === active" :data-ink="t === active ? undefined : ''" @click="emit('change', t)"
9
+ :style="{ background: 'transparent', border: 'none', cursor: 'pointer', height: 'var(--bar-h)', boxSizing: 'border-box', padding: '0 var(--control-px)', marginBottom: '-1px', borderBottom: t === active ? '2px solid var(--action-primary)' : '2px solid transparent', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', color: t === active ? 'var(--action-primary)' : 'var(--ink, var(--text-body))', whiteSpace: 'nowrap' }">{{ t }}</button>
10
10
  </div>
11
11
  </template>
@@ -1,7 +1,9 @@
1
1
  import * as React from "react";
2
- /** list/grid segmented icon toggle (26px, r4, active #f5f5f5). */
2
+ /** list/grid icon toggle — two IconButtons at the region's tier (sm 24 · md 32 · lg 36), active gets the tinted wash. */
3
3
  export interface ViewToggleProps {
4
4
  value?: "list" | "grid";
5
+ /** Size tier. Omit to inherit the enclosing region's tier (the default). */
6
+ size?: "sm" | "md" | "lg";
5
7
  onChange?: (v: "list" | "grid") => void;
6
8
  style?: React.CSSProperties;
7
9
  }
@@ -1,12 +1,12 @@
1
1
  import React from "react";
2
2
  import { IconButton } from "../buttons/IconButton.jsx";
3
3
 
4
- /** Two-state list/grid icon toggle (palette header). */
5
- export function ViewToggle({ value = "grid", onChange, style }) {
4
+ /** Two-state list/grid icon toggle (palette header). Two IconButtons at the region's tier; omit `size` to inherit. */
5
+ export function ViewToggle({ value = "grid", onChange, size, style }) {
6
6
  return (
7
- <div style={{ display: "inline-flex", gap: 2, ...style }}>
8
- <IconButton icon="list" size="sm" active={value === "list"} onClick={() => onChange && onChange("list")} label="List view" />
9
- <IconButton icon="layout-grid" size="sm" active={value === "grid"} onClick={() => onChange && onChange("grid")} label="Grid view" />
7
+ <div data-size={size} style={{ display: "inline-flex", gap: 2, ...style }}>
8
+ <IconButton icon="list" active={value === "list"} onClick={() => onChange && onChange("list")} label="List view" />
9
+ <IconButton icon="layout-grid" active={value === "grid"} onClick={() => onChange && onChange("grid")} label="Grid view" />
10
10
  </div>
11
11
  );
12
12
  }
@@ -1,12 +1,12 @@
1
1
  <script setup>
2
2
  import IconButton from '../buttons/IconButton.vue'
3
- // Two-state list/grid icon toggle (palette header).
4
- defineProps({ modelValue: { type: String, default: 'grid' }, style: Object })
3
+ // Two-state list/grid icon toggle (palette header). Two IconButtons at the region's tier; omit `size` to inherit.
4
+ defineProps({ modelValue: { type: String, default: 'grid' }, size: String, style: Object })
5
5
  const emit = defineEmits(['update:modelValue'])
6
6
  </script>
7
7
  <template>
8
- <div :style="{ display: 'inline-flex', gap: 2, ...style }">
9
- <IconButton icon="list" size="sm" :active="modelValue === 'list'" label="List view" @click="emit('update:modelValue', 'list')" />
10
- <IconButton icon="layout-grid" size="sm" :active="modelValue === 'grid'" label="Grid view" @click="emit('update:modelValue', 'grid')" />
8
+ <div :data-size="size" :style="{ display: 'inline-flex', gap: '2px', ...style }">
9
+ <IconButton icon="list" :active="modelValue === 'list'" label="List view" @click="emit('update:modelValue', 'list')" />
10
+ <IconButton icon="layout-grid" :active="modelValue === 'grid'" label="Grid view" @click="emit('update:modelValue', 'grid')" />
11
11
  </div>
12
12
  </template>
@@ -1,4 +1,4 @@
1
- Tab systems: `Tabs` (13px underline — page level), `PanelTabs` (12px underline — config panel), `PillTabs` (pill filters), `ViewToggle` (list/grid icons).
1
+ Tab systems: `Tabs` (underline — page level), `PanelTabs` (lighter underline — config panel, put the panel in an sm region), `PillTabs` (pill filters), `ViewToggle` (list/grid icons). All read the region's size tier: bars are 32 / 40 / 44px, pills 24 / 32 / 36px.
2
2
 
3
3
  ```jsx
4
4
  <Tabs tabs={["Import","Export"]} active={tab} onChange={setTab}/>
@@ -7,8 +7,10 @@ Tab systems: `Tabs` (13px underline — page level), `PanelTabs` (12px underline
7
7
  <ViewToggle value={view} onChange={setView}/>
8
8
  ```
9
9
 
10
- `SegmentedControl` (addition) for view/range switches:
10
+ `SegmentedControl` (addition) for view/range switches — same data, instant; never a saved value and never page navigation. `iconOnly`, per-option `disabled`, `fullWidth`:
11
11
 
12
12
  ```jsx
13
13
  <SegmentedControl options={[{label:"List",icon:"list"},{label:"Grid",icon:"layout-grid"}]} value={v} onChange={setV}/>
14
+ <SegmentedControl iconOnly options={[{icon:"table",label:"Table"},{icon:"chart-column",label:"Chart"}]} value={m} onChange={setM}/>
15
+ <SegmentedControl fullWidth options={["Overview","Details","History"]} value={t} onChange={setT}/>
14
16
  ```
package/keybricks.css ADDED
@@ -0,0 +1,4 @@
1
+ /* Consumer entry: tokens + the hover hooks that component inline styles read.
2
+ (hover.css stays outside styles.css so the design-system compiler does not register its state signals as tokens.) */
3
+ @import './styles.css';
4
+ @import './tokens/hover.css';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@keyfunc/keybricks-ds",
3
- "version": "1.0.3",
4
- "description": "Keybricks design system — 334 design tokens and 118 UI components in React and Vue 3, source-only.",
3
+ "version": "2.1.1",
4
+ "description": "Keybricks design system — 352 design tokens, three size tiers and 117 UI components in React and Vue 3, source-only.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
7
7
  "sideEffects": [
@@ -13,7 +13,8 @@
13
13
  "default": "./components/index.js"
14
14
  },
15
15
  "./vue": "./components/_vue/index.mjs",
16
- "./styles.css": "./styles.css",
16
+ "./styles.css": "./keybricks.css",
17
+ "./keybricks.css": "./keybricks.css",
17
18
  "./tokens": {
18
19
  "types": "./tokens/tokens.d.ts",
19
20
  "default": "./tokens/tokens.js"
@@ -31,7 +32,9 @@
31
32
  "assets",
32
33
  "_scripts",
33
34
  "styles.css",
35
+ "keybricks.css",
34
36
  "readme.md",
37
+ "readme.zh-CN.md",
35
38
  "DESIGN.md",
36
39
  "INTEGRATION.md",
37
40
  "DEPLOY.md",