@keyfunc/keybricks-ds 2.1.1 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEPLOY.md +13 -4
- package/DESIGN.md +104 -19
- package/INTEGRATION.md +75 -20
- package/SKILL.md +11 -11
- package/_adherence.oxlintrc.json +28 -11
- package/_scripts/build-index.mjs +15 -4
- package/assets/icons/icon-index.js +1 -1
- package/assets/icons/icon-index.json +1 -1
- package/components/_cards/accordion.card.html +1 -1
- package/components/_cards/action-chip.card.html +11 -14
- package/components/_cards/activity.card.html +2 -2
- package/components/_cards/alert.card.html +2 -2
- package/components/_cards/app-chrome.card.html +1 -1
- package/components/_cards/banner.card.html +1 -1
- package/components/_cards/breadcrumb.card.html +4 -4
- package/components/_cards/button-1-base.card.html +23 -26
- package/components/_cards/button-2-states.card.html +14 -12
- package/components/_cards/button-3-sizes-icons.card.html +20 -19
- package/components/_cards/button-4-group.card.html +14 -31
- package/components/_cards/canvas-zoom-controls.card.html +1 -1
- package/components/_cards/cards-indicators.card.html +1 -1
- package/components/_cards/charts-1-sparkline.card.html +4 -4
- package/components/_cards/charts-10-gauge.card.html +2 -2
- package/components/_cards/charts-11-funnel.card.html +2 -2
- package/components/_cards/charts-12-treemap.card.html +2 -2
- package/components/_cards/charts-14-radar.card.html +3 -3
- package/components/_cards/charts-15-distribution.card.html +2 -2
- package/components/_cards/charts-16-pareto.card.html +2 -2
- package/components/_cards/charts-17-candlestick.card.html +2 -2
- package/components/_cards/charts-18-sankey.card.html +2 -2
- package/components/_cards/charts-19-chord.card.html +2 -2
- package/components/_cards/charts-2-line.card.html +3 -3
- package/components/_cards/charts-20-sunburst.card.html +2 -2
- package/components/_cards/charts-21-worldmap.card.html +3 -3
- package/components/_cards/charts-22-gantt.card.html +2 -2
- package/components/_cards/charts-23-bar3d.card.html +2 -2
- package/components/_cards/charts-24-pie3d.card.html +2 -2
- package/components/_cards/charts-25-surface3d.card.html +2 -2
- package/components/_cards/charts-26-scatter3d.card.html +2 -2
- package/components/_cards/charts-27-ribbon3d.card.html +2 -2
- package/components/_cards/charts-3-bar.card.html +6 -6
- package/components/_cards/charts-4-barlist.card.html +2 -2
- package/components/_cards/charts-5-donut.card.html +2 -2
- package/components/_cards/charts-6-scatter.card.html +2 -2
- package/components/_cards/charts-7-heatmap.card.html +2 -2
- package/components/_cards/charts-8-waterfall.card.html +2 -2
- package/components/_cards/charts-9-bullet.card.html +3 -3
- package/components/_cards/charts-99-fullpage.card.html +1 -1
- package/components/_cards/checkbox.card.html +6 -3
- package/components/_cards/codeblock.card.html +2 -2
- package/components/_cards/combobox.card.html +6 -4
- package/components/_cards/context-menu.card.html +6 -4
- package/components/_cards/date-picker.card.html +8 -5
- package/components/_cards/diffvalue.card.html +2 -2
- package/components/_cards/divider.card.html +2 -2
- package/components/_cards/drawer.card.html +3 -3
- package/components/_cards/dropdown-menu.card.html +2 -2
- package/components/_cards/dropzone.card.html +5 -3
- package/components/_cards/empty-state.card.html +2 -2
- package/components/_cards/filter-bar.card.html +1 -1
- package/components/_cards/icon-button.card.html +14 -14
- package/components/_cards/icons-1-core.card.html +4 -4
- package/components/_cards/icons-2-feather.card.html +4 -4
- package/components/_cards/icons-3-unicons.card.html +3 -3
- package/components/_cards/input.card.html +5 -5
- package/components/_cards/kbd.card.html +1 -1
- package/components/_cards/keyvalue.card.html +1 -1
- package/components/_cards/listrow.card.html +1 -1
- package/components/_cards/loading.card.html +2 -2
- package/components/_cards/log-viewer.card.html +2 -2
- package/components/_cards/modal.card.html +1 -1
- package/components/_cards/multi-select.card.html +6 -4
- package/components/_cards/number-stepper.card.html +9 -6
- package/components/_cards/pagination.card.html +6 -4
- package/components/_cards/people.card.html +2 -2
- package/components/_cards/pipeline-node.card.html +8 -8
- package/components/_cards/pipeline-palette.card.html +2 -2
- package/components/_cards/radio.card.html +12 -7
- package/components/_cards/search.card.html +6 -6
- package/components/_cards/segmented-control.card.html +11 -10
- package/components/_cards/select.card.html +7 -3
- package/components/_cards/slider.card.html +2 -2
- package/components/_cards/smart-table.card.html +3 -3
- package/components/_cards/spreadsheet.card.html +2 -2
- package/components/_cards/stepper.card.html +2 -2
- package/components/_cards/table.card.html +79 -17
- package/components/_cards/tabs.card.html +6 -4
- package/components/_cards/tag.card.html +6 -6
- package/components/_cards/textarea.card.html +1 -1
- package/components/_cards/timeline.card.html +3 -3
- package/components/_cards/toast.card.html +7 -5
- package/components/_cards/toggles.card.html +14 -9
- package/components/_cards/tooltip.card.html +3 -3
- package/components/_cards/tree.card.html +2 -2
- package/components/_shared/card-kit.css +25 -0
- package/components/_shared/card-kit.js +50 -0
- package/components/_shared/src-buttons.js +1 -1
- package/components/_shared/src-charts.js +1 -1
- package/components/_shared/src-data.js +1 -1
- package/components/_shared/src-display.js +1 -1
- package/components/_shared/src-feedback.js +1 -1
- package/components/_shared/src-forms.js +1 -1
- package/components/_shared/src-icons.js +1 -1
- package/components/_shared/src-navigation.js +1 -1
- package/components/_shared/src-tabs.js +1 -1
- package/components/_shared/view-source.js +3 -3
- package/components/_vue/index.mjs +7 -0
- package/components/buttons/ActionChip.d.ts +1 -1
- package/components/buttons/Button.d.ts +1 -1
- package/components/buttons/Button.jsx +1 -1
- package/components/buttons/Button.vue +1 -1
- package/components/buttons/IconButton.d.ts +1 -1
- package/components/buttons/IconButton.jsx +5 -3
- package/components/buttons/IconButton.vue +3 -2
- package/components/charts/Bar3D.jsx +1 -1
- package/components/charts/Bar3D.vue +2 -2
- package/components/charts/BarChart.jsx +3 -3
- package/components/charts/BarChart.vue +6 -6
- package/components/charts/BarList.jsx +3 -3
- package/components/charts/BarList.vue +3 -3
- package/components/charts/BoxPlot.vue +2 -2
- package/components/charts/BulletChart.jsx +5 -5
- package/components/charts/BulletChart.vue +5 -5
- package/components/charts/CandlestickChart.vue +3 -3
- package/components/charts/ChartKit.jsx +4 -3
- package/components/charts/ChartTooltip.vue +1 -1
- package/components/charts/ChordChart.jsx +1 -1
- package/components/charts/ChordChart.vue +2 -2
- package/components/charts/DonutChart.jsx +1 -1
- package/components/charts/DonutChart.vue +2 -2
- package/components/charts/FunnelChart.jsx +3 -3
- package/components/charts/FunnelChart.vue +4 -4
- package/components/charts/GanttChart.vue +1 -1
- package/components/charts/GaugeChart.jsx +2 -2
- package/components/charts/GaugeChart.vue +2 -2
- package/components/charts/HeatmapChart.jsx +1 -1
- package/components/charts/HeatmapChart.vue +1 -1
- package/components/charts/Histogram.jsx +2 -2
- package/components/charts/Histogram.vue +4 -4
- package/components/charts/LineChart.vue +2 -2
- package/components/charts/ParetoChart.jsx +1 -1
- package/components/charts/ParetoChart.vue +3 -3
- package/components/charts/Pie3D.jsx +1 -1
- package/components/charts/Pie3D.vue +2 -2
- package/components/charts/PlotGrid.vue +1 -1
- package/components/charts/RadarChart.vue +1 -1
- package/components/charts/Ribbon3D.jsx +1 -1
- package/components/charts/Ribbon3D.vue +2 -2
- package/components/charts/SankeyChart.jsx +1 -1
- package/components/charts/SankeyChart.vue +2 -2
- package/components/charts/Scatter3D.vue +1 -1
- package/components/charts/ScatterChart.jsx +1 -1
- package/components/charts/ScatterChart.vue +3 -3
- package/components/charts/Sparkline.jsx +1 -1
- package/components/charts/Sparkline.vue +1 -1
- package/components/charts/SunburstChart.vue +1 -1
- package/components/charts/Surface3D.jsx +1 -1
- package/components/charts/Surface3D.vue +2 -2
- package/components/charts/TreemapChart.jsx +1 -1
- package/components/charts/TreemapChart.vue +2 -2
- package/components/charts/WaterfallChart.jsx +1 -1
- package/components/charts/WaterfallChart.vue +5 -4
- package/components/charts/WorldMap.jsx +4 -4
- package/components/charts/WorldMap.vue +5 -5
- package/components/data/DiffValue.d.ts +1 -0
- package/components/data/DiffValue.jsx +1 -1
- package/components/data/DiffValue.vue +1 -1
- package/components/data/KeyValue.d.ts +1 -1
- package/components/data/KeyValue.jsx +2 -2
- package/components/data/KeyValue.vue +2 -2
- package/components/data/Pagination.jsx +2 -2
- package/components/data/Pagination.vue +1 -1
- package/components/data/SmartTable.jsx +5 -5
- package/components/data/SmartTable.vue +6 -6
- package/components/data/Spreadsheet.jsx +5 -5
- package/components/data/Spreadsheet.vue +6 -6
- package/components/data/Table.d.ts +58 -4
- package/components/data/Table.jsx +153 -15
- package/components/data/Table.vue +105 -15
- package/components/data/data.prompt.md +1 -1
- package/components/display/Avatar.jsx +2 -2
- package/components/display/Avatar.vue +1 -1
- package/components/display/AvatarGroup.vue +1 -1
- package/components/display/Badge.d.ts +4 -1
- package/components/display/Badge.jsx +10 -5
- package/components/display/IconTile.vue +9 -4
- package/components/display/Kbd.d.ts +1 -1
- package/components/display/Kbd.jsx +1 -1
- package/components/display/Kbd.vue +1 -1
- package/components/feedback/ActivityItem.jsx +1 -1
- package/components/feedback/Modal.jsx +1 -1
- package/components/feedback/Modal.vue +1 -1
- package/components/feedback/Timeline.vue +2 -2
- package/components/forms/Combobox.d.ts +1 -1
- package/components/forms/DatePicker.d.ts +1 -1
- package/components/forms/Input.d.ts +1 -1
- package/components/forms/Input.jsx +2 -2
- package/components/forms/Input.vue +1 -1
- package/components/forms/MultiSelect.d.ts +1 -1
- package/components/forms/NumberStepper.d.ts +1 -1
- package/components/forms/SearchInput.d.ts +1 -1
- package/components/forms/Select.d.ts +1 -1
- package/components/forms/Select.jsx +1 -1
- package/components/forms/Select.vue +1 -1
- package/components/forms/Slider.jsx +1 -1
- package/components/forms/Slider.vue +1 -1
- package/components/forms/Switch.jsx +3 -3
- package/components/forms/Switch.vue +11 -8
- package/components/forms/Textarea.vue +1 -1
- package/components/forms/input-base.js +1 -1
- package/components/icons/Icon.jsx +17 -1
- package/components/icons/Icon.prompt.md +2 -0
- package/components/icons/Icon.vue +1 -1
- package/components/icons/icon-paths.js +15 -0
- package/components/index.js +1 -1
- package/components/navigation/Breadcrumb.jsx +3 -7
- package/components/navigation/Breadcrumb.vue +2 -8
- package/components/navigation/Sidebar.jsx +3 -22
- package/components/navigation/Sidebar.vue +2 -7
- package/components/navigation/Tree.jsx +1 -1
- package/components/navigation/Tree.vue +9 -4
- package/components/navigation/nav-model.js +23 -0
- package/components/navigation/theme-store.js +9 -0
- package/components/tabs/PillTabs.vue +1 -1
- package/components/tabs/SegmentedControl.d.ts +1 -1
- package/components/tabs/SegmentedControl.jsx +1 -1
- package/components/tabs/SegmentedControl.vue +1 -1
- package/components/tabs/Tabs.jsx +1 -1
- package/components/tabs/ViewToggle.d.ts +1 -1
- package/package.json +2 -2
- package/readme.md +6 -5
- package/readme.zh-CN.md +6 -5
- package/tokens/alpha.css +3 -1
- package/tokens/colors.css +1 -1
- package/tokens/dark.css +1 -1
- package/tokens/layout.css +7 -5
- package/tokens/tokens.d.ts +22 -0
- package/tokens/tokens.js +39 -6
- package/tokens/tokens.json +90 -11
- package/tokens/typography.css +26 -9
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { px } from '../px.js'
|
|
3
3
|
import IconButton from '../buttons/IconButton.vue'
|
|
4
|
-
// Modal dialog. A size region that defaults to lg (
|
|
4
|
+
// Modal dialog. A size region that defaults to lg (40px controls) whatever the page behind it uses — a dialog does not compete for space; pass `size` to override. 24px padding, footer buttons 8px apart.
|
|
5
5
|
defineProps({ open: Boolean, title: String, subtitle: String, width: { type: [Number, String], default: 440 }, size: { type: String, default: 'lg' }, style: Object })
|
|
6
6
|
const emit = defineEmits(['close'])
|
|
7
7
|
</script>
|
|
@@ -12,8 +12,8 @@ defineProps({ items: { type: Array, default: () => [] }, style: Object })
|
|
|
12
12
|
<span v-if="i < items.length - 1" style="width:1.5px;flex:1;min-height:14px;background:var(--border-control)" />
|
|
13
13
|
</div>
|
|
14
14
|
<div :style="{ display: 'flex', flexDirection: 'column', gap: '2px', paddingBottom: i < items.length - 1 ? '18px' : 0, minWidth: 0, paddingTop: '3px' }">
|
|
15
|
-
<span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);color:var(--text-heading)">{{ it.title }}</span>
|
|
16
|
-
<span v-if="it.meta" style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-muted)">{{ it.meta }}</span>
|
|
15
|
+
<span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-heading)">{{ it.title }}</span>
|
|
16
|
+
<span v-if="it.meta" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);letter-spacing:var(--tracking-xs);color:var(--text-muted)">{{ it.meta }}</span>
|
|
17
17
|
<span v-if="it.body" style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-body);padding-top:4px">{{ it.body }}</span>
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
@@ -8,7 +8,7 @@ export interface ComboboxProps {
|
|
|
8
8
|
disabled?: boolean;
|
|
9
9
|
error?: boolean;
|
|
10
10
|
width?: number | string;
|
|
11
|
-
/** Size tier: sm
|
|
11
|
+
/** Size tier: sm 28 · md 32 · lg 40. Omit to inherit the enclosing region's tier (the default). */
|
|
12
12
|
size?: "sm" | "md" | "lg";
|
|
13
13
|
onChange?: (value: string) => void;
|
|
14
14
|
style?: React.CSSProperties;
|
|
@@ -7,7 +7,7 @@ export interface DatePickerProps {
|
|
|
7
7
|
disabled?: boolean;
|
|
8
8
|
error?: boolean;
|
|
9
9
|
width?: number | string;
|
|
10
|
-
/** Size tier: sm
|
|
10
|
+
/** Size tier: sm 28 · md 32 · lg 40. Omit to inherit the enclosing region's tier (the default). */
|
|
11
11
|
size?: "sm" | "md" | "lg";
|
|
12
12
|
/** BCP 47 locale for the month title, weekday initials and the field label (default "en-US"; e.g. "zh-CN", "ja-JP") */
|
|
13
13
|
locale?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
/** r8 hairline text input at the region's tier height (sm
|
|
2
|
+
/** r8 hairline text input at the region's tier height (sm 28 · md 32 · lg 40); readOnly = inset fill, disabled = 50% opacity, error = red border+ring. Extends native input props. */
|
|
3
3
|
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
4
4
|
/** Size tier. Omit to inherit the enclosing region's tier (the default); controls that share a row must share a tier. */
|
|
5
5
|
size?: "sm" | "md" | "lg";
|
|
@@ -3,7 +3,7 @@ import React from "react";
|
|
|
3
3
|
import { inputBase } from "./input-base.js";
|
|
4
4
|
export { inputBase };
|
|
5
5
|
|
|
6
|
-
/** Text input, r8, tier height (sm
|
|
6
|
+
/** Text input, r8, tier height (sm 28 · md 32 · lg 40 — omit `size` to inherit the region's tier). readOnly = inset fill; disabled = 50% opacity; error = red border + ring. */
|
|
7
7
|
export function Input({ readOnly, disabled, error, size, style, ...rest }) {
|
|
8
8
|
const [focus, setFocus] = React.useState(false);
|
|
9
9
|
return <input readOnly={readOnly} disabled={disabled} data-size={size} data-field={readOnly || disabled ? undefined : ""} onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}
|
|
@@ -30,7 +30,7 @@ export function Textarea({ style, rows = 4, error, disabled, showCount, maxLengt
|
|
|
30
30
|
...(error || over ? { borderColor: "var(--danger)" } : null),
|
|
31
31
|
...(focus && !disabled ? { borderColor: error || over ? "var(--danger)" : "var(--focus-border)", boxShadow: `0 0 0 2px ${error || over ? "var(--danger-bg)" : "var(--focus-ring)"}` } : null),
|
|
32
32
|
...(disabled ? { opacity: .5, cursor: "not-allowed", background: "var(--bg-inset)" } : null), ...style }} {...rest} />
|
|
33
|
-
{showCount ? <span style={{ position: "absolute", right: 10, bottom: 7, fontFamily: "var(--font-
|
|
33
|
+
{showCount ? <span style={{ position: "absolute", right: 10, bottom: 7, fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", color: over ? "var(--danger)" : "var(--text-muted)" }}>{len}{maxLength != null ? `/${maxLength}` : ""}</span> : null}
|
|
34
34
|
</div>
|
|
35
35
|
);
|
|
36
36
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref, computed } from 'vue'
|
|
3
3
|
import { inputBase } from './input-base.js'
|
|
4
|
-
// Text input, r8, tier height (sm
|
|
4
|
+
// Text input, r8, tier height (sm 28 · md 32 · lg 40 — omit `size` to inherit the region's tier). v-model. readonly = inset fill; disabled = 50% opacity; error = red border + ring.
|
|
5
5
|
// Multiline variant lives in Textarea.vue.
|
|
6
6
|
const props = defineProps({ modelValue: String, readonly: Boolean, disabled: Boolean, error: Boolean, size: String, style: Object })
|
|
7
7
|
defineEmits(['update:modelValue'])
|
|
@@ -9,7 +9,7 @@ export interface MultiSelectProps {
|
|
|
9
9
|
maxTags?: number;
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
width?: number | string;
|
|
12
|
-
/** Size tier: sm
|
|
12
|
+
/** Size tier: sm 28 · md 32 · lg 40. Omit to inherit the enclosing region's tier (the default). */
|
|
13
13
|
size?: "sm" | "md" | "lg";
|
|
14
14
|
onChange?: (value: string[]) => void;
|
|
15
15
|
style?: React.CSSProperties;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
/** Segmented −/value/+ number stepper at the region's tier height, r8 — "Preview rows" control. */
|
|
3
3
|
export interface NumberStepperProps {
|
|
4
|
-
/** Size tier: sm
|
|
4
|
+
/** Size tier: sm 28 · md 32 · lg 40. Omit to inherit the enclosing region's tier (the default). */
|
|
5
5
|
size?: "sm" | "md" | "lg";
|
|
6
6
|
value?: number;
|
|
7
7
|
onChange?: (next: number) => void;
|
|
@@ -3,7 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
export interface SearchInputProps {
|
|
4
4
|
placeholder?: string;
|
|
5
5
|
bare?: boolean;
|
|
6
|
-
/** Size tier: sm
|
|
6
|
+
/** Size tier: sm 28 · md 32 · lg 40. Omit to inherit the enclosing region's tier (the default). */
|
|
7
7
|
size?: "sm" | "md" | "lg";
|
|
8
8
|
value?: string;
|
|
9
9
|
/** Receives the new string value (consistent with Select / Combobox / MultiSelect) */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
/** Hairline select, r8, at the region's tier height (sm
|
|
2
|
+
/** Hairline select, r8, at the region's tier height (sm 28 · md 32 · lg 40). disabled fills inset like Input's readOnly. */
|
|
3
3
|
export interface SelectProps {
|
|
4
4
|
options?: string[];
|
|
5
5
|
/** Size tier. Omit to inherit the enclosing region's tier (the default); controls that share a row must share a tier. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Icon } from "../icons/Icon.jsx";
|
|
3
3
|
|
|
4
|
-
/** Native select styled per Keybricks: r8, tier height (sm
|
|
4
|
+
/** Native select styled per Keybricks: r8, tier height (sm 28 · md 32 · lg 40 — omit `size` to inherit the region's tier). disabled = inset fill (matches Input readOnly), 50% opacity; error = red border. */
|
|
5
5
|
export function Select({ options = [], size, value, onChange, placeholder, disabled, error, style }) {
|
|
6
6
|
return (
|
|
7
7
|
<div data-size={size} style={{ position: "relative", display: "inline-flex", ...style }}>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import Icon from '../icons/Icon.vue'
|
|
3
|
-
// Native select styled per Keybricks: r8, tier height (sm
|
|
3
|
+
// Native select styled per Keybricks: r8, tier height (sm 28 · md 32 · lg 40 — omit `size` to inherit the region's tier). disabled = inset fill (matches Input readonly), 50% opacity; error = red border.
|
|
4
4
|
const props = defineProps({ modelValue: String, options: { type: Array, default: () => [] }, size: String, placeholder: String, disabled: Boolean, error: Boolean, style: Object })
|
|
5
5
|
defineEmits(['update:modelValue'])
|
|
6
6
|
</script>
|
|
@@ -13,7 +13,7 @@ export function Slider({ value = 50, min = 0, max = 100, step = 1, labels, showV
|
|
|
13
13
|
<div style={{ position: "absolute", left: 0, right: 0, height: 6, borderRadius: "var(--radius-full)", background: "var(--track)" }}></div>
|
|
14
14
|
<div style={{ position: "absolute", left: 0, width: pct + "%", height: 6, borderRadius: "var(--radius-full)", background: "var(--action-primary)" }}></div>
|
|
15
15
|
<span style={{ position: "absolute", left: pct + "%", transform: "translateX(-50%)", width: 16, height: 16, borderRadius: "var(--radius-full)", background: "var(--bg-surface)", border: "2px solid var(--action-primary)", boxSizing: "border-box", boxShadow: drag ? "0 0 0 4px var(--focus-ring), 0 1px 2px rgba(0,0,0,.15)" : "var(--thumb-ring, 0 1px 2px rgba(0,0,0,.15))", transition: "box-shadow var(--dur-fast) var(--ease-out)" }}></span>
|
|
16
|
-
{(drag || showValue) && <span style={{ position: "absolute", left: pct + "%", transform: "translateX(-50%)", bottom: 22, background: "var(--tooltip-bg)", color: "var(--tooltip-fg)", borderRadius: "var(--radius-sm)", padding: "2px 6px", fontFamily: "var(--font-
|
|
16
|
+
{(drag || showValue) && <span style={{ position: "absolute", left: pct + "%", transform: "translateX(-50%)", bottom: 22, background: "var(--tooltip-bg)", color: "var(--tooltip-fg)", borderRadius: "var(--radius-sm)", padding: "2px 6px", fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", whiteSpace: "nowrap", pointerEvents: "none" }}>{value}{unit}</span>}
|
|
17
17
|
</div>
|
|
18
18
|
{labels && <div style={{ display: "flex", justifyContent: "space-between", paddingTop: 4, fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-muted)" }}><span>{min}{unit}</span><span>{max}{unit}</span></div>}
|
|
19
19
|
</div>
|
|
@@ -16,7 +16,7 @@ onUnmounted(up)
|
|
|
16
16
|
<div style="position:absolute;left:0;right:0;height:6px;border-radius:var(--radius-full);background:var(--track)" />
|
|
17
17
|
<div :style="{ position: 'absolute', left: 0, width: pct + '%', height: '6px', borderRadius: 'var(--radius-full)', background: 'var(--action-primary)' }" />
|
|
18
18
|
<span :style="{ position: 'absolute', left: pct + '%', transform: 'translateX(-50%)', width: '16px', height: '16px', borderRadius: 'var(--radius-full)', background: 'var(--bg-surface)', border: '2px solid var(--action-primary)', boxSizing: 'border-box', boxShadow: drag ? '0 0 0 4px var(--focus-ring), 0 1px 2px rgba(0,0,0,.15)' : 'var(--thumb-ring, 0 1px 2px rgba(0,0,0,.15))', transition: 'box-shadow var(--dur-fast) var(--ease-out)' }" />
|
|
19
|
-
<span v-if="drag || showValue" :style="{ position: 'absolute', left: pct + '%', transform: 'translateX(-50%)', bottom: '22px', background: 'var(--tooltip-bg)', color: 'var(--tooltip-fg)', borderRadius: 'var(--radius-sm)', padding: '2px 6px', fontFamily: 'var(--font-
|
|
19
|
+
<span v-if="drag || showValue" :style="{ position: 'absolute', left: pct + '%', transform: 'translateX(-50%)', bottom: '22px', background: 'var(--tooltip-bg)', color: 'var(--tooltip-fg)', borderRadius: 'var(--radius-sm)', padding: '2px 6px', fontFamily: 'var(--font-sans)', fontVariantNumeric: 'tabular-nums', fontSize: 'var(--text-xs)', whiteSpace: 'nowrap', pointerEvents: 'none' }">{{ modelValue }}{{ unit }}</span>
|
|
20
20
|
</div>
|
|
21
21
|
<div v-if="labels" style="display:flex;justify-content:space-between;padding-top:4px;font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-muted)"><span>{{ min }}{{ unit }}</span><span>{{ max }}{{ unit }}</span></div>
|
|
22
22
|
</div>
|
|
@@ -20,7 +20,7 @@ import { Icon } from "../icons/Icon.jsx";
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
/* Geometry in CSS so it follows the tier without JavaScript: knob = track height − 4, glyph = half the track height. */
|
|
23
|
-
const H = "var(--switch-h)", W = "var(--switch-w)", KNOB = "calc(var(--switch-h) - 4px)", GLYPH = "calc(var(--switch-h) * .5)"
|
|
23
|
+
const H = "var(--switch-h)", W = "var(--switch-w)", KNOB = "calc(var(--switch-h) - 4px)", GLYPH = "calc(var(--switch-h) * .5)", TEXT = "max(calc(var(--switch-h) * .5), var(--text-glyph-min))" /* text never drops below the glyph floor (12px in CJK) */;
|
|
24
24
|
|
|
25
25
|
const pick = (v, on) => (v && typeof v === "object" ? (on ? v.on : v.off) : v);
|
|
26
26
|
|
|
@@ -60,14 +60,14 @@ export function Switch({ checked = false, onChange, disabled, size, tone = "prim
|
|
|
60
60
|
const glyphText = pick(knobText, checked);
|
|
61
61
|
const knobX = checked ? `calc(100% - ${H} + 2px)` : 2;
|
|
62
62
|
|
|
63
|
-
const knob = <span aria-hidden="true" style={{ position: "absolute", top: 2, left: knobX, width: KNOB, height: KNOB, borderRadius: "var(--radius-full)", background: "var(--bg-surface)", boxShadow: "0 1px 2px rgba(0,0,0,.18)", transition: "left 160ms ease-out", display: "flex", alignItems: "center", justifyContent: "center", color: track, fontSize:
|
|
63
|
+
const knob = <span aria-hidden="true" style={{ position: "absolute", top: 2, left: knobX, width: KNOB, height: KNOB, borderRadius: "var(--radius-full)", background: "var(--bg-surface)", boxShadow: "0 1px 2px rgba(0,0,0,.18)", transition: "left 160ms ease-out", display: "flex", alignItems: "center", justifyContent: "center", color: track, fontSize: TEXT, lineHeight: 1, fontWeight: "var(--weight-semibold)", fontFamily: "var(--font-sans)", overflow: "hidden" }}>
|
|
64
64
|
{loading
|
|
65
65
|
? <span style={{ width: GLYPH, height: GLYPH, borderRadius: "50%", border: `1.5px solid ${track}`, borderRightColor: "transparent", animation: "kb-spin 720ms linear infinite" }}></span>
|
|
66
66
|
: glyphIcon ? <Icon name={glyphIcon} size={10} strokeWidth={2.5} style={{ width: GLYPH, height: GLYPH }} />
|
|
67
67
|
: glyphText ? <span>{String(glyphText).slice(0, 1)}</span> : null}
|
|
68
68
|
</span>;
|
|
69
69
|
|
|
70
|
-
const insideStyle = (side) => ({ position: "absolute", top: 0, bottom: 0, [side]: 6, display: "flex", alignItems: "center", fontSize:
|
|
70
|
+
const insideStyle = (side) => ({ position: "absolute", top: 0, bottom: 0, [side]: 6, display: "flex", alignItems: "center", fontSize: TEXT, lineHeight: 1, fontWeight: "var(--weight-semibold)", fontFamily: "var(--font-sans)", letterSpacing: ".2px", color: "var(--on-accent)", transition: "opacity 160ms ease-out", pointerEvents: "none", whiteSpace: "nowrap" });
|
|
71
71
|
|
|
72
72
|
const pill = <span role="switch" aria-checked={checked} aria-busy={loading || undefined} aria-disabled={disabled || undefined} tabIndex={inert ? -1 : 0} onClick={flip}
|
|
73
73
|
onKeyDown={e => { if (e.key === " " || e.key === "Enter") { e.preventDefault(); flip(); } }}
|
|
@@ -12,17 +12,20 @@ const props = defineProps({
|
|
|
12
12
|
size: String, tone: { type: String, default: 'primary' },
|
|
13
13
|
onColor: String, offColor: String,
|
|
14
14
|
knobIcon: [String, Object], knobText: [String, Object],
|
|
15
|
-
insideOn: String, insideOff: String, label: String, labelOn: String, labelOff: String,
|
|
15
|
+
insideOn: String, insideOff: String, inlinePrompt: Object, label: String, labelOn: String, labelOff: String,
|
|
16
16
|
beforeChange: Function, style: Object
|
|
17
17
|
})
|
|
18
18
|
const emit = defineEmits(['update:checked', 'change'])
|
|
19
|
-
const H = 'var(--switch-h)', W = 'var(--switch-w)', KNOB = 'calc(var(--switch-h) - 4px)', GLYPH = 'calc(var(--switch-h) * .5)'
|
|
19
|
+
const H = 'var(--switch-h)', W = 'var(--switch-w)', KNOB = 'calc(var(--switch-h) - 4px)', GLYPH = 'calc(var(--switch-h) * .5)', TEXT = 'max(calc(var(--switch-h) * .5), var(--text-glyph-min))' /* text never drops below the glyph floor (12px in CJK) */
|
|
20
20
|
const busy = ref(false); let alive = true; onBeforeUnmount(() => { alive = false })
|
|
21
21
|
const isLoading = computed(() => props.loading || busy.value)
|
|
22
22
|
const inert = computed(() => props.disabled || isLoading.value)
|
|
23
|
-
|
|
23
|
+
// inlinePrompt { on, off } is an alias of the insideOn / insideOff pair (same as Switch.jsx)
|
|
24
|
+
const inOn = computed(() => props.insideOn ?? (props.inlinePrompt ? props.inlinePrompt.on : undefined))
|
|
25
|
+
const inOff = computed(() => props.insideOff ?? (props.inlinePrompt ? props.inlinePrompt.off : undefined))
|
|
26
|
+
const hasInside = computed(() => inOn.value != null || inOff.value != null)
|
|
24
27
|
// inside text needs a wider track so the knob never covers the word: text runs at half the track height, ~.68em per character
|
|
25
|
-
const width = computed(() => { if (!hasInside.value) return W; const n = Math.max((
|
|
28
|
+
const width = computed(() => { if (!hasInside.value) return W; const n = Math.max((inOn.value || '').length, (inOff.value || '').length); return `max(${W}, calc(${H} * ${(1 + n * .34).toFixed(2)} + 6px))` })
|
|
26
29
|
const track = computed(() => props.checked ? (props.onColor || (props.tone === 'semantic' ? 'var(--success)' : 'var(--action-primary)')) : (props.offColor || (props.tone === 'semantic' ? 'var(--danger)' : 'var(--border-strong)')))
|
|
27
30
|
const pick = (v) => v && typeof v === 'object' ? (props.checked ? v.on : v.off) : v
|
|
28
31
|
const glyphIcon = computed(() => pick(props.knobIcon))
|
|
@@ -40,7 +43,7 @@ async function flip() {
|
|
|
40
43
|
}
|
|
41
44
|
emit('update:checked', next); emit('change', next)
|
|
42
45
|
}
|
|
43
|
-
const insideStyle = (side, on) => ({ position: 'absolute', top: 0, bottom: 0, [side]: '6px', display: 'flex', alignItems: 'center', fontSize:
|
|
46
|
+
const insideStyle = (side, on) => ({ position: 'absolute', top: 0, bottom: 0, [side]: '6px', display: 'flex', alignItems: 'center', fontSize: TEXT, lineHeight: 1, fontWeight: 'var(--weight-semibold)', fontFamily: 'var(--font-sans)', letterSpacing: '.2px', color: 'var(--on-accent)', transition: 'opacity 160ms ease-out', pointerEvents: 'none', whiteSpace: 'nowrap', opacity: on === props.checked ? 1 : 0 })
|
|
44
47
|
const cap = (active) => ({ fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', fontFamily: 'var(--font-sans)', fontWeight: active ? 'var(--weight-medium)' : 'var(--weight-regular)', color: active ? 'var(--text-heading)' : 'var(--text-muted)', transition: 'color 160ms ease-out', cursor: inert.value ? 'default' : 'pointer', userSelect: 'none' })
|
|
45
48
|
</script>
|
|
46
49
|
<template>
|
|
@@ -48,9 +51,9 @@ const cap = (active) => ({ fontSize: 'var(--control-text)', lineHeight: 'var(--c
|
|
|
48
51
|
<span v-if="labelOff != null" :style="cap(!checked)" @click="flip">{{ labelOff }}</span>
|
|
49
52
|
<span role="switch" :aria-checked="checked" :aria-busy="isLoading || undefined" :aria-disabled="disabled || undefined" :tabindex="inert ? -1 : 0" @click="flip" @keydown.space.prevent="flip" @keydown.enter.prevent="flip"
|
|
50
53
|
:style="{ position: 'relative', display: 'inline-block', width: width, height: H, borderRadius: 'var(--radius-full)', '--sw-track': track, background: 'var(--sw-bg, var(--sw-track))', transition: 'background 160ms ease-out', cursor: inert ? (isLoading ? 'progress' : 'not-allowed') : 'pointer', flexShrink: 0, verticalAlign: 'middle', outline: 'none' }">
|
|
51
|
-
<span v-if="hasInside" :style="insideStyle('left', true)">{{
|
|
52
|
-
<span v-if="hasInside" :style="insideStyle('right', false)">{{
|
|
53
|
-
<span aria-hidden="true" :style="{ position: 'absolute', top: '2px', left: knobX, width: KNOB, height: KNOB, borderRadius: 'var(--radius-full)', background: 'var(--bg-surface)', boxShadow: '0 1px 2px rgba(0,0,0,.18)', transition: 'left 160ms ease-out', display: 'flex', alignItems: 'center', justifyContent: 'center', color: track, fontSize:
|
|
54
|
+
<span v-if="hasInside" :style="insideStyle('left', true)">{{ inOn }}</span>
|
|
55
|
+
<span v-if="hasInside" :style="insideStyle('right', false)">{{ inOff }}</span>
|
|
56
|
+
<span aria-hidden="true" :style="{ position: 'absolute', top: '2px', left: knobX, width: KNOB, height: KNOB, borderRadius: 'var(--radius-full)', background: 'var(--bg-surface)', boxShadow: '0 1px 2px rgba(0,0,0,.18)', transition: 'left 160ms ease-out', display: 'flex', alignItems: 'center', justifyContent: 'center', color: track, fontSize: TEXT, lineHeight: 1, fontWeight: 'var(--weight-semibold)', fontFamily: 'var(--font-sans)', overflow: 'hidden' }">
|
|
54
57
|
<span v-if="isLoading" :style="{ width: GLYPH, height: GLYPH, borderRadius: '50%', border: '1.5px solid ' + track, borderRightColor: 'transparent', animation: 'kb-spin 720ms linear infinite' }"></span>
|
|
55
58
|
<Icon v-else-if="glyphIcon" :name="glyphIcon" :size="10" :stroke-width="2.5" :style="{ width: GLYPH, height: GLYPH }" />
|
|
56
59
|
<span v-else-if="glyphText">{{ glyphText }}</span>
|
|
@@ -15,6 +15,6 @@ const s = computed(() => ({ ...inputBase, height: '90px', resize: 'vertical', pa
|
|
|
15
15
|
<template>
|
|
16
16
|
<div style="position:relative">
|
|
17
17
|
<textarea :rows="rows" :disabled="disabled" :data-size="size" :data-field="disabled ? undefined : ''" :maxlength="showCount ? undefined : maxLength" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" @focus="focus = true" @blur="focus = false" :style="s"></textarea>
|
|
18
|
-
<span v-if="showCount" :style="{ position: 'absolute', right: '10px', bottom: '7px', fontFamily: 'var(--font-
|
|
18
|
+
<span v-if="showCount" :style="{ position: 'absolute', right: '10px', bottom: '7px', fontFamily: 'var(--font-sans)', fontVariantNumeric: 'tabular-nums', fontSize: 'var(--text-xs)', color: over ? 'var(--danger)' : 'var(--text-muted)' }">{{ len }}{{ maxLength != null ? `/${maxLength}` : '' }}</span>
|
|
19
19
|
</div>
|
|
20
20
|
</template>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// Shared base style for text-like controls (Input, Textarea, Combobox, DatePicker).
|
|
2
2
|
// Lives in a plain module because <script setup> cannot export values.
|
|
3
|
-
// Height, text and padding come from the size tier of the enclosing region (tokens/layout.css): sm
|
|
3
|
+
// Height, text and padding come from the size tier of the enclosing region (tokens/layout.css): sm 28 · md 32 · lg 40.
|
|
4
4
|
export const inputBase = { height: 'var(--control-h)', borderRadius: 'var(--radius-md)', border: '1px solid var(--field-border, var(--border-control))', background: 'var(--bg-surface)', padding: '0 var(--control-px)', fontFamily: 'var(--font-sans)', fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', color: 'var(--text-primary)', outline: 'none', boxSizing: 'border-box', width: '100%' }
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
// Keybricks icon set — the source file uses Lucide stroke glyphs (24-grid, stroke 2,
|
|
3
3
|
// round caps/joins, currentColor) rendered at 14/16px. Path data below is from the
|
|
4
|
-
// public Lucide set, matching the glyphs used across Keyfunc UI v8
|
|
4
|
+
// public Lucide set, matching the glyphs used across Keyfunc UI v8, plus thirteen Keybricks
|
|
5
|
+
// glyphs (event … time-restatement, ai, ai-sparkles) drawn to the same rules.
|
|
5
6
|
export const ICON_PATHS = {
|
|
6
7
|
search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
|
|
7
8
|
bell: '<path d="M10.268 21a2 2 0 0 0 3.464 0"/><path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326"/>',
|
|
@@ -74,6 +75,21 @@ export const ICON_PATHS = {
|
|
|
74
75
|
info: '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',
|
|
75
76
|
"triangle-alert": '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 20h16a2 2 0 0 0 1.73-2Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
|
|
76
77
|
calendar: '<path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/>',
|
|
78
|
+
// Keybricks glyphs — drawn to the core-set rules (see readme.md › Iconography)
|
|
79
|
+
event: '<path d="M17 2 22 12 17 22 12 12Z"/><path d="M7 2 2 12l5 10"/><path d="M12 2 7 12l5 10"/>',
|
|
80
|
+
consolidation: '<rect width="4" height="4" x="2" y="17" rx="1"/><rect width="4" height="4" x="10" y="17" rx="1"/><rect width="4" height="4" x="18" y="17" rx="1"/><path d="M4 17v-5a2 2 0 0 1 2-2h2"/><path d="M20 17v-5a2 2 0 0 0-2-2h-2"/><path d="M12 17v-4"/><path d="M12 3v6"/><path d="M9 6h6"/>',
|
|
81
|
+
journal: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="M2 8h20"/><path d="M12 8v12"/><path d="M6 12h2"/><path d="M16 16h2"/>',
|
|
82
|
+
reconciliation: '<path d="M3 6h8"/><path d="m15 4 2 2 4-4"/><path d="M3 10h18"/><path d="M3 18h8"/><path d="m15 16 2 2 4-4"/><path d="M3 22h18"/>',
|
|
83
|
+
"view-tree": '<rect width="4" height="4" x="4" y="2" rx="1"/><rect width="4" height="4" x="16" y="10" rx="1"/><rect width="4" height="4" x="16" y="18" rx="1"/><path d="M6 6v4a2 2 0 0 0 2 2h8"/><path d="M6 10v8a2 2 0 0 0 2 2h8"/>',
|
|
84
|
+
"drilldown": '<path d="M12 2v11"/><path d="m8 9 4 4 4-4"/><path d="M19 17H5"/><path d="M16 21H8"/>',
|
|
85
|
+
"add-below": '<path d="M11 2v10"/><path d="M6 7h10"/><path d="M22 9v5a4 4 0 0 1-4 4H2"/><path d="m6 14-4 4 4 4"/>',
|
|
86
|
+
"copy-data": '<path d="M14 22h5a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-5"/><path d="m11 16 4-4-4-4"/><path d="M3 22v-6a4 4 0 0 1 4-4h8"/>',
|
|
87
|
+
"task-execution": '<rect width="8" height="4" x="8" y="2" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M8 14h8"/><path d="m13 11 3 3-3 3"/>',
|
|
88
|
+
"time-future": '<path d="M20 6A10 10 0 1 0 22 12"/><path d="M12 7v5h5"/><path d="M21 2v5h-5"/>',
|
|
89
|
+
"time-restatement": '<path d="M2 12A10 10 0 1 0 4 6"/><path d="M12 7v5h5"/><path d="M3 2v5h5"/>',
|
|
90
|
+
// AI glyphs (2.3.0) — traced from the two reference glyphs, redrawn to core rules (stroke 2, no fill, integer endpoints, >=4 between separate elements)
|
|
91
|
+
ai: '<path d="M9 4H6a4 4 0 0 0-4 4v10a4 4 0 0 0 4 4h10a4 4 0 0 0 4-4v-3"/><path d="M16 2c1.2 3 3 4.8 6 6c-3 1.2-4.8 3-6 6c-1.2-3-3-4.8-6-6c3-1.2 4.8-3 6-6Z"/><path d="M9 12c.6 1.5 1.5 2.4 3 3c-1.5.6-2.4 1.5-3 3c-.6-1.5-1.5-2.4-3-3c1.5-.6 2.4-1.5 3-3Z"/>',
|
|
92
|
+
"ai-sparkles": '<path d="M9 2c1.4 3.5 3.5 5.6 7 7c-3.5 1.4-5.6 3.5-7 7c-1.4-3.5-3.5-5.6-7-7c3.5-1.4 5.6-3.5 7-7Z"/><path d="M17 13c.8 2 2 3.2 4 4c-2 .8-3.2 2-4 4c-.8-2-2-3.2-4-4c2-.8 3.2-2 4-4Z"/>',
|
|
77
93
|
};
|
|
78
94
|
|
|
79
95
|
import { FEATHER_PATHS } from "./feather-paths.js";
|
|
@@ -6,3 +6,5 @@ Stroke icon component embedding the Lucide glyphs used across Keybricks; use for
|
|
|
6
6
|
```
|
|
7
7
|
|
|
8
8
|
Sizes in the product: 14 (dense chrome), 16 (nav/buttons), 18 (palette tiles), 22 (node tiles). Color inherits `currentColor`. Categories pair hue + tint bg: source `database/table/layers/grid-3x3`, join `text-search/shuffle`, transform `sliders-horizontal/split/arrow-right-left/menu/funnel/chart-no-axes-column/trending-up/code`, merge `database/merge`, output `table/layers/grid-3x3`.
|
|
9
|
+
|
|
10
|
+
Keybricks glyphs (drawn to the same grid rules): modules `reconciliation`, `consolidation`, `journal`, `event`, `view-tree`; actions `drilldown`, `add-below`, `copy-data`, `task-execution`, `time-future`, `time-restatement`. For hierarchy use `layers`; for change use `repeat`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
// Keybricks icon set — Lucide stroke glyphs (24-grid, stroke 2, round caps/joins, currentColor), rendered at 14/16px.
|
|
3
|
-
// Full path dictionary mirrors icons/Icon.jsx's ICON_PATHS (
|
|
3
|
+
// Full path dictionary mirrors icons/Icon.jsx's ICON_PATHS (81 entries) — see that file for the complete list.
|
|
4
4
|
import { VUE_ICON_PATHS } from './icon-paths.js'
|
|
5
5
|
import { FEATHER_PATHS } from './feather-paths.js'
|
|
6
6
|
const props = defineProps({ name: String, size: { type: Number, default: 16 }, strokeWidth: { type: Number, default: 2 }, color: String, style: Object })
|
|
@@ -70,4 +70,19 @@ export const VUE_ICON_PATHS = {
|
|
|
70
70
|
info: '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',
|
|
71
71
|
"triangle-alert": '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 20h16a2 2 0 0 0 1.73-2Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
|
|
72
72
|
calendar: '<path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/>',
|
|
73
|
+
// Keybricks glyphs — drawn to the core-set rules (see readme.md › Iconography)
|
|
74
|
+
event: '<path d="M17 2 22 12 17 22 12 12Z"/><path d="M7 2 2 12l5 10"/><path d="M12 2 7 12l5 10"/>',
|
|
75
|
+
consolidation: '<rect width="4" height="4" x="2" y="17" rx="1"/><rect width="4" height="4" x="10" y="17" rx="1"/><rect width="4" height="4" x="18" y="17" rx="1"/><path d="M4 17v-5a2 2 0 0 1 2-2h2"/><path d="M20 17v-5a2 2 0 0 0-2-2h-2"/><path d="M12 17v-4"/><path d="M12 3v6"/><path d="M9 6h6"/>',
|
|
76
|
+
journal: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="M2 8h20"/><path d="M12 8v12"/><path d="M6 12h2"/><path d="M16 16h2"/>',
|
|
77
|
+
reconciliation: '<path d="M3 6h8"/><path d="m15 4 2 2 4-4"/><path d="M3 10h18"/><path d="M3 18h8"/><path d="m15 16 2 2 4-4"/><path d="M3 22h18"/>',
|
|
78
|
+
"view-tree": '<rect width="4" height="4" x="4" y="2" rx="1"/><rect width="4" height="4" x="16" y="10" rx="1"/><rect width="4" height="4" x="16" y="18" rx="1"/><path d="M6 6v4a2 2 0 0 0 2 2h8"/><path d="M6 10v8a2 2 0 0 0 2 2h8"/>',
|
|
79
|
+
"drilldown": '<path d="M12 2v11"/><path d="m8 9 4 4 4-4"/><path d="M19 17H5"/><path d="M16 21H8"/>',
|
|
80
|
+
"add-below": '<path d="M11 2v10"/><path d="M6 7h10"/><path d="M22 9v5a4 4 0 0 1-4 4H2"/><path d="m6 14-4 4 4 4"/>',
|
|
81
|
+
"copy-data": '<path d="M14 22h5a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-5"/><path d="m11 16 4-4-4-4"/><path d="M3 22v-6a4 4 0 0 1 4-4h8"/>',
|
|
82
|
+
"task-execution": '<rect width="8" height="4" x="8" y="2" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M8 14h8"/><path d="m13 11 3 3-3 3"/>',
|
|
83
|
+
"time-future": '<path d="M20 6A10 10 0 1 0 22 12"/><path d="M12 7v5h5"/><path d="M21 2v5h-5"/>',
|
|
84
|
+
"time-restatement": '<path d="M2 12A10 10 0 1 0 4 6"/><path d="M12 7v5h5"/><path d="M3 2v5h5"/>',
|
|
85
|
+
// AI glyphs (2.3.0) — traced from the two reference glyphs, redrawn to core rules (stroke 2, no fill, integer endpoints, >=4 between separate elements)
|
|
86
|
+
ai: '<path d="M9 4H6a4 4 0 0 0-4 4v10a4 4 0 0 0 4 4h10a4 4 0 0 0 4-4v-3"/><path d="M16 2c1.2 3 3 4.8 6 6c-3 1.2-4.8 3-6 6c-1.2-3-3-4.8-6-6c3-1.2 4.8-3 6-6Z"/><path d="M9 12c.6 1.5 1.5 2.4 3 3c-1.5.6-2.4 1.5-3 3c-.6-1.5-1.5-2.4-3-3c1.5-.6 2.4-1.5 3-3Z"/>',
|
|
87
|
+
"ai-sparkles": '<path d="M9 2c1.4 3.5 3.5 5.6 7 7c-3.5 1.4-5.6 3.5-7 7c-1.4-3.5-3.5-5.6-7-7c3.5-1.4 5.6-3.5 7-7Z"/><path d="M17 13c.8 2 2 3.2 4 4c-2 .8-3.2 2-4 4c-.8-2-2-3.2-4-4c2-.8 3.2-2 4-4Z"/>',
|
|
73
88
|
};
|
package/components/index.js
CHANGED
|
@@ -90,7 +90,7 @@ export { ICON_PATHS, Icon } from "./icons/Icon.jsx";
|
|
|
90
90
|
export { Unicon } from "./icons/Unicon.jsx";
|
|
91
91
|
|
|
92
92
|
/* navigation/ */
|
|
93
|
-
export { Breadcrumb, ThemeToggle } from "./navigation/Breadcrumb.jsx";
|
|
93
|
+
export { Breadcrumb, ThemeToggle, applyStoredTheme } from "./navigation/Breadcrumb.jsx";
|
|
94
94
|
export { DocTree } from "./navigation/DocTree.jsx";
|
|
95
95
|
export { KEYBRICKS_NAV, Sidebar } from "./navigation/Sidebar.jsx";
|
|
96
96
|
export { PageHeading, PageToolbar, TopBar } from "./navigation/TopBar.jsx";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Icon } from "../icons/Icon.jsx";
|
|
3
|
+
import { applyStoredTheme } from "./theme-store.js";
|
|
3
4
|
|
|
4
5
|
function Crumb({ label, last, hovered, onSelect }) {
|
|
5
6
|
const [hover, setHover] = React.useState(false);
|
|
@@ -111,10 +112,5 @@ export function ThemeToggle({ size = 34, dark: darkProp, onToggle, style }) {
|
|
|
111
112
|
);
|
|
112
113
|
}
|
|
113
114
|
|
|
114
|
-
/** Reads the persisted theme and applies it before first paint. Call once at app start. */
|
|
115
|
-
export
|
|
116
|
-
if (typeof document === "undefined") return;
|
|
117
|
-
let t = "light";
|
|
118
|
-
try { t = localStorage.getItem("kb-theme") || "light"; } catch (e) {}
|
|
119
|
-
document.documentElement.dataset.theme = t;
|
|
120
|
-
}
|
|
115
|
+
/** Reads the persisted theme and applies it before first paint. Call once at app start. Defined in theme-store.js, shared with the Vue entry. */
|
|
116
|
+
export { applyStoredTheme };
|
|
@@ -62,11 +62,5 @@ const crumbStyle = (i, it) => ({ fontFamily: 'var(--font-sans)', fontSize: 'var(
|
|
|
62
62
|
</nav>
|
|
63
63
|
</template>
|
|
64
64
|
|
|
65
|
-
<!-- applyStoredTheme()
|
|
66
|
-
|
|
67
|
-
export function applyStoredTheme() {
|
|
68
|
-
let t = 'light'
|
|
69
|
-
try { t = localStorage.getItem('kb-theme') || 'light' } catch (e) {}
|
|
70
|
-
document.documentElement.dataset.theme = t
|
|
71
|
-
}
|
|
72
|
-
-->
|
|
65
|
+
<!-- applyStoredTheme() lives in ./theme-store.js (shared with React) and is exported from the Vue entry.
|
|
66
|
+
Call it once in main.js before mounting. -->
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Icon } from "../icons/Icon.jsx";
|
|
3
|
+
import { KEYBRICKS_NAV } from "./nav-model.js";
|
|
3
4
|
|
|
4
5
|
function NavItem({ icon, label, active, indent, collapsed, badge, onClick }) {
|
|
5
6
|
const [hover, setHover] = React.useState(false);
|
|
@@ -51,25 +52,5 @@ export function Sidebar({ items = [], footerItems = [{ icon: "circle-help", labe
|
|
|
51
52
|
);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
/** Default Keybricks nav model (Home dashboard). */
|
|
55
|
-
export
|
|
56
|
-
{ icon: "house", label: "Home" },
|
|
57
|
-
{ icon: "file-text", label: "Documents" },
|
|
58
|
-
{ icon: "layout-grid", label: "Business Intelligence" },
|
|
59
|
-
{ icon: "database", label: "Data Integration" },
|
|
60
|
-
{ icon: "scale", label: "Reconciliation" },
|
|
61
|
-
{ icon: "network", label: "Consolidation" },
|
|
62
|
-
{
|
|
63
|
-
icon: "folder-cog", label: "Management", children: [
|
|
64
|
-
{ icon: "box", label: "Modeling" },
|
|
65
|
-
{ icon: "workflow", label: "Workflow" },
|
|
66
|
-
{ icon: "shield", label: "Security Management" },
|
|
67
|
-
{ icon: "refresh-cw", label: "System Migration" },
|
|
68
|
-
{ icon: "chart-column", label: "Business Logic" },
|
|
69
|
-
{ icon: "list-todo", label: "Tasks Management" },
|
|
70
|
-
{ icon: "circle-check", label: "Validation Rules" },
|
|
71
|
-
{ icon: "settings-2", label: "Control Center" },
|
|
72
|
-
{ icon: "network", label: "Reconciliation" },
|
|
73
|
-
],
|
|
74
|
-
},
|
|
75
|
-
];
|
|
55
|
+
/** Default Keybricks nav model (Home dashboard) — defined in nav-model.js, shared with the Vue entry. */
|
|
56
|
+
export { KEYBRICKS_NAV };
|
|
@@ -45,10 +45,5 @@ function itemStyle(active, indent) { return { height: indent ? 'var(--h-subnav-i
|
|
|
45
45
|
</aside>
|
|
46
46
|
</template>
|
|
47
47
|
|
|
48
|
-
<!--
|
|
49
|
-
|
|
50
|
-
{ icon: 'house', label: 'Home' }, { icon: 'file-text', label: 'Documents' },
|
|
51
|
-
{ icon: 'layout-grid', label: 'Business Intelligence' }, { icon: 'database', label: 'Data Integration' },
|
|
52
|
-
{ icon: 'scale', label: 'Reconciliation' }, { icon: 'network', label: 'Consolidation' },
|
|
53
|
-
{ icon: 'folder-cog', label: 'Management', children: [ ... ] },
|
|
54
|
-
] -->
|
|
48
|
+
<!-- The product's nav model is KEYBRICKS_NAV in ./nav-model.js — also exported from the Vue entry:
|
|
49
|
+
import { Sidebar, KEYBRICKS_NAV } from '@keyfunc/keybricks-ds/vue' -->
|
|
@@ -165,7 +165,7 @@ function Row({ item, state, cfg, api }) {
|
|
|
165
165
|
<Highlight text={titleOf(node)} query={cfg.searchValue} />
|
|
166
166
|
</span>
|
|
167
167
|
{node.tag ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: 1, padding: "2px 5px", borderRadius: "var(--radius-sm)", background: "var(--bg-inset)", color: "var(--text-secondary)", flexShrink: 0 }}>{node.tag}</span> : null}
|
|
168
|
-
{node.count != null ? <span style={{ marginLeft: "auto", fontFamily: "var(--font-
|
|
168
|
+
{node.count != null ? <span style={{ marginLeft: "auto", fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", color: "var(--text-muted)", paddingLeft: 8 }}>{node.count}</span> : null}
|
|
169
169
|
{cfg.rowActions && !disabled ? <span style={{ marginLeft: node.count != null ? 6 : "auto", display: "flex", gap: 2, opacity: hover ? 1 : 0, transition: "var(--t-hover)" }} onClick={(e) => e.stopPropagation()}>{cfg.rowActions(node)}</span> : null}
|
|
170
170
|
</div>
|
|
171
171
|
{open && loading && !kids.length ? (
|
|
@@ -9,6 +9,8 @@ const docHues = { orange: 'var(--cat-transform)', green: 'var(--cat-source)', pu
|
|
|
9
9
|
const INDENT = 18
|
|
10
10
|
const props = defineProps({
|
|
11
11
|
items: { type: Array, default: () => [] },
|
|
12
|
+
treeData: Array, // alias of items (same as Tree.jsx)
|
|
13
|
+
expandedKeys: Array, selectedKeys: Array, checkedKeys: Array, // controlled: the parent's value wins whenever it changes
|
|
12
14
|
defaultExpanded: { type: Array, default: () => [] },
|
|
13
15
|
defaultActive: String,
|
|
14
16
|
defaultSelected: { type: Array, default: () => [] },
|
|
@@ -24,12 +26,15 @@ const props = defineProps({
|
|
|
24
26
|
})
|
|
25
27
|
const emit = defineEmits(['select', 'check', 'expand', 'drop'])
|
|
26
28
|
|
|
27
|
-
const data = ref(JSON.parse(JSON.stringify(props.items)))
|
|
29
|
+
const data = ref(JSON.parse(JSON.stringify(props.treeData || props.items)))
|
|
28
30
|
// Compare by value: re-seeding on identity alone would wipe async-loaded children.
|
|
29
|
-
watch(() => JSON.stringify(props.items), (next, prev) => { if (next !== prev) data.value = JSON.parse(next) })
|
|
31
|
+
watch(() => JSON.stringify(props.treeData || props.items), (next, prev) => { if (next !== prev) data.value = JSON.parse(next) })
|
|
30
32
|
const expanded = ref([...props.defaultExpanded])
|
|
31
33
|
const selected = ref(props.defaultActive ? [props.defaultActive] : [...props.defaultSelected])
|
|
32
34
|
const checked = ref([...props.defaultChecked])
|
|
35
|
+
watch(() => props.expandedKeys, (v) => { if (v) expanded.value = [...v] }, { immediate: true })
|
|
36
|
+
watch(() => props.selectedKeys, (v) => { if (v) selected.value = [...v] }, { immediate: true })
|
|
37
|
+
watch(() => props.checkedKeys, (v) => { if (v) checked.value = [...v] }, { immediate: true })
|
|
33
38
|
const loading = ref([]); const loaded = ref([]); const focus = ref(null)
|
|
34
39
|
const dragKey = ref(null); const drop = ref(null)
|
|
35
40
|
|
|
@@ -245,14 +250,14 @@ const dropAt = k => (drop.value && drop.value.key === k ? drop.value.pos : null)
|
|
|
245
250
|
<template v-for="(p, i) in parts(titleOf(row.n))" :key="i"><mark v-if="p.hit" style="background:var(--soft-bg);color:var(--info-fg);font-weight:var(--weight-medium);padding:0;border-radius:2px">{{ p.t }}</mark><template v-else>{{ p.t }}</template></template>
|
|
246
251
|
</span>
|
|
247
252
|
<span v-if="row.n.tag" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:1;padding:2px 5px;border-radius:var(--radius-sm);background:var(--bg-inset);color:var(--text-secondary);flex-shrink:0">{{ row.n.tag }}</span>
|
|
248
|
-
<span v-if="row.n.count != null" style="margin-left:auto;font-family:var(--font-
|
|
253
|
+
<span v-if="row.n.count != null" style="margin-left:auto;font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-muted);padding-left:8px">{{ row.n.count }}</span>
|
|
249
254
|
</div>
|
|
250
255
|
<div v-if="!rows.length" style="padding:10px 8px;font-family:var(--font-sans);font-size:var(--text-base);color:var(--text-muted)">No matching items</div>
|
|
251
256
|
</div>
|
|
252
257
|
</template>
|
|
253
258
|
<style scoped>
|
|
254
259
|
.kb-tree-row:not(.is-disabled):hover{background:var(--bg-hover)}
|
|
255
|
-
.kb-tree-row:focus-visible{outline:2px solid var(--focus-
|
|
260
|
+
.kb-tree-row:focus-visible{outline:2px solid var(--focus-border);outline-offset:-2px}
|
|
256
261
|
.kb-tree-spin{animation:kb-tree-spin 900ms linear infinite}
|
|
257
262
|
@keyframes kb-tree-spin{to{transform:rotate(360deg)}}
|
|
258
263
|
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The product's real nav model (Home dashboard). Framework-neutral: imported by Sidebar.jsx (re-exported
|
|
2
|
+
// as KEYBRICKS_NAV from the React entry) and exported from the Vue entry for <Sidebar :items="KEYBRICKS_NAV">.
|
|
3
|
+
export const KEYBRICKS_NAV = [
|
|
4
|
+
{ icon: 'house', label: 'Home' },
|
|
5
|
+
{ icon: 'file-text', label: 'Documents' },
|
|
6
|
+
{ icon: 'layout-grid', label: 'Business Intelligence' },
|
|
7
|
+
{ icon: 'database', label: 'Data Integration' },
|
|
8
|
+
{ icon: 'reconciliation', label: 'Reconciliation' },
|
|
9
|
+
{ icon: 'consolidation', label: 'Consolidation' },
|
|
10
|
+
{
|
|
11
|
+
icon: 'folder-cog', label: 'Management', children: [
|
|
12
|
+
{ icon: 'box', label: 'Modeling' },
|
|
13
|
+
{ icon: 'workflow', label: 'Workflow' },
|
|
14
|
+
{ icon: 'shield', label: 'Security Management' },
|
|
15
|
+
{ icon: 'refresh-cw', label: 'System Migration' },
|
|
16
|
+
{ icon: 'chart-column', label: 'Business Logic' },
|
|
17
|
+
{ icon: 'list-todo', label: 'Tasks Management' },
|
|
18
|
+
{ icon: 'circle-check', label: 'Validation Rules' },
|
|
19
|
+
{ icon: 'settings-2', label: 'Control Center' },
|
|
20
|
+
{ icon: 'reconciliation', label: 'Reconciliation' },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Theme persistence shared by ThemeToggle (React and Vue). Framework-neutral.
|
|
2
|
+
// applyStoredTheme(): reads localStorage("kb-theme") and sets <html data-theme> before first paint —
|
|
3
|
+
// call once at app start (main.jsx / main.js).
|
|
4
|
+
export function applyStoredTheme() {
|
|
5
|
+
if (typeof document === 'undefined') return;
|
|
6
|
+
let t = 'light';
|
|
7
|
+
try { t = localStorage.getItem('kb-theme') || 'light'; } catch (e) {}
|
|
8
|
+
document.documentElement.dataset.theme = t;
|
|
9
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
// Text pill filter tabs. Active = tinted pill. Pills are control-height (sm
|
|
2
|
+
// Text pill filter tabs. Active = tinted pill. Pills are control-height (sm 28 · md 32 · lg 40); omit `size` to inherit.
|
|
3
3
|
defineProps({ tabs: { type: Array, default: () => [] }, active: String, size: String, style: Object })
|
|
4
4
|
const emit = defineEmits(['change'])
|
|
5
5
|
</script>
|
|
@@ -5,7 +5,7 @@ export interface SegmentedControlProps {
|
|
|
5
5
|
options?: (string | { label: string; icon?: string; disabled?: boolean })[];
|
|
6
6
|
value?: string;
|
|
7
7
|
onChange?: (value: string) => void;
|
|
8
|
-
/** Size tier: sm
|
|
8
|
+
/** Size tier: sm 28 · md 32 · lg 40. Omit to inherit the enclosing region's tier (the default). */
|
|
9
9
|
size?: "sm" | "md" | "lg";
|
|
10
10
|
/** Hide labels; each label becomes the segment's aria-label and tooltip */
|
|
11
11
|
iconOnly?: boolean;
|
|
@@ -4,7 +4,7 @@ import { Icon } from "../icons/Icon.jsx";
|
|
|
4
4
|
/**
|
|
5
5
|
* Segmented control — inset track, raised active segment. Switches views of the same data, applies instantly.
|
|
6
6
|
* options: string[] | { label, icon?, disabled? }[] value/onChange by label
|
|
7
|
-
* size sm
|
|
7
|
+
* size sm 28 | md 32 | lg 40 — omit it to inherit the region's tier · iconOnly hides labels (label becomes aria-label / title)
|
|
8
8
|
* disabled greys the whole control · fullWidth stretches segments evenly
|
|
9
9
|
* Keyboard: ← → move between enabled segments.
|
|
10
10
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import Icon from '../icons/Icon.vue'
|
|
4
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
|
|
5
|
+
// options: string[] | { label, icon?, disabled? }[] · v-model by label · size sm 28 | md 32 | lg 40 — omit it to inherit the region's tier
|
|
6
6
|
// iconOnly hides labels (label → aria-label/title) · disabled greys the control · fullWidth stretches segments · ← → keyboard.
|
|
7
7
|
const tierIcon = { width: 'var(--control-icon)', height: 'var(--control-icon)' }
|
|
8
8
|
const props = defineProps({ options: { type: Array, default: () => [] }, modelValue: String, size: String, iconOnly: Boolean, disabled: Boolean, fullWidth: Boolean, style: Object })
|
package/components/tabs/Tabs.jsx
CHANGED
|
@@ -34,7 +34,7 @@ export function PanelTabs({ tabs = [], active, onChange, size, style }) {
|
|
|
34
34
|
);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** Text pill filter tabs (For Review · To be Submitted · For Approval). Active = tinted pill. Pills are control-height (sm
|
|
37
|
+
/** Text pill filter tabs (For Review · To be Submitted · For Approval). Active = tinted pill. Pills are control-height (sm 28 · md 32 · lg 40); omit `size` to inherit. */
|
|
38
38
|
export function PillTabs({ tabs = [], active, onChange, size, style }) {
|
|
39
39
|
return (
|
|
40
40
|
<div role="tablist" data-size={size} style={{ display: "flex", gap: "var(--control-gap)", ...style }}>
|