@keyfunc/keybricks-ds 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEPLOY.md +204 -0
- package/DESIGN.md +269 -0
- package/INTEGRATION.md +417 -0
- package/SKILL.md +32 -0
- package/_adherence.oxlintrc.json +1469 -0
- package/_scripts/build-index.mjs +80 -0
- package/_scripts/build-tokens.mjs +135 -0
- package/assets/icons/icon-index.js +1 -0
- package/assets/icons/icon-index.json +1 -0
- package/assets/icons/unicons-index.js +1 -0
- package/assets/icons/unicons-index.json +1 -0
- package/assets/keybricks-logo.png +0 -0
- package/components/_cards/accordion.card.html +32 -0
- package/components/_cards/action-chip.card.html +22 -0
- package/components/_cards/activity.card.html +34 -0
- package/components/_cards/alert.card.html +32 -0
- package/components/_cards/app-chrome.card.html +36 -0
- package/components/_cards/banner.card.html +21 -0
- package/components/_cards/brand-visual.card.html +43 -0
- package/components/_cards/breadcrumb.card.html +23 -0
- package/components/_cards/button-1-base.card.html +39 -0
- package/components/_cards/button-2-states.card.html +56 -0
- package/components/_cards/button-3-sizes-icons.card.html +32 -0
- package/components/_cards/button-4-group.card.html +47 -0
- package/components/_cards/canvas-zoom-controls.card.html +19 -0
- package/components/_cards/cards-indicators.card.html +59 -0
- package/components/_cards/charts-1-sparkline.card.html +55 -0
- package/components/_cards/charts-10-gauge.card.html +31 -0
- package/components/_cards/charts-11-funnel.card.html +28 -0
- package/components/_cards/charts-12-treemap.card.html +23 -0
- package/components/_cards/charts-14-radar.card.html +31 -0
- package/components/_cards/charts-15-distribution.card.html +32 -0
- package/components/_cards/charts-16-pareto.card.html +25 -0
- package/components/_cards/charts-17-candlestick.card.html +34 -0
- package/components/_cards/charts-18-sankey.card.html +29 -0
- package/components/_cards/charts-19-chord.card.html +31 -0
- package/components/_cards/charts-2-line.card.html +46 -0
- package/components/_cards/charts-20-sunburst.card.html +38 -0
- package/components/_cards/charts-21-worldmap.card.html +33 -0
- package/components/_cards/charts-22-gantt.card.html +37 -0
- package/components/_cards/charts-23-bar3d.card.html +26 -0
- package/components/_cards/charts-24-pie3d.card.html +32 -0
- package/components/_cards/charts-25-surface3d.card.html +24 -0
- package/components/_cards/charts-26-scatter3d.card.html +24 -0
- package/components/_cards/charts-27-ribbon3d.card.html +23 -0
- package/components/_cards/charts-3-bar.card.html +71 -0
- package/components/_cards/charts-4-barlist.card.html +30 -0
- package/components/_cards/charts-5-donut.card.html +35 -0
- package/components/_cards/charts-6-scatter.card.html +26 -0
- package/components/_cards/charts-7-heatmap.card.html +28 -0
- package/components/_cards/charts-8-waterfall.card.html +25 -0
- package/components/_cards/charts-9-bullet.card.html +31 -0
- package/components/_cards/charts-99-fullpage.card.html +194 -0
- package/components/_cards/checkbox.card.html +34 -0
- package/components/_cards/codeblock.card.html +19 -0
- package/components/_cards/combobox.card.html +25 -0
- package/components/_cards/context-menu.card.html +21 -0
- package/components/_cards/datatable.card.html +48 -0
- package/components/_cards/date-picker.card.html +31 -0
- package/components/_cards/diffvalue.card.html +21 -0
- package/components/_cards/divider.card.html +24 -0
- package/components/_cards/drawer.card.html +30 -0
- package/components/_cards/dropdown-menu.card.html +38 -0
- package/components/_cards/dropzone.card.html +22 -0
- package/components/_cards/empty-state.card.html +29 -0
- package/components/_cards/filter-bar.card.html +34 -0
- package/components/_cards/icon-button.card.html +27 -0
- package/components/_cards/icons-1-core.card.html +49 -0
- package/components/_cards/icons-2-feather.card.html +39 -0
- package/components/_cards/icons-3-unicons.card.html +44 -0
- package/components/_cards/input.card.html +25 -0
- package/components/_cards/kbd.card.html +20 -0
- package/components/_cards/keyvalue.card.html +23 -0
- package/components/_cards/listrow.card.html +30 -0
- package/components/_cards/loading.card.html +33 -0
- package/components/_cards/log-viewer.card.html +23 -0
- package/components/_cards/modal.card.html +35 -0
- package/components/_cards/multi-select.card.html +26 -0
- package/components/_cards/number-stepper.card.html +24 -0
- package/components/_cards/pagination.card.html +25 -0
- package/components/_cards/people.card.html +46 -0
- package/components/_cards/pipeline-node.card.html +76 -0
- package/components/_cards/pipeline-palette.card.html +54 -0
- package/components/_cards/search.card.html +23 -0
- package/components/_cards/select.card.html +23 -0
- package/components/_cards/slider.card.html +25 -0
- package/components/_cards/smart-table.card.html +37 -0
- package/components/_cards/spreadsheet.card.html +31 -0
- package/components/_cards/stepper.card.html +25 -0
- package/components/_cards/tabs.card.html +25 -0
- package/components/_cards/tag.card.html +26 -0
- package/components/_cards/textarea.card.html +27 -0
- package/components/_cards/timeline.card.html +32 -0
- package/components/_cards/toast.card.html +33 -0
- package/components/_cards/toggles.card.html +68 -0
- package/components/_cards/tooltip.card.html +22 -0
- package/components/_cards/tree.card.html +150 -0
- package/components/_shared/frame-theme.js +16 -0
- package/components/_shared/src-brand.js +1 -0
- package/components/_shared/src-buttons.js +1 -0
- package/components/_shared/src-charts.js +1 -0
- package/components/_shared/src-data.js +1 -0
- package/components/_shared/src-display.js +1 -0
- package/components/_shared/src-feedback.js +1 -0
- package/components/_shared/src-forms.js +1 -0
- package/components/_shared/src-icons.js +1 -0
- package/components/_shared/src-navigation.js +1 -0
- package/components/_shared/src-pipeline.js +1 -0
- package/components/_shared/src-tabs.js +1 -0
- package/components/_shared/svg-download.js +19 -0
- package/components/_shared/view-source.js +185 -0
- package/components/_vue/index.mjs +114 -0
- package/components/brand/BrandVisual.d.ts +17 -0
- package/components/brand/BrandVisual.jsx +29 -0
- package/components/brand/BrandVisual.vue +34 -0
- package/components/brand/_dir.card.html +13 -0
- package/components/brand/brandMotifs.js +210 -0
- package/components/buttons/ActionChip.d.ts +10 -0
- package/components/buttons/ActionChip.jsx +20 -0
- package/components/buttons/ActionChip.vue +18 -0
- package/components/buttons/Button.d.ts +40 -0
- package/components/buttons/Button.jsx +101 -0
- package/components/buttons/Button.vue +87 -0
- package/components/buttons/IconButton.d.ts +26 -0
- package/components/buttons/IconButton.jsx +28 -0
- package/components/buttons/IconButton.vue +21 -0
- package/components/buttons/_dir.card.html +13 -0
- package/components/buttons/buttons.prompt.md +19 -0
- package/components/charts/Bar3D.d.ts +15 -0
- package/components/charts/Bar3D.jsx +93 -0
- package/components/charts/Bar3D.vue +79 -0
- package/components/charts/BarChart.d.ts +23 -0
- package/components/charts/BarChart.jsx +135 -0
- package/components/charts/BarChart.vue +139 -0
- package/components/charts/BarList.d.ts +13 -0
- package/components/charts/BarList.jsx +31 -0
- package/components/charts/BarList.vue +25 -0
- package/components/charts/BoxPlot.d.ts +18 -0
- package/components/charts/BoxPlot.jsx +62 -0
- package/components/charts/BoxPlot.vue +58 -0
- package/components/charts/BulletChart.d.ts +12 -0
- package/components/charts/BulletChart.jsx +41 -0
- package/components/charts/BulletChart.vue +33 -0
- package/components/charts/CandlestickChart.d.ts +15 -0
- package/components/charts/CandlestickChart.jsx +63 -0
- package/components/charts/CandlestickChart.vue +62 -0
- package/components/charts/ChartKit.d.ts +8 -0
- package/components/charts/ChartKit.jsx +76 -0
- package/components/charts/ChartKit.vue +47 -0
- package/components/charts/ChordChart.d.ts +13 -0
- package/components/charts/ChordChart.jsx +55 -0
- package/components/charts/ChordChart.vue +54 -0
- package/components/charts/DonutChart.d.ts +15 -0
- package/components/charts/DonutChart.jsx +73 -0
- package/components/charts/DonutChart.vue +61 -0
- package/components/charts/FunnelChart.d.ts +11 -0
- package/components/charts/FunnelChart.jsx +37 -0
- package/components/charts/FunnelChart.vue +29 -0
- package/components/charts/GanttChart.d.ts +20 -0
- package/components/charts/GanttChart.jsx +80 -0
- package/components/charts/GanttChart.vue +60 -0
- package/components/charts/GaugeChart.d.ts +13 -0
- package/components/charts/GaugeChart.jsx +33 -0
- package/components/charts/GaugeChart.vue +29 -0
- package/components/charts/HeatmapChart.d.ts +13 -0
- package/components/charts/HeatmapChart.jsx +62 -0
- package/components/charts/HeatmapChart.vue +38 -0
- package/components/charts/Histogram.d.ts +16 -0
- package/components/charts/Histogram.jsx +56 -0
- package/components/charts/Histogram.vue +53 -0
- package/components/charts/LineChart.d.ts +17 -0
- package/components/charts/LineChart.jsx +84 -0
- package/components/charts/LineChart.vue +86 -0
- package/components/charts/ParetoChart.d.ts +13 -0
- package/components/charts/ParetoChart.jsx +54 -0
- package/components/charts/ParetoChart.vue +55 -0
- package/components/charts/Pie3D.d.ts +17 -0
- package/components/charts/Pie3D.jsx +145 -0
- package/components/charts/Pie3D.vue +115 -0
- package/components/charts/RadarChart.d.ts +14 -0
- package/components/charts/RadarChart.jsx +79 -0
- package/components/charts/RadarChart.vue +73 -0
- package/components/charts/Ribbon3D.d.ts +13 -0
- package/components/charts/Ribbon3D.jsx +89 -0
- package/components/charts/Ribbon3D.vue +79 -0
- package/components/charts/SankeyChart.d.ts +15 -0
- package/components/charts/SankeyChart.jsx +59 -0
- package/components/charts/SankeyChart.vue +53 -0
- package/components/charts/Scatter3D.d.ts +12 -0
- package/components/charts/Scatter3D.jsx +69 -0
- package/components/charts/Scatter3D.vue +59 -0
- package/components/charts/ScatterChart.d.ts +12 -0
- package/components/charts/ScatterChart.jsx +53 -0
- package/components/charts/ScatterChart.vue +57 -0
- package/components/charts/Sparkline.d.ts +19 -0
- package/components/charts/Sparkline.jsx +63 -0
- package/components/charts/Sparkline.vue +54 -0
- package/components/charts/SunburstChart.d.ts +12 -0
- package/components/charts/SunburstChart.jsx +61 -0
- package/components/charts/SunburstChart.vue +60 -0
- package/components/charts/Surface3D.d.ts +13 -0
- package/components/charts/Surface3D.jsx +70 -0
- package/components/charts/Surface3D.vue +63 -0
- package/components/charts/TreemapChart.d.ts +11 -0
- package/components/charts/TreemapChart.jsx +35 -0
- package/components/charts/TreemapChart.vue +27 -0
- package/components/charts/WaterfallChart.d.ts +11 -0
- package/components/charts/WaterfallChart.jsx +59 -0
- package/components/charts/WaterfallChart.vue +46 -0
- package/components/charts/WorldMap.d.ts +21 -0
- package/components/charts/WorldMap.jsx +107 -0
- package/components/charts/WorldMap.vue +73 -0
- package/components/charts/_dir.card.html +7 -0
- package/components/charts/chart-3d.js +84 -0
- package/components/charts/chart-geometry.js +223 -0
- package/components/charts/charts.prompt.md +24 -0
- package/components/data/CodeBlock.d.ts +9 -0
- package/components/data/CodeBlock.jsx +19 -0
- package/components/data/CodeBlock.vue +22 -0
- package/components/data/DataTable.d.ts +28 -0
- package/components/data/DataTable.jsx +32 -0
- package/components/data/DataTable.vue +25 -0
- package/components/data/DiffValue.d.ts +11 -0
- package/components/data/DiffValue.jsx +15 -0
- package/components/data/DiffValue.vue +13 -0
- package/components/data/FilterBar.d.ts +16 -0
- package/components/data/FilterBar.jsx +22 -0
- package/components/data/FilterBar.vue +23 -0
- package/components/data/KeyValue.d.ts +16 -0
- package/components/data/KeyValue.jsx +25 -0
- package/components/data/KeyValue.vue +26 -0
- package/components/data/ListRow.d.ts +14 -0
- package/components/data/ListRow.jsx +25 -0
- package/components/data/ListRow.vue +25 -0
- package/components/data/Pagination.d.ts +12 -0
- package/components/data/Pagination.jsx +27 -0
- package/components/data/Pagination.vue +24 -0
- package/components/data/SmartTable.d.ts +30 -0
- package/components/data/SmartTable.jsx +73 -0
- package/components/data/SmartTable.vue +74 -0
- package/components/data/Spreadsheet.d.ts +33 -0
- package/components/data/Spreadsheet.jsx +110 -0
- package/components/data/Spreadsheet.vue +95 -0
- package/components/data/_dir.card.html +13 -0
- package/components/data/data.prompt.md +33 -0
- package/components/display/Avatar.d.ts +21 -0
- package/components/display/Avatar.jsx +31 -0
- package/components/display/Avatar.vue +34 -0
- package/components/display/Badge.d.ts +27 -0
- package/components/display/Badge.jsx +40 -0
- package/components/display/Badge.vue +47 -0
- package/components/display/Card.d.ts +27 -0
- package/components/display/Card.jsx +27 -0
- package/components/display/Card.vue +28 -0
- package/components/display/Divider.d.ts +8 -0
- package/components/display/Divider.jsx +14 -0
- package/components/display/Divider.vue +13 -0
- package/components/display/Kbd.d.ts +7 -0
- package/components/display/Kbd.jsx +12 -0
- package/components/display/Kbd.vue +9 -0
- package/components/display/ProgressBar.d.ts +42 -0
- package/components/display/ProgressBar.jsx +70 -0
- package/components/display/ProgressBar.vue +69 -0
- package/components/display/Tag.d.ts +9 -0
- package/components/display/Tag.jsx +15 -0
- package/components/display/Tag.vue +15 -0
- package/components/display/_dir.card.html +13 -0
- package/components/display/display.prompt.md +30 -0
- package/components/feedback/Accordion.d.ts +13 -0
- package/components/feedback/Accordion.jsx +21 -0
- package/components/feedback/Accordion.vue +22 -0
- package/components/feedback/ActivityItem.d.ts +25 -0
- package/components/feedback/ActivityItem.jsx +39 -0
- package/components/feedback/ActivityItem.vue +46 -0
- package/components/feedback/Alert.d.ts +36 -0
- package/components/feedback/Alert.jsx +48 -0
- package/components/feedback/Alert.vue +59 -0
- package/components/feedback/Banner.d.ts +15 -0
- package/components/feedback/Banner.jsx +25 -0
- package/components/feedback/Banner.vue +25 -0
- package/components/feedback/ContextMenu.d.ts +8 -0
- package/components/feedback/ContextMenu.jsx +18 -0
- package/components/feedback/ContextMenu.vue +19 -0
- package/components/feedback/Drawer.d.ts +18 -0
- package/components/feedback/Drawer.jsx +26 -0
- package/components/feedback/Drawer.vue +24 -0
- package/components/feedback/Menu.d.ts +10 -0
- package/components/feedback/Menu.jsx +24 -0
- package/components/feedback/Menu.vue +21 -0
- package/components/feedback/Modal.d.ts +38 -0
- package/components/feedback/Modal.jsx +69 -0
- package/components/feedback/Modal.vue +65 -0
- package/components/feedback/Stepper.d.ts +9 -0
- package/components/feedback/Stepper.jsx +24 -0
- package/components/feedback/Stepper.vue +18 -0
- package/components/feedback/Timeline.d.ts +15 -0
- package/components/feedback/Timeline.jsx +27 -0
- package/components/feedback/Timeline.vue +21 -0
- package/components/feedback/_dir.card.html +13 -0
- package/components/feedback/feedback.prompt.md +35 -0
- package/components/forms/Checkbox.d.ts +13 -0
- package/components/forms/Checkbox.jsx +17 -0
- package/components/forms/Checkbox.vue +18 -0
- package/components/forms/Combobox.d.ts +13 -0
- package/components/forms/Combobox.jsx +34 -0
- package/components/forms/Combobox.vue +35 -0
- package/components/forms/DatePicker.d.ts +12 -0
- package/components/forms/DatePicker.jsx +48 -0
- package/components/forms/DatePicker.vue +46 -0
- package/components/forms/Dropzone.d.ts +20 -0
- package/components/forms/Dropzone.jsx +36 -0
- package/components/forms/Dropzone.vue +28 -0
- package/components/forms/Field.d.ts +13 -0
- package/components/forms/Field.jsx +13 -0
- package/components/forms/Field.vue +12 -0
- package/components/forms/Input.d.ts +18 -0
- package/components/forms/Input.jsx +35 -0
- package/components/forms/Input.vue +38 -0
- package/components/forms/MultiSelect.d.ts +14 -0
- package/components/forms/MultiSelect.jsx +33 -0
- package/components/forms/MultiSelect.vue +36 -0
- package/components/forms/NumberStepper.d.ts +13 -0
- package/components/forms/NumberStepper.jsx +15 -0
- package/components/forms/NumberStepper.vue +17 -0
- package/components/forms/Radio.vue +16 -0
- package/components/forms/RadioGroup.vue +11 -0
- package/components/forms/SearchInput.d.ts +12 -0
- package/components/forms/SearchInput.jsx +17 -0
- package/components/forms/SearchInput.vue +14 -0
- package/components/forms/Select.d.ts +14 -0
- package/components/forms/Select.jsx +17 -0
- package/components/forms/Select.vue +16 -0
- package/components/forms/Slider.d.ts +15 -0
- package/components/forms/Slider.jsx +21 -0
- package/components/forms/Slider.vue +23 -0
- package/components/forms/Switch.d.ts +31 -0
- package/components/forms/Switch.jsx +40 -0
- package/components/forms/Switch.vue +16 -0
- package/components/forms/_dir.card.html +13 -0
- package/components/forms/forms.prompt.md +23 -0
- package/components/icons/Icon.d.ts +19 -0
- package/components/icons/Icon.jsx +93 -0
- package/components/icons/Icon.prompt.md +8 -0
- package/components/icons/Icon.vue +12 -0
- package/components/icons/Unicon.d.ts +12 -0
- package/components/icons/Unicon.jsx +16 -0
- package/components/icons/Unicon.vue +15 -0
- package/components/icons/_dir.card.html +13 -0
- package/components/icons/feather-paths.js +2 -0
- package/components/icons/icon-paths.js +73 -0
- package/components/index.d.ts +87 -0
- package/components/index.js +110 -0
- package/components/navigation/Breadcrumb.d.ts +24 -0
- package/components/navigation/Breadcrumb.jsx +120 -0
- package/components/navigation/Breadcrumb.vue +94 -0
- package/components/navigation/DocTree.d.ts +18 -0
- package/components/navigation/DocTree.jsx +37 -0
- package/components/navigation/DocTree.vue +25 -0
- package/components/navigation/Sidebar.d.ts +29 -0
- package/components/navigation/Sidebar.jsx +75 -0
- package/components/navigation/Sidebar.vue +54 -0
- package/components/navigation/TopBar.d.ts +27 -0
- package/components/navigation/TopBar.jsx +44 -0
- package/components/navigation/TopBar.vue +51 -0
- package/components/navigation/Tree.d.ts +83 -0
- package/components/navigation/Tree.jsx +347 -0
- package/components/navigation/Tree.vue +258 -0
- package/components/navigation/_dir.card.html +13 -0
- package/components/navigation/navigation.prompt.md +22 -0
- package/components/pipeline/ConfigPanel.d.ts +18 -0
- package/components/pipeline/ConfigPanel.jsx +30 -0
- package/components/pipeline/ConfigPanel.vue +32 -0
- package/components/pipeline/DataPreview.d.ts +14 -0
- package/components/pipeline/DataPreview.jsx +24 -0
- package/components/pipeline/DataPreview.vue +21 -0
- package/components/pipeline/NodeCard.d.ts +47 -0
- package/components/pipeline/NodeCard.jsx +97 -0
- package/components/pipeline/NodeCard.vue +75 -0
- package/components/pipeline/PaletteItem.vue +15 -0
- package/components/pipeline/PaletteListItem.vue +15 -0
- package/components/pipeline/PalettePanel.d.ts +20 -0
- package/components/pipeline/PalettePanel.jsx +74 -0
- package/components/pipeline/PalettePanel.vue +45 -0
- package/components/pipeline/PipelineCanvas.d.ts +25 -0
- package/components/pipeline/PipelineCanvas.jsx +94 -0
- package/components/pipeline/PipelineCanvas.vue +73 -0
- package/components/pipeline/ZoomControls.d.ts +9 -0
- package/components/pipeline/ZoomControls.jsx +15 -0
- package/components/pipeline/ZoomControls.vue +15 -0
- package/components/pipeline/_dir.card.html +13 -0
- package/components/pipeline/pipeline.prompt.md +22 -0
- package/components/tabs/SegmentedControl.d.ts +11 -0
- package/components/tabs/SegmentedControl.jsx +21 -0
- package/components/tabs/SegmentedControl.vue +15 -0
- package/components/tabs/Tabs.d.ts +13 -0
- package/components/tabs/Tabs.jsx +52 -0
- package/components/tabs/Tabs.vue +39 -0
- package/components/tabs/ViewToggle.d.ts +8 -0
- package/components/tabs/ViewToggle.jsx +12 -0
- package/components/tabs/ViewToggle.vue +12 -0
- package/components/tabs/_dir.card.html +13 -0
- package/components/tabs/tabs.prompt.md +14 -0
- package/package.json +76 -0
- package/readme.md +150 -0
- package/styles.css +10 -0
- package/tokens/alpha.css +27 -0
- package/tokens/charts.css +55 -0
- package/tokens/colors-extended.css +40 -0
- package/tokens/colors.css +39 -0
- package/tokens/dark.css +96 -0
- package/tokens/fonts.css +4 -0
- package/tokens/interaction.css +48 -0
- package/tokens/layout.css +30 -0
- package/tokens/motion.css +22 -0
- package/tokens/tokens.d.ts +680 -0
- package/tokens/tokens.js +1018 -0
- package/tokens/tokens.json +2928 -0
- package/tokens/typography.css +22 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/* Keybricks brand motifs — generative geometry for BrandVisual.
|
|
2
|
+
Every motif is authored against one canvas (1200×400) and expressed in fractions of W/H,
|
|
3
|
+
so a single component renders at any size. Deterministic: seeded PRNG per motif. */
|
|
4
|
+
|
|
5
|
+
const seeded = (s) => { let a = s >>> 0; return () => { a = (a * 1664525 + 1013904223) >>> 0; return a / 4294967296; }; };
|
|
6
|
+
const lerp = (a, b, t) => a + (b - a) * t;
|
|
7
|
+
const clamp = (v, a, b) => Math.min(b, Math.max(a, v));
|
|
8
|
+
const n = (v) => Math.round(v * 10) / 10;
|
|
9
|
+
|
|
10
|
+
export const motifList = ["orbit", "entropy", "focal", "manifold", "tesseract", "meridian", "ledger"];
|
|
11
|
+
export const motifLabels = {
|
|
12
|
+
orbit: "Orbit — concentric arcs",
|
|
13
|
+
entropy: "Entropy — crystal and dust",
|
|
14
|
+
focal: "Focal — depth-of-field lattice",
|
|
15
|
+
manifold: "Manifold — parametric surface",
|
|
16
|
+
tesseract: "Tesseract — nested cubes",
|
|
17
|
+
meridian: "Meridian — bent meridians",
|
|
18
|
+
ledger: "Ledger — scale and cursor",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const LINE = "#51a2ff", BRIGHT = "#93c5fd", HI = "#dbeafe";
|
|
22
|
+
|
|
23
|
+
/** Returns { viewBox, els } — els are plain SVG element descriptors: { t, ...attrs }. */
|
|
24
|
+
export function buildMotif(motif = "orbit", W = 1200, H = 400) {
|
|
25
|
+
const rnd = seeded(motif.length * 7919 + 104729);
|
|
26
|
+
const els = [];
|
|
27
|
+
const push = (t, a) => els.push({ t, ...a });
|
|
28
|
+
const line = (d, o, w = 1, stroke = LINE, filter) => push("path", { d, fill: "none", stroke, "stroke-opacity": o, "stroke-width": w, ...(filter ? { filter } : null) });
|
|
29
|
+
const dot = (x, y, r, fill, o) => push("circle", { cx: n(x), cy: n(y), r: n(r), fill, "fill-opacity": o });
|
|
30
|
+
const ring = (x, y, r, o, w = 1.5, stroke = HI) => push("circle", { cx: n(x), cy: n(y), r: n(r), fill: "none", stroke, "stroke-opacity": o, "stroke-width": w });
|
|
31
|
+
const marker = (x, y, s = 1) => { dot(x, y, 4.5 * s, HI, 1); ring(x, y, 12 * s, 0.55); ring(x, y, 25 * s, 0.22, 1.2, BRIGHT); };
|
|
32
|
+
const grid = (cols, rows, o = 0.14) => {
|
|
33
|
+
for (let i = 1; i < rows; i++) line(`M 0 ${n(H * i / rows)} L ${W} ${n(H * i / rows)}`, o, 1, BRIGHT);
|
|
34
|
+
for (let i = 1; i < cols; i++) line(`M ${n(W * i / cols)} 0 L ${n(W * i / cols)} ${H}`, o, 1, BRIGHT);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (motif === "orbit") {
|
|
38
|
+
push("rect", { width: W, height: H, fill: "url(#kbv-bg)" });
|
|
39
|
+
push("ellipse", { cx: n(W * 0.72), cy: n(H * 0.2), rx: n(W * 0.36), ry: n(H * 0.78), fill: "url(#kbv-core)" });
|
|
40
|
+
push("ellipse", { cx: n(W * 0.12), cy: n(H * 0.92), rx: n(W * 0.28), ry: n(H * 0.5), fill: "url(#kbv-glow)", opacity: 0.55 });
|
|
41
|
+
grid(4, 3);
|
|
42
|
+
const cx = W * 0.84, cy = H * 1.06;
|
|
43
|
+
const arc = (r, a1, a2) => {
|
|
44
|
+
const p = (a) => [cx + r * Math.cos(a * Math.PI / 180), cy + r * Math.sin(a * Math.PI / 180)];
|
|
45
|
+
const [x1, y1] = p(a1), [x2, y2] = p(a2);
|
|
46
|
+
return `M ${n(x1)} ${n(y1)} A ${n(r)} ${n(r)} 0 0 1 ${n(x2)} ${n(y2)}`;
|
|
47
|
+
};
|
|
48
|
+
const wedge = (r, a1, a2) => `M ${n(cx)} ${n(cy)} L ${arc(r, a1, a2).slice(2)} Z`;
|
|
49
|
+
push("path", { d: wedge(H * 1.24, 194, 248), fill: LINE, opacity: 0.2 });
|
|
50
|
+
push("path", { d: wedge(H * 1.24, 248, 262), fill: BRIGHT, opacity: 0.16 });
|
|
51
|
+
push("path", { d: wedge(H * 0.86, 206, 252), fill: "#155dfc", opacity: 0.42 });
|
|
52
|
+
[[1.62, 0.2], [1.28, 0.34], [0.98, 0.5], [0.7, 0.66], [0.44, 0.4]].forEach(([k, o], i) =>
|
|
53
|
+
line(arc(H * k, 178, 272), o, i === 2 ? 1.5 : 1, i > 1 ? BRIGHT : LINE));
|
|
54
|
+
marker(W * 0.34, H * 0.78);
|
|
55
|
+
dot(W * 0.25, H * 0.28, 3, HI, 0.9);
|
|
56
|
+
dot(W * 0.11, H * 0.6, 2.5, BRIGHT, 0.7);
|
|
57
|
+
dot(W * 0.75, H * 0.28, 2.5, HI, 0.6);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (motif === "entropy") {
|
|
61
|
+
push("rect", { width: W, height: H, fill: "url(#kbv-bg-d)" });
|
|
62
|
+
push("ellipse", { cx: n(W * 0.74), cy: n(H * 0.35), rx: n(W * 0.4), ry: n(H * 0.82), fill: "url(#kbv-core)" });
|
|
63
|
+
push("ellipse", { cx: n(W * 0.18), cy: n(H * 0.95), rx: n(W * 0.3), ry: n(H * 0.5), fill: "url(#kbv-glow)", opacity: 0.45 });
|
|
64
|
+
const NX = 11, NY = 6, NZ = 3, nuc = [8.2, 1.1, 2.05];
|
|
65
|
+
const sx = W * 0.042, sy = H * 0.072, sz = H * 0.132, nx = W * 0.745, ny = H * 0.335;
|
|
66
|
+
const iso = (x, y, z) => [nx + ((x - nuc[0]) - (y - nuc[1])) * sx, ny + ((x - nuc[0]) + (y - nuc[1])) * sy - (z - nuc[2]) * sz];
|
|
67
|
+
const nodes = new Map();
|
|
68
|
+
for (let x = 0; x < NX; x++) for (let y = 0; y < NY; y++) for (let z = 0; z < NZ; z++) {
|
|
69
|
+
const dn = Math.hypot((x - nuc[0]) * 0.92, (y - nuc[1]) * 0.72, (z - nuc[2]) * 1.15);
|
|
70
|
+
const order = clamp(1 - (dn - 1.5) / 5.4 + (rnd() - 0.5) * 0.24, 0, 1);
|
|
71
|
+
if (order < 0.1 && rnd() < 0.66) continue;
|
|
72
|
+
const c = iso(x, y, z), chaos = Math.pow(1 - order, 1.75);
|
|
73
|
+
nodes.set(`${x},${y},${z}`, { px: c[0] + (rnd() - 0.5) * W * 0.17 * chaos, py: c[1] + (rnd() - 0.5) * H * 0.52 * chaos, order, depth: x + y - z * 1.2 });
|
|
74
|
+
}
|
|
75
|
+
for (const [k, p] of nodes) {
|
|
76
|
+
const [x, y, z] = k.split(",").map(Number);
|
|
77
|
+
[[x + 1, y, z], [x, y + 1, z], [x, y, z + 1]].forEach((q) => {
|
|
78
|
+
const b = nodes.get(q.join(","));
|
|
79
|
+
if (!b) return;
|
|
80
|
+
const o = Math.min(p.order, b.order);
|
|
81
|
+
if (o < 0.28 && rnd() < 0.62) return;
|
|
82
|
+
const near = clamp(1 - (p.depth + 5) / 16, 0, 1);
|
|
83
|
+
const d = `M ${n(p.px)} ${n(p.py)} L ${n(b.px)} ${n(b.py)}`;
|
|
84
|
+
if (o > 0.82) line(d, 0.34, 3.4, BRIGHT, "url(#kbv-b2)");
|
|
85
|
+
line(d, lerp(0.05, 0.62, Math.pow(o, 1.2)) * lerp(0.55, 1.15, near), lerp(1, 1.5, o), o > 0.7 ? BRIGHT : LINE);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
for (const [, p] of nodes) {
|
|
89
|
+
const near = clamp(1 - (p.depth + 5) / 16, 0, 1);
|
|
90
|
+
dot(p.px, p.py, lerp(1.1, 3.2, near) * lerp(0.65, 1, p.order), p.order > 0.72 ? HI : BRIGHT, lerp(0.2, 0.95, p.order * 0.65 + near * 0.35));
|
|
91
|
+
}
|
|
92
|
+
for (let i = 0; i < 34; i++) {
|
|
93
|
+
const x = rnd() * W * 0.42, y = rnd() * H, L = lerp(W * 0.004, W * 0.022, rnd());
|
|
94
|
+
const a = Math.atan2(ny - y, nx - x) + (rnd() - 0.5) * 0.7;
|
|
95
|
+
line(`M ${n(x)} ${n(y)} L ${n(x + Math.cos(a) * L)} ${n(y + Math.sin(a) * L)}`, lerp(0.07, 0.3, rnd()), 1, BRIGHT, rnd() < 0.45 ? "url(#kbv-b1)" : undefined);
|
|
96
|
+
}
|
|
97
|
+
for (let i = 0; i < 18; i++) dot(rnd() * W * 0.46, rnd() * H, lerp(0.6, 1.7, rnd()), BRIGHT, lerp(0.12, 0.42, rnd()));
|
|
98
|
+
push("circle", { cx: n(nx), cy: n(ny), r: n(H * 0.06), fill: LINE, "fill-opacity": 0.3, filter: "url(#kbv-b3)" });
|
|
99
|
+
marker(nx, ny);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (motif === "focal") {
|
|
103
|
+
push("rect", { width: W, height: H, fill: "url(#kbv-bg-v)" });
|
|
104
|
+
push("ellipse", { cx: n(W * 0.5), cy: n(H * 0.3), rx: n(W * 0.42), ry: n(H * 0.5), fill: "url(#kbv-glow)" });
|
|
105
|
+
const R = 11, C = 15, pts = [];
|
|
106
|
+
for (let i = 0; i <= R; i++) {
|
|
107
|
+
const t = i / R, y = lerp(H * 0.26, H * 1.02, Math.pow(t, 1.4)), half = lerp(W * 0.17, W * 0.6, Math.pow(t, 1.08));
|
|
108
|
+
const row = [];
|
|
109
|
+
for (let j = 0; j <= C; j++) row.push([W * 0.48 + lerp(-half, half, j / C) - W * 0.04 * (1 - t), y - H * 0.03 * Math.sin(j / C * Math.PI * 1.6)]);
|
|
110
|
+
pts.push({ row, t });
|
|
111
|
+
}
|
|
112
|
+
pts.forEach(({ row, t }) => {
|
|
113
|
+
line(row.map(([x, y], j) => `${j ? "L" : "M"} ${n(x)} ${n(y)}`).join(" "), lerp(0.08, 0.42, t), 1, t > 0.6 ? BRIGHT : LINE);
|
|
114
|
+
});
|
|
115
|
+
for (let j = 0; j <= C; j += 1) {
|
|
116
|
+
line(pts.map(({ row }, i) => `${i ? "L" : "M"} ${n(row[j][0])} ${n(row[j][1])}`).join(" "), j % 3 === 0 ? 0.2 : 0.09, 1, LINE);
|
|
117
|
+
}
|
|
118
|
+
pts.forEach(({ row, t }) => row.forEach(([x, y]) => dot(x, y, lerp(1, 3, t), t > 0.55 ? HI : BRIGHT, lerp(0.22, 0.9, t))));
|
|
119
|
+
marker(W * 0.82, H * 0.34, 0.9);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (motif === "manifold") {
|
|
123
|
+
push("rect", { width: W, height: H, fill: "url(#kbv-bg-v)" });
|
|
124
|
+
push("ellipse", { cx: n(W * 0.5), cy: n(H * 0.32), rx: n(W * 0.42), ry: n(H * 0.46), fill: "url(#kbv-glow)" });
|
|
125
|
+
const V = 9, U = 16, span = W * 0.46;
|
|
126
|
+
const f = (u, v) => {
|
|
127
|
+
const t = v / V, x = lerp(W * 0.5 - span * (0.35 + 0.65 * t), W * 0.5 + span * (0.35 + 0.65 * t), u / U);
|
|
128
|
+
const y = lerp(H * 0.27, H * 1.03, Math.pow(t, 1.5));
|
|
129
|
+
const z = Math.sin(u / U * Math.PI * 2.2 + t * 3) * Math.cos(t * 2.4) * H * 0.077 * (0.4 + t);
|
|
130
|
+
return [x, y - z, z];
|
|
131
|
+
};
|
|
132
|
+
for (let v = 0; v <= V; v++) {
|
|
133
|
+
let d = "", peak = null, pz = -1e9;
|
|
134
|
+
for (let u = 0; u <= U; u++) { const [x, y, z] = f(u, v); d += `${u ? " L " : "M "}${n(x)} ${n(y)}`; if (z > pz) { pz = z; peak = [x, y]; } }
|
|
135
|
+
const t = v / V;
|
|
136
|
+
line(d, lerp(0.15, 0.55, t), 1.2, t > 0.7 ? BRIGHT : LINE);
|
|
137
|
+
if (v === 6) marker(peak[0], peak[1], 0.9);
|
|
138
|
+
}
|
|
139
|
+
for (let u = 0; u <= U; u += 2) {
|
|
140
|
+
let d = "";
|
|
141
|
+
for (let v = 0; v <= V; v++) { const [x, y] = f(u, v); d += `${v ? " L " : "M "}${n(x)} ${n(y)}`; }
|
|
142
|
+
line(d, 0.14, 1.2);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (motif === "tesseract") {
|
|
147
|
+
push("rect", { width: W, height: H, fill: "url(#kbv-bg)" });
|
|
148
|
+
push("ellipse", { cx: n(W * 0.5), cy: n(H * 0.46), rx: n(W * 0.4), ry: n(H * 0.56), fill: "url(#kbv-core)" });
|
|
149
|
+
const cx = W * 0.5, cy = H * 0.5;
|
|
150
|
+
const cube = (r, rt, o, c, w) => {
|
|
151
|
+
const p = [];
|
|
152
|
+
for (let i = 0; i < 4; i++) { const a = rt + i * Math.PI / 2; p.push([cx + r * Math.cos(a), cy + r * Math.sin(a) * 0.82]); }
|
|
153
|
+
const up = p.map(([x, y]) => [x, y - r * 0.62]);
|
|
154
|
+
for (let i = 0; i < 4; i++) {
|
|
155
|
+
const j = (i + 1) % 4;
|
|
156
|
+
line(`M ${n(p[i][0])} ${n(p[i][1])} L ${n(p[j][0])} ${n(p[j][1])}`, o, w, c);
|
|
157
|
+
line(`M ${n(up[i][0])} ${n(up[i][1])} L ${n(up[j][0])} ${n(up[j][1])}`, o, w, c);
|
|
158
|
+
line(`M ${n(p[i][0])} ${n(p[i][1])} L ${n(up[i][0])} ${n(up[i][1])}`, o * 0.8, w, c);
|
|
159
|
+
}
|
|
160
|
+
return { p, up };
|
|
161
|
+
};
|
|
162
|
+
const A = cube(H * 0.44, Math.PI / 4 + 0.2, 0.32, LINE, 1.2);
|
|
163
|
+
const B = cube(H * 0.218, Math.PI / 4 + 0.55, 0.75, BRIGHT, 1.4);
|
|
164
|
+
for (let i = 0; i < 4; i++) {
|
|
165
|
+
line(`M ${n(A.p[i][0])} ${n(A.p[i][1])} L ${n(B.p[i][0])} ${n(B.p[i][1])}`, 0.22, 1.2);
|
|
166
|
+
line(`M ${n(A.up[i][0])} ${n(A.up[i][1])} L ${n(B.up[i][0])} ${n(B.up[i][1])}`, 0.22, 1.2);
|
|
167
|
+
}
|
|
168
|
+
marker(B.up[1][0], B.up[1][1], 0.85);
|
|
169
|
+
dot(W * 0.14, H * 0.79, 2.5, HI, 0.65);
|
|
170
|
+
dot(W * 0.87, H * 0.2, 2.5, HI, 0.65);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (motif === "meridian") {
|
|
174
|
+
push("rect", { width: W, height: H, fill: "url(#kbv-bg-v)" });
|
|
175
|
+
push("ellipse", { cx: n(W * 0.5), cy: n(H * 0.5), rx: n(W * 0.34), ry: n(H * 0.62), fill: "url(#kbv-core)" });
|
|
176
|
+
const cx = W * 0.5, cy = H * 0.5;
|
|
177
|
+
for (let j = 0; j <= 12; j++) {
|
|
178
|
+
const x0 = lerp(W * 0.06, W * 0.94, j / 12), k = (x0 - cx) / (W * 0.5);
|
|
179
|
+
let d = "";
|
|
180
|
+
for (let i = 0; i <= 24; i++) {
|
|
181
|
+
const y = lerp(-H * 0.04, H * 1.04, i / 24);
|
|
182
|
+
const bend = k * W * 0.055 * Math.exp(-Math.pow((y - cy) / (H * 0.42), 2));
|
|
183
|
+
d += `${i ? " L " : "M "}${n(x0 + bend)} ${n(y)}`;
|
|
184
|
+
}
|
|
185
|
+
line(d, lerp(0.42, 0.16, Math.abs(k)), Math.abs(k) < 0.2 ? 1.4 : 1, Math.abs(k) < 0.35 ? BRIGHT : LINE);
|
|
186
|
+
}
|
|
187
|
+
line(`M 0 ${n(cy)} L ${W} ${n(cy)}`, 0.14, 1, BRIGHT);
|
|
188
|
+
marker(cx, cy);
|
|
189
|
+
dot(W * 0.2, H * 0.22, 2.5, BRIGHT, 0.6);
|
|
190
|
+
dot(W * 0.84, H * 0.78, 2, BRIGHT, 0.5);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (motif === "ledger") {
|
|
194
|
+
push("rect", { width: W, height: H, fill: "url(#kbv-bg-d)" });
|
|
195
|
+
push("ellipse", { cx: n(W * 0.5), cy: n(H * 1.04), rx: n(W * 0.46), ry: n(H * 0.66), fill: "url(#kbv-glow)" });
|
|
196
|
+
grid(8, 4, 0.12);
|
|
197
|
+
for (let i = 1; i < 40; i++) {
|
|
198
|
+
const x = W * i / 40, major = i % 5 === 0;
|
|
199
|
+
line(`M ${n(x)} ${n(H * 0.68)} L ${n(x)} ${n(H * 0.68 + (major ? H * 0.09 : H * 0.045))}`, major ? 0.5 : 0.24, 1, major ? BRIGHT : LINE);
|
|
200
|
+
}
|
|
201
|
+
const cur = W * 0.48;
|
|
202
|
+
line(`M ${n(cur)} 0 L ${n(cur)} ${H}`, 0.6, 1.4, HI);
|
|
203
|
+
line(`M ${n(cur)} 0 L ${n(cur)} ${H}`, 0.22, 4, BRIGHT, "url(#kbv-b2)");
|
|
204
|
+
marker(cur, H * 0.42, 0.9);
|
|
205
|
+
dot(W * 0.18, H * 0.22, 2.5, BRIGHT, 0.55);
|
|
206
|
+
dot(W * 0.72, H * 0.72, 2, BRIGHT, 0.45);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { viewBox: `0 0 ${W} ${H}`, els };
|
|
210
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/** Tinted bulk-action chip (comparison-table actions). 30px, r8, 12px/500. */
|
|
3
|
+
export interface ActionChipProps {
|
|
4
|
+
/** success #ecfdf5/#007a55 · warning #fff7ed/#ca3500 · neutral #f5f5f5/#525252 · info · danger */
|
|
5
|
+
tone?: "success" | "warning" | "neutral" | "info" | "danger";
|
|
6
|
+
onClick?: () => void;
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
style?: React.CSSProperties;
|
|
9
|
+
}
|
|
10
|
+
export declare function ActionChip(props: ActionChipProps): JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
const chipTones = {
|
|
4
|
+
success: { background: "var(--success-bg)", color: "var(--success-text)" },
|
|
5
|
+
warning: { background: "var(--warning-bg)", color: "var(--warning-text)" },
|
|
6
|
+
neutral: { background: "var(--bg-hover)", color: "var(--text-body)" },
|
|
7
|
+
info: { background: "var(--info-bg)", color: "var(--action-primary)" },
|
|
8
|
+
danger: { background: "var(--danger-bg)", color: "var(--danger-text)" },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** Tinted bulk-action chip ("Mark as New", "Mark as Overwrite", "Skip Selected"). */
|
|
12
|
+
export function ActionChip({ tone = "neutral", children, onClick, style }) {
|
|
13
|
+
const [hover, setHover] = React.useState(false);
|
|
14
|
+
return (
|
|
15
|
+
<button type="button" onClick={onClick} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
|
|
16
|
+
style={{ height: 30, padding: "0 12px", borderRadius: "var(--radius-md)", border: "none", cursor: "pointer", fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", display: "inline-flex", alignItems: "center", filter: hover ? "brightness(1.06) saturate(1.05)" : "none", transition: "var(--t-hover),filter var(--dur-fast) var(--ease-out)", ...chipTones[tone], ...style }}>
|
|
17
|
+
{children}
|
|
18
|
+
</button>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
const chipTones = {
|
|
4
|
+
success: { background: 'var(--success-bg)', color: 'var(--success-text)' },
|
|
5
|
+
warning: { background: 'var(--warning-bg)', color: 'var(--warning-text)' },
|
|
6
|
+
neutral: { background: 'var(--bg-hover)', color: 'var(--text-body)' },
|
|
7
|
+
info: { background: 'var(--info-bg)', color: 'var(--action-primary)' },
|
|
8
|
+
danger: { background: 'var(--danger-bg)', color: 'var(--danger-text)' },
|
|
9
|
+
}
|
|
10
|
+
// Tinted bulk-action chip ("Mark as New", "Mark as Overwrite", "Skip Selected").
|
|
11
|
+
const props = defineProps({ tone: { type: String, default: 'neutral' }, style: Object })
|
|
12
|
+
defineEmits(['click'])
|
|
13
|
+
const hover = ref(false)
|
|
14
|
+
const s = computed(() => ({ height: 30, padding: '0 12px', borderRadius: 'var(--radius-md)', border: 'none', cursor: 'pointer', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', display: 'inline-flex', alignItems: 'center', filter: hover.value ? 'brightness(1.06) saturate(1.05)' : 'none', transition: 'var(--t-hover),filter var(--dur-fast) var(--ease-out)', ...chipTones[props.tone], ...props.style }))
|
|
15
|
+
</script>
|
|
16
|
+
<template>
|
|
17
|
+
<button type="button" @click="$emit('click')" @mouseenter="hover = true" @mouseleave="hover = false" :style="s"><slot /></button>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Keybricks button — flat, hairline, 13px default.
|
|
4
|
+
*/
|
|
5
|
+
export interface ButtonProps {
|
|
6
|
+
/** primary (blue #155dfc) | dark (#171717, e.g. "AI Assistant") | outline | ghost | primary-soft (blue tint toggle, e.g. "Data Preview") | danger (solid red, destructive) | danger-soft (red tint, e.g. "Remove") | text (no background/border ever — underlines on hover, e.g. "View all", "Cancel") */
|
|
7
|
+
variant?: "primary" | "dark" | "outline" | "ghost" | "primary-soft" | "danger" | "danger-soft" | "text";
|
|
8
|
+
/** sm 30px/12px text (panel actions) | md 32px/13px (default) | lg 35.5px r10 medium (wizard footers) */
|
|
9
|
+
size?: "sm" | "md" | "lg";
|
|
10
|
+
/** Icon glyph name */
|
|
11
|
+
icon?: string;
|
|
12
|
+
iconSize?: number;
|
|
13
|
+
/** Where `icon` renders relative to the label (default "left") */
|
|
14
|
+
iconPosition?: "left" | "right";
|
|
15
|
+
/** Force 500 weight (nav/footer emphasis) */
|
|
16
|
+
medium?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** Shows a spinner in place of the icon and disables interaction, keeping width stable */
|
|
19
|
+
loading?: boolean;
|
|
20
|
+
/** Stretches to fill its container */
|
|
21
|
+
fullWidth?: boolean;
|
|
22
|
+
/** Square, icon-only mode — hides children, requires `label` for accessibility */
|
|
23
|
+
iconOnly?: boolean;
|
|
24
|
+
/** aria-label, required when iconOnly */
|
|
25
|
+
label?: string;
|
|
26
|
+
/** Force the press (mouse-down) styling — for specimens and controlled toggles */
|
|
27
|
+
pressed?: boolean;
|
|
28
|
+
/** Force the keyboard-focus ring — for specimens */
|
|
29
|
+
focused?: boolean;
|
|
30
|
+
onClick?: () => void;
|
|
31
|
+
children?: React.ReactNode;
|
|
32
|
+
style?: React.CSSProperties;
|
|
33
|
+
}
|
|
34
|
+
export declare function Button(props: ButtonProps): JSX.Element;
|
|
35
|
+
/** Attached row of Buttons sharing one outline (e.g. a segmented "Day / Week / Month" action group). */
|
|
36
|
+
export interface ButtonGroupProps {
|
|
37
|
+
children?: React.ReactNode;
|
|
38
|
+
style?: React.CSSProperties;
|
|
39
|
+
}
|
|
40
|
+
export declare function ButtonGroup(props: ButtonGroupProps): JSX.Element;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Icon } from "../icons/Icon.jsx";
|
|
3
|
+
|
|
4
|
+
const btnBase = { display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6, fontFamily: "var(--font-sans)", fontWeight: "var(--weight-regular)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", border: "1px solid transparent", cursor: "pointer", whiteSpace: "nowrap", boxSizing: "border-box", background: "transparent", transition: "var(--t-hover)" };
|
|
5
|
+
|
|
6
|
+
const variants = {
|
|
7
|
+
primary: { background: "var(--action-primary)", color: "var(--on-accent)" },
|
|
8
|
+
dark: { background: "var(--action-dark)", color: "var(--action-dark-fg)" },
|
|
9
|
+
outline: { background: "var(--bg-surface)", color: "var(--text-body)", borderColor: "var(--border-control)" },
|
|
10
|
+
ghost: { color: "var(--text-body)" },
|
|
11
|
+
"primary-soft": { background: "var(--soft-bg)", color: "var(--action-primary)", borderColor: "var(--soft-border)" },
|
|
12
|
+
danger: { background: "var(--danger)", color: "var(--on-accent)" },
|
|
13
|
+
"danger-soft": { background: "var(--danger-bg)", color: "var(--danger-text)" },
|
|
14
|
+
text: { color: "var(--text-secondary)", padding: "0", height: "auto" },
|
|
15
|
+
};
|
|
16
|
+
const hovers = {
|
|
17
|
+
primary: "var(--action-primary-hover)", dark: "var(--action-dark-hover)",
|
|
18
|
+
outline: "var(--bg-hover)", ghost: "var(--bg-hover)", "primary-soft": "var(--soft-hover)",
|
|
19
|
+
danger: "var(--danger-text)", "danger-soft": "var(--danger-bg)",
|
|
20
|
+
};
|
|
21
|
+
/** Press (mouse-down) fills — one step past hover. */
|
|
22
|
+
const presses = {
|
|
23
|
+
primary: "var(--action-primary-active)", dark: "var(--action-dark-active)",
|
|
24
|
+
outline: "var(--bg-press)", ghost: "var(--bg-press)", "primary-soft": "var(--soft-active)",
|
|
25
|
+
danger: "var(--danger-active)", "danger-soft": "var(--danger-soft-active)",
|
|
26
|
+
};
|
|
27
|
+
/** Variants that change text color (+ underline) on hover instead of a background wash. */
|
|
28
|
+
const colorHoverVariants = { text: "var(--text-heading)" };
|
|
29
|
+
const sizes = {
|
|
30
|
+
sm: { height: 30, padding: "6px 12px", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", letterSpacing: 0, borderRadius: "var(--radius-md)" },
|
|
31
|
+
md: { height: 32, padding: "6px 12px", borderRadius: "var(--radius-md)" },
|
|
32
|
+
lg: { height: 35.5, padding: "8px 16px", fontWeight: "var(--weight-medium)", borderRadius: "var(--radius-lg)" },
|
|
33
|
+
};
|
|
34
|
+
const iconOnlyPad = { sm: 0, md: 0, lg: 0 };
|
|
35
|
+
const iconOnlyBox = { sm: 30, md: 32, lg: 36 };
|
|
36
|
+
|
|
37
|
+
function Spinner({ size, color }) {
|
|
38
|
+
return <span style={{ display: "inline-block", width: size, height: size, borderRadius: "var(--radius-full)", border: "2px solid currentColor", borderTopColor: "transparent", opacity: .9, color, animation: "kb-spin .7s linear infinite", flexShrink: 0 }} />;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Keybricks button. variant: primary | dark | outline | ghost | primary-soft | danger | danger-soft | text; size: sm | md | lg. Interaction states: hover (one step darker) · press (two steps, set via mouse-down) · keyboard focus ring · loading · disabled. */
|
|
42
|
+
export function Button({ variant = "primary", size = "md", icon, iconSize, iconPosition = "left", medium, disabled, loading, fullWidth, iconOnly, label, pressed, focused, children, style, onClick, ...rest }) {
|
|
43
|
+
const [hover, setHover] = React.useState(false);
|
|
44
|
+
const [press, setPress] = React.useState(false);
|
|
45
|
+
const [focus, setFocus] = React.useState(false);
|
|
46
|
+
const busy = disabled || loading;
|
|
47
|
+
const isPressed = press || pressed;
|
|
48
|
+
const isFocused = focus || focused;
|
|
49
|
+
const s = {
|
|
50
|
+
...btnBase, ...sizes[size], ...variants[variant],
|
|
51
|
+
...(medium ? { fontWeight: "var(--weight-medium)" } : null),
|
|
52
|
+
...(fullWidth ? { width: "100%" } : null),
|
|
53
|
+
...(iconOnly ? { width: iconOnlyBox[size], padding: iconOnlyPad[size], gap: 0 } : null),
|
|
54
|
+
...(busy ? { opacity: .55, cursor: "default" } : null),
|
|
55
|
+
...style,
|
|
56
|
+
};
|
|
57
|
+
if (!busy) {
|
|
58
|
+
if (colorHoverVariants[variant]) {
|
|
59
|
+
if (hover || isPressed) { s.color = colorHoverVariants[variant]; s.textDecoration = "underline"; }
|
|
60
|
+
if (isPressed) s.opacity = .7;
|
|
61
|
+
} else if (isPressed) {
|
|
62
|
+
s.background = presses[variant];
|
|
63
|
+
} else if (hover) {
|
|
64
|
+
s.background = hovers[variant];
|
|
65
|
+
}
|
|
66
|
+
if (isFocused) s.boxShadow = "var(--ring-focus)";
|
|
67
|
+
}
|
|
68
|
+
const iSize = iconSize || (size === "sm" ? 12 : 14);
|
|
69
|
+
const leading = loading ? <Spinner size={iSize} color={s.color} /> : (icon && iconPosition === "left" ? <Icon name={icon} size={iSize} /> : null);
|
|
70
|
+
const trailing = !loading && icon && iconPosition === "right" ? <Icon name={icon} size={iSize} /> : null;
|
|
71
|
+
return (
|
|
72
|
+
<button type="button" data-press="own" style={s} disabled={busy} aria-label={iconOnly ? label : undefined} onClick={onClick}
|
|
73
|
+
onMouseEnter={() => setHover(true)} onMouseLeave={() => { setHover(false); setPress(false); }}
|
|
74
|
+
onMouseDown={() => setPress(true)} onMouseUp={() => setPress(false)}
|
|
75
|
+
onKeyDown={(e) => { if (e.key === " " || e.key === "Enter") setPress(true); }} onKeyUp={() => setPress(false)}
|
|
76
|
+
onFocus={(e) => { try { setFocus(e.target.matches(":focus-visible")); } catch (err) { setFocus(true); } }} onBlur={() => setFocus(false)} {...rest}>
|
|
77
|
+
{leading}
|
|
78
|
+
{iconOnly ? null : children}
|
|
79
|
+
{trailing}
|
|
80
|
+
</button>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Attached row of Buttons sharing one outline (segmented action group). Children should be <Button variant="outline"> or similar. */
|
|
85
|
+
export function ButtonGroup({ children, style }) {
|
|
86
|
+
const items = React.Children.toArray(children);
|
|
87
|
+
return (
|
|
88
|
+
<div style={{ display: "inline-flex", ...style }}>
|
|
89
|
+
{items.map((child, i) => React.cloneElement(child, {
|
|
90
|
+
key: i,
|
|
91
|
+
style: {
|
|
92
|
+
...(child.props.style || {}),
|
|
93
|
+
borderRadius: 0,
|
|
94
|
+
...(i === 0 ? { borderTopLeftRadius: "var(--radius-md)", borderBottomLeftRadius: "var(--radius-md)" } : null),
|
|
95
|
+
...(i === items.length - 1 ? { borderTopRightRadius: "var(--radius-md)", borderBottomRightRadius: "var(--radius-md)" } : null),
|
|
96
|
+
marginLeft: i === 0 ? 0 : -1,
|
|
97
|
+
},
|
|
98
|
+
}))}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, ref } from 'vue'
|
|
3
|
+
import Icon from '../icons/Icon.vue'
|
|
4
|
+
|
|
5
|
+
const btnBase = { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6, fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-regular)', fontSize: 'var(--text-base)', lineHeight: 'var(--leading-base)', letterSpacing: 'var(--tracking-base)', border: '1px solid transparent', cursor: 'pointer', whiteSpace: 'nowrap', boxSizing: 'border-box', background: 'transparent', transition: 'var(--t-hover)' }
|
|
6
|
+
const variants = {
|
|
7
|
+
primary: { background: 'var(--action-primary)', color: 'var(--on-accent)' },
|
|
8
|
+
dark: { background: 'var(--action-dark)', color: 'var(--action-dark-fg)' },
|
|
9
|
+
outline: { background: 'var(--bg-surface)', color: 'var(--text-body)', borderColor: 'var(--border-control)' },
|
|
10
|
+
ghost: { color: 'var(--text-body)' },
|
|
11
|
+
'primary-soft': { background: 'var(--soft-bg)', color: 'var(--action-primary)', borderColor: 'var(--soft-border)' },
|
|
12
|
+
danger: { background: 'var(--danger)', color: 'var(--on-accent)' },
|
|
13
|
+
'danger-soft': { background: 'var(--danger-bg)', color: 'var(--danger-text)' },
|
|
14
|
+
text: { color: 'var(--text-secondary)', padding: '0', height: 'auto' },
|
|
15
|
+
}
|
|
16
|
+
const hovers = { primary: 'var(--action-primary-hover)', dark: 'var(--action-dark-hover)', outline: 'var(--bg-hover)', ghost: 'var(--bg-hover)', 'primary-soft': 'var(--soft-hover)', danger: 'var(--danger-text)', 'danger-soft': 'var(--danger-bg)' }
|
|
17
|
+
const presses = { primary: 'var(--action-primary-active)', dark: 'var(--action-dark-active)', outline: 'var(--bg-press)', ghost: 'var(--bg-press)', 'primary-soft': 'var(--soft-active)', danger: 'var(--danger-active)', 'danger-soft': 'var(--danger-soft-active)' }
|
|
18
|
+
const colorHoverVariants = { text: 'var(--text-heading)' }
|
|
19
|
+
const sizes = {
|
|
20
|
+
sm: { height: 30, padding: '6px 12px', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', letterSpacing: 0, borderRadius: 'var(--radius-md)' },
|
|
21
|
+
md: { height: 32, padding: '6px 12px', borderRadius: 'var(--radius-md)' },
|
|
22
|
+
lg: { height: 35.5, padding: '8px 16px', fontWeight: 'var(--weight-medium)', borderRadius: 'var(--radius-lg)' },
|
|
23
|
+
}
|
|
24
|
+
const iconOnlyPad = { sm: 0, md: 0, lg: 0 }
|
|
25
|
+
const iconOnlyBox = { sm: 30, md: 32, lg: 36 }
|
|
26
|
+
|
|
27
|
+
// Button — variant: primary | dark | outline | ghost | primary-soft | danger | danger-soft | text; size: sm | md | lg.
|
|
28
|
+
const props = defineProps({
|
|
29
|
+
variant: { type: String, default: 'primary' }, size: { type: String, default: 'md' },
|
|
30
|
+
icon: String, iconSize: Number, iconPosition: { type: String, default: 'left' },
|
|
31
|
+
medium: Boolean, disabled: Boolean, loading: Boolean, fullWidth: Boolean, iconOnly: Boolean,
|
|
32
|
+
label: String, pressed: Boolean, focused: Boolean, style: Object,
|
|
33
|
+
})
|
|
34
|
+
defineEmits(['click'])
|
|
35
|
+
const hover = ref(false), press = ref(false), focus = ref(false)
|
|
36
|
+
const busy = computed(() => props.disabled || props.loading)
|
|
37
|
+
const isPressed = computed(() => press.value || props.pressed)
|
|
38
|
+
const isFocused = computed(() => focus.value || props.focused)
|
|
39
|
+
const s = computed(() => {
|
|
40
|
+
const base = { ...btnBase, ...sizes[props.size], ...variants[props.variant],
|
|
41
|
+
...(props.medium ? { fontWeight: 'var(--weight-medium)' } : null),
|
|
42
|
+
...(props.fullWidth ? { width: '100%' } : null),
|
|
43
|
+
...(props.iconOnly ? { width: iconOnlyBox[props.size], padding: iconOnlyPad[props.size], gap: 0 } : null),
|
|
44
|
+
...(busy.value ? { opacity: .55, cursor: 'default' } : null), ...props.style }
|
|
45
|
+
if (!busy.value) {
|
|
46
|
+
if (colorHoverVariants[props.variant]) {
|
|
47
|
+
if (hover.value || isPressed.value) { base.color = colorHoverVariants[props.variant]; base.textDecoration = 'underline' }
|
|
48
|
+
if (isPressed.value) base.opacity = .7
|
|
49
|
+
} else if (isPressed.value) base.background = presses[props.variant]
|
|
50
|
+
else if (hover.value) base.background = hovers[props.variant]
|
|
51
|
+
if (isFocused.value) base.boxShadow = 'var(--ring-focus)'
|
|
52
|
+
}
|
|
53
|
+
return base
|
|
54
|
+
})
|
|
55
|
+
const iSize = computed(() => props.iconSize || (props.size === 'sm' ? 12 : 14))
|
|
56
|
+
</script>
|
|
57
|
+
<template>
|
|
58
|
+
<button type="button" data-press="own" :style="s" :disabled="busy" :aria-label="iconOnly ? label : undefined"
|
|
59
|
+
@click="$emit('click')" @mouseenter="hover = true" @mouseleave="hover = false; press = false"
|
|
60
|
+
@mousedown="press = true" @mouseup="press = false" @keydown.enter="press = true" @keydown.space="press = true" @keyup="press = false"
|
|
61
|
+
@focus="focus = $event.target.matches(':focus-visible')" @blur="focus = false">
|
|
62
|
+
<span v-if="loading" :style="{ display: 'inline-block', width: iSize, height: iSize, borderRadius: 'var(--radius-full)', border: '2px solid currentColor', borderTopColor: 'transparent', opacity: .9, animation: 'kb-spin .7s linear infinite', flexShrink: 0 }" />
|
|
63
|
+
<Icon v-else-if="icon && iconPosition === 'left'" :name="icon" :size="iSize" />
|
|
64
|
+
<slot v-if="!iconOnly" />
|
|
65
|
+
<Icon v-if="!loading && icon && iconPosition === 'right'" :name="icon" :size="iSize" />
|
|
66
|
+
</button>
|
|
67
|
+
</template>
|
|
68
|
+
|
|
69
|
+
<!-- ButtonGroup — attached row of Buttons sharing one outline (segmented action group). -->
|
|
70
|
+
<!--
|
|
71
|
+
<script setup name="ButtonGroup">
|
|
72
|
+
import { useSlots, computed } from 'vue'
|
|
73
|
+
const props = defineProps({ style: Object })
|
|
74
|
+
const slots = useSlots()
|
|
75
|
+
const items = computed(() => slots.default ? slots.default() : [])
|
|
76
|
+
</script>
|
|
77
|
+
<template>
|
|
78
|
+
<div :style="{ display: 'inline-flex', ...style }">
|
|
79
|
+
<template v-for="(child, i) in items" :key="i">
|
|
80
|
+
<component :is="child" :style="{ borderRadius: 0,
|
|
81
|
+
...(i === 0 ? { borderTopLeftRadius: 'var(--radius-md)', borderBottomLeftRadius: 'var(--radius-md)' } : null),
|
|
82
|
+
...(i === items.length - 1 ? { borderTopRightRadius: 'var(--radius-md)', borderBottomRightRadius: 'var(--radius-md)' } : null),
|
|
83
|
+
marginLeft: i === 0 ? 0 : -1 }" />
|
|
84
|
+
</template>
|
|
85
|
+
</div>
|
|
86
|
+
</template>
|
|
87
|
+
-->
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/** Square ghost icon button used in toolbars, headers, and panel chrome. */
|
|
3
|
+
export interface IconButtonProps {
|
|
4
|
+
/** Icon glyph name */
|
|
5
|
+
icon: string;
|
|
6
|
+
/** xs 22/r8 · sm 26/r4 (view toggle) · md 28/r8 (toolbar) · lg 34/r8 (top bar) */
|
|
7
|
+
size?: "xs" | "sm" | "md" | "lg";
|
|
8
|
+
/** Pressed/selected state (#f5f5f5 wash) */
|
|
9
|
+
active?: boolean;
|
|
10
|
+
/** Muted #a1a1a1 glyph */
|
|
11
|
+
muted?: boolean;
|
|
12
|
+
/** Red glyph + red-tint hover, for destructive icon actions */
|
|
13
|
+
danger?: boolean;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Shows a spinner in place of the icon and disables interaction */
|
|
16
|
+
loading?: boolean;
|
|
17
|
+
/** aria-label */
|
|
18
|
+
label?: string;
|
|
19
|
+
/** Force the press (mouse-down) styling — for specimens */
|
|
20
|
+
pressed?: boolean;
|
|
21
|
+
/** Force the keyboard-focus ring — for specimens */
|
|
22
|
+
focused?: boolean;
|
|
23
|
+
onClick?: () => void;
|
|
24
|
+
style?: React.CSSProperties;
|
|
25
|
+
}
|
|
26
|
+
export declare function IconButton(props: IconButtonProps): JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Icon } from "../icons/Icon.jsx";
|
|
3
|
+
|
|
4
|
+
const ibSizes = { xs: { box: 22, icon: 14, radius: "var(--radius-md)" }, sm: { box: 26, icon: 14, radius: "var(--radius-sm)" }, md: { box: 28, icon: 16, radius: "var(--radius-md)" }, lg: { box: 34, icon: 18, radius: "var(--radius-md)" } };
|
|
5
|
+
|
|
6
|
+
/** Square ghost icon button (toolbar/back/bell/view toggles). size: xs 22 | sm 26 | md 28 | lg 34. danger tints red; loading shows a spinner and disables interaction. States: hover · press · keyboard focus ring · active (persistent toggle-on) · disabled. */
|
|
7
|
+
export function IconButton({ icon, size = "md", active, muted, danger, disabled, loading, label, pressed, focused, style, onClick, ...rest }) {
|
|
8
|
+
const [hover, setHover] = React.useState(false);
|
|
9
|
+
const [press, setPress] = React.useState(false);
|
|
10
|
+
const [focus, setFocus] = React.useState(false);
|
|
11
|
+
const t = ibSizes[size];
|
|
12
|
+
const busy = disabled || loading;
|
|
13
|
+
const isPressed = (press || pressed) && !busy;
|
|
14
|
+
const color = danger ? "var(--danger)" : muted ? "var(--text-muted)" : "var(--text-body)";
|
|
15
|
+
const bg = isPressed ? (danger ? "var(--danger-soft-active)" : "var(--bg-press)")
|
|
16
|
+
: active ? "var(--bg-active)"
|
|
17
|
+
: hover && !busy ? (danger ? "var(--danger-bg)" : "var(--bg-hover)") : "transparent";
|
|
18
|
+
return (
|
|
19
|
+
<button type="button" data-press="own" aria-label={label} disabled={busy} onClick={onClick}
|
|
20
|
+
onMouseEnter={() => setHover(true)} onMouseLeave={() => { setHover(false); setPress(false); }}
|
|
21
|
+
onMouseDown={() => setPress(true)} onMouseUp={() => setPress(false)}
|
|
22
|
+
onKeyDown={(e) => { if (e.key === " " || e.key === "Enter") setPress(true); }} onKeyUp={() => setPress(false)}
|
|
23
|
+
onFocus={(e) => { try { setFocus(e.target.matches(":focus-visible")); } catch (err) { setFocus(true); } }} onBlur={() => setFocus(false)}
|
|
24
|
+
style={{ width: t.box, height: t.box, borderRadius: t.radius, border: "none", cursor: busy ? "default" : "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center", background: bg, color, opacity: busy ? .5 : 1, boxShadow: (focus || focused) && !busy ? "var(--ring-focus)" : "none", transition: "var(--t-hover)", padding: 0, flexShrink: 0, ...style }} {...rest}>
|
|
25
|
+
{loading ? <span style={{ display: "inline-block", width: t.icon, height: t.icon, borderRadius: "var(--radius-full)", border: "2px solid currentColor", borderTopColor: "transparent", animation: "kb-spin .7s linear infinite" }} /> : <Icon name={icon} size={t.icon} />}
|
|
26
|
+
</button>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
import Icon from '../icons/Icon.vue'
|
|
4
|
+
const ibSizes = { xs: { box: 22, icon: 14, radius: 'var(--radius-md)' }, sm: { box: 26, icon: 14, radius: 'var(--radius-sm)' }, md: { box: 28, icon: 16, radius: 'var(--radius-md)' }, lg: { box: 34, icon: 18, radius: 'var(--radius-md)' } }
|
|
5
|
+
// Square ghost icon button (toolbar/back/bell/view toggles). size: xs 22 | sm 26 | md 28 | lg 34.
|
|
6
|
+
const props = defineProps({ icon: String, size: { type: String, default: 'md' }, active: Boolean, muted: Boolean, danger: Boolean, disabled: Boolean, loading: Boolean, label: String, pressed: Boolean, focused: Boolean, style: Object })
|
|
7
|
+
defineEmits(['click'])
|
|
8
|
+
const hover = ref(false), press = ref(false), focus = ref(false)
|
|
9
|
+
const t = computed(() => ibSizes[props.size])
|
|
10
|
+
const busy = computed(() => props.disabled || props.loading)
|
|
11
|
+
const isPressed = computed(() => (press.value || props.pressed) && !busy.value)
|
|
12
|
+
const color = computed(() => props.danger ? 'var(--danger)' : props.muted ? 'var(--text-muted)' : 'var(--text-body)')
|
|
13
|
+
const bg = computed(() => isPressed.value ? (props.danger ? 'var(--danger-soft-active)' : 'var(--bg-press)') : props.active ? 'var(--bg-active)' : (hover.value && !busy.value ? (props.danger ? 'var(--danger-bg)' : 'var(--bg-hover)') : 'transparent'))
|
|
14
|
+
const s = computed(() => ({ width: t.value.box, height: t.value.box, borderRadius: t.value.radius, border: 'none', cursor: busy.value ? 'default' : 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', background: bg.value, color: color.value, opacity: busy.value ? .5 : 1, boxShadow: (focus.value || props.focused) && !busy.value ? 'var(--ring-focus)' : 'none', transition: 'var(--t-hover)', padding: 0, flexShrink: 0, ...props.style }))
|
|
15
|
+
</script>
|
|
16
|
+
<template>
|
|
17
|
+
<button type="button" data-press="own" :aria-label="label" :disabled="busy" @click="$emit('click')" @mouseenter="hover = true" @mouseleave="hover = false; press = false" @mousedown="press = true" @mouseup="press = false" @keydown.enter="press = true" @keydown.space="press = true" @keyup="press = false" @focus="focus = $event.target.matches(':focus-visible')" @blur="focus = false" :style="s">
|
|
18
|
+
<span v-if="loading" :style="{ display: 'inline-block', width: t.icon, height: t.icon, borderRadius: 'var(--radius-full)', border: '2px solid currentColor', borderTopColor: 'transparent', animation: 'kb-spin .7s linear infinite' }" />
|
|
19
|
+
<Icon v-else :name="icon" :size="t.icon" />
|
|
20
|
+
</button>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!-- @dsCard group="Directory Index" viewport="700x70" name="Buttons dir" subtitle="Full card: Components → Button" -->
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html><head><meta charset="utf-8"><link rel="stylesheet" href="../../styles.css">
|
|
4
|
+
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
|
5
|
+
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
|
6
|
+
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
|
7
|
+
<script src="../../_ds_bundle.js"></script>
|
|
8
|
+
<style>body{margin:0;background:var(--bg-surface);padding:16px;font-family:var(--font-sans)}</style></head>
|
|
9
|
+
<body><div id="root"></div>
|
|
10
|
+
<script type="text/babel">
|
|
11
|
+
const { Button } = window.KeybricksDesignSystem_71231a;
|
|
12
|
+
ReactDOM.createRoot(document.getElementById("root")).render(<Button icon="send">Submit</Button>);
|
|
13
|
+
</script></body></html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Buttons: `Button` (primary/dark/outline/ghost/primary-soft; sm/md/lg), `IconButton` (square ghost), `ActionChip` (tinted bulk actions).
|
|
2
|
+
|
|
3
|
+
```jsx
|
|
4
|
+
<Button icon="send">Submit</Button>
|
|
5
|
+
<Button variant="dark" icon="sparkles">AI Assistant</Button>
|
|
6
|
+
<Button variant="ghost" icon="play">Run Test</Button>
|
|
7
|
+
<Button variant="text">Cancel</Button>
|
|
8
|
+
<Button variant="primary-soft" icon="eye" size="sm">Data Preview</Button>
|
|
9
|
+
<Button variant="danger" icon="trash-2">Delete Node</Button>
|
|
10
|
+
<Button loading>Submit</Button>
|
|
11
|
+
<Button icon="bell" iconOnly label="Notifications" variant="outline"/>
|
|
12
|
+
<Button fullWidth icon="upload">Upload Configuration File</Button>
|
|
13
|
+
<ButtonGroup><Button variant="outline" size="sm">Day</Button><Button variant="outline" size="sm">Week</Button></ButtonGroup>
|
|
14
|
+
<IconButton icon="bell" size="lg"/>
|
|
15
|
+
<IconButton icon="trash-2" danger label="Delete"/>
|
|
16
|
+
<ActionChip tone="success">Mark as New</ActionChip>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Primary = the single blue CTA per view. Dark = brand-level actions (AI Assistant, Show). Ghost text buttons sit beside the CTA ("Back", "Run Test"). `text` = no background/border ever, underlines on hover — for tertiary actions like "Cancel"/"View all", never a page's main action. Danger/danger-soft = destructive actions. lg (r10, 500 weight) only in wizard footers. `loading` swaps the icon for a spinner and disables the button without changing its size; `iconOnly` needs a `label` for accessibility; `ButtonGroup` fuses adjacent buttons into one segmented control or a split button (main action + attached icon-only).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface Bar3DSeries { name: string; values: number[]; color?: string; }
|
|
3
|
+
/** 3D cuboid bars — categories on x, series as depth rows or stacked; drag to orbit. */
|
|
4
|
+
export interface Bar3DProps {
|
|
5
|
+
series?: Bar3DSeries[];
|
|
6
|
+
categories?: string[];
|
|
7
|
+
mode?: "grouped" | "stacked";
|
|
8
|
+
width?: number; height?: number;
|
|
9
|
+
yaw?: number; pitch?: number;
|
|
10
|
+
/** Fraction of each cell left empty, 0–1 */ gap?: number;
|
|
11
|
+
interactive?: boolean;
|
|
12
|
+
valueFormat?: (v: number) => string;
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
export declare function Bar3D(props: Bar3DProps): JSX.Element;
|