@kolkrabbi/kol-component 0.221.0 → 0.222.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.222.0",
|
|
4
4
|
"description": "KOL design-system components — atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/index.js
CHANGED
|
@@ -120,6 +120,7 @@ export { default as Slider } from './molecules/Slider.jsx'
|
|
|
120
120
|
export { default as SpecList } from './molecules/SpecList.jsx'
|
|
121
121
|
export { default as SpectrumControls, HueStrip, SBSquare, WheelTriangle } from './organisms/SpectrumControls.jsx'
|
|
122
122
|
export { default as SplitToolButton } from './molecules/SplitToolButton.jsx'
|
|
123
|
+
export { default as ToolPalette } from './organisms/ToolPalette.jsx'
|
|
123
124
|
export { default as SwatchControls, SwatchStack, EyedropPick } from './molecules/SwatchControls.jsx'
|
|
124
125
|
export { default as TabsRow } from './molecules/TabsRow.jsx'
|
|
125
126
|
|
|
@@ -54,7 +54,14 @@ import { glyphSize } from '../hooks/glyphLadders.js'
|
|
|
54
54
|
* @param {boolean} props.active - Whether this tool group is the active tool — lit trigger, `aria-pressed`
|
|
55
55
|
* @param {'xs'|'sm'|'md'|'lg'} props.size - Trigger box on the pinned-square ladder — 22 · 26 · 32 · 40 (default: 'md'). The glyph follows the SOLO ladder; it is never set at the call site
|
|
56
56
|
* @param {boolean} props.blurOnClick - Blur the trigger after click so a canvas can reclaim focus and refresh its cursor (default: false)
|
|
57
|
+
* @param {Function} props.onTrigger - Replaces the trigger's arm: fires with the trigger variant's id on every click. For a fold of one-shot ACTIONS (Boolean: the trigger re-runs the last-picked op) — no pressed state is implied (default: arm via `onChange`)
|
|
58
|
+
* @param {boolean} props.disabled - Disables the trigger — `.kol-btn:disabled` dims it (default: false)
|
|
59
|
+
* @param {ElementType} props.iconComponent - Glyph renderer receiving `{ name, size, className, style }` — Button's seam (default: DS `Icon`)
|
|
57
60
|
* @param {string} props.aria-label - Trigger label fallback when no variant resolves
|
|
61
|
+
*
|
|
62
|
+
* A variant may carry its own `onSelect()` — that row does its own thing instead of `onChange`
|
|
63
|
+
* (Text: "Text" arms the tool, "Kinetic type" inserts a layer). Without `lastPicked` the trigger
|
|
64
|
+
* remembers the last row picked from the menu (kol-fxr ToolPalette, editor-panels-the-held-specs A3).
|
|
58
65
|
* @param {string} props.className - Additional classes on the trigger
|
|
59
66
|
*/
|
|
60
67
|
|
|
@@ -77,6 +84,9 @@ const SplitToolButton = ({
|
|
|
77
84
|
active = false,
|
|
78
85
|
size = 'md',
|
|
79
86
|
blurOnClick = false,
|
|
87
|
+
onTrigger,
|
|
88
|
+
disabled = false,
|
|
89
|
+
iconComponent: IconC = Icon,
|
|
80
90
|
className = '',
|
|
81
91
|
'aria-label': ariaLabel,
|
|
82
92
|
}) => {
|
|
@@ -89,20 +99,23 @@ const SplitToolButton = ({
|
|
|
89
99
|
role: 'menu',
|
|
90
100
|
})
|
|
91
101
|
|
|
102
|
+
const [ownLast, setOwnLast] = useState(null)
|
|
92
103
|
const byId = (id) => variants.find((v) => v.id === id)
|
|
93
|
-
const triggerVariant = (active && byId(value)) || byId(lastPicked) || variants[0]
|
|
104
|
+
const triggerVariant = (active && byId(value)) || byId(lastPicked ?? ownLast) || variants[0]
|
|
94
105
|
const label = triggerVariant?.label ?? ariaLabel
|
|
95
106
|
const title = triggerVariant?.shortcut ? `${label} (${triggerVariant.shortcut})` : label
|
|
96
107
|
|
|
97
108
|
/* Arm the last-picked variant on the same click that toggles the menu —
|
|
98
109
|
* floating-ui's useClick (on the reference) handles the open/close half. */
|
|
99
110
|
const handleTriggerClick = (event) => {
|
|
100
|
-
if (
|
|
111
|
+
if (onTrigger) { if (triggerVariant) onTrigger(triggerVariant.id) }
|
|
112
|
+
else if (!active && triggerVariant) onChange?.(triggerVariant.id)
|
|
101
113
|
if (blurOnClick) event.currentTarget.blur()
|
|
102
114
|
}
|
|
103
115
|
|
|
104
116
|
const handleSelect = (variant) => {
|
|
105
|
-
|
|
117
|
+
if (variant.onSelect) variant.onSelect()
|
|
118
|
+
else { onChange?.(variant.id); setOwnLast(variant.id) }
|
|
106
119
|
setOpen(false)
|
|
107
120
|
}
|
|
108
121
|
|
|
@@ -113,11 +126,12 @@ const SplitToolButton = ({
|
|
|
113
126
|
{...popover.getReferenceProps({ onClick: handleTriggerClick })}
|
|
114
127
|
type="button"
|
|
115
128
|
className={`relative kol-btn kol-btn-ghost kol-btn-icon kol-btn-${size} ${active ? 'kol-btn-pressed' : 'kol-btn-quiet'} ${className}`.trim()}
|
|
116
|
-
|
|
129
|
+
disabled={disabled}
|
|
130
|
+
aria-pressed={onTrigger ? undefined : active}
|
|
117
131
|
aria-label={label}
|
|
118
132
|
title={title}
|
|
119
133
|
>
|
|
120
|
-
{triggerVariant && <
|
|
134
|
+
{triggerVariant && <IconC name={triggerVariant.icon} size={glyphSize(size, true)} />}
|
|
121
135
|
<FoldIndicator />
|
|
122
136
|
</button>
|
|
123
137
|
{/* w-max — floats size to content, the menu-family law (2026-08-09). */}
|
|
@@ -137,7 +151,7 @@ const SplitToolButton = ({
|
|
|
137
151
|
className="w-full kol-helper-12 px-3 h-8 inline-flex items-center gap-2 text-body hover:text-emphasis text-left"
|
|
138
152
|
>
|
|
139
153
|
<span className="shrink-0 w-4 inline-flex items-center justify-center">
|
|
140
|
-
<
|
|
154
|
+
<IconC name={variant.icon} size={14} />
|
|
141
155
|
</span>
|
|
142
156
|
<span className="flex-1 truncate">{variant.label}</span>
|
|
143
157
|
<span className="kol-helper-10 text-emphasis shrink-0">
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
|
+
import Button from '../atoms/Button.jsx'
|
|
3
|
+
import Divider from '../atoms/Divider.jsx'
|
|
4
|
+
import SplitToolButton from '../molecules/SplitToolButton.jsx'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ToolPalette — the editor's tool bar as one row (kol-fxr `shell/panels/ToolPalette.jsx`,
|
|
8
|
+
* editor-panels-the-held-specs A3, 2026-09-25). Four kinds of cell, all on one pinned-square rung:
|
|
9
|
+
*
|
|
10
|
+
* - `tool` — arms a mode; lit while `activeId` is its id
|
|
11
|
+
* - `action` — a one-shot operation (flip, rotate, duplicate)
|
|
12
|
+
* - `split` — a fold of variants: `SplitToolButton`. A fold of TOOLS arms (Shape); a fold with
|
|
13
|
+
* `action: true` runs its variants instead, the trigger re-running the last-picked one
|
|
14
|
+
* (Boolean); a variant with `action: true` inside a tool fold is a one-shot row (Text →
|
|
15
|
+
* Kinetic type)
|
|
16
|
+
* - `divider` — a hairline between groups
|
|
17
|
+
*
|
|
18
|
+
* The store is the consumer's (the source read `useTool` + `useComposeState` directly): what
|
|
19
|
+
* is armed, what each action does and **when an item is disabled** — needs a selection, needs
|
|
20
|
+
* two booleanable layers — all arrive in `items` and are never inferred here.
|
|
21
|
+
*
|
|
22
|
+
* `min-w-0 overflow-x-auto`: at a narrow width the row scrolls in its own box instead of
|
|
23
|
+
* painting over the rail beside it, as the source learned.
|
|
24
|
+
*
|
|
25
|
+
* @param {Object} props
|
|
26
|
+
* @param {Array<{kind: 'tool'|'action'|'split'|'divider', id?: string, icon?: string, label?: string, shortcut?: string, disabled?: boolean, action?: boolean, variants?: Array<{id: string, label: string, icon: string, shortcut?: string, action?: boolean}>}>} props.items - The row, left to right
|
|
27
|
+
* @param {string} props.activeId - The armed tool's id — a `tool` item's, or any variant's of a tool fold
|
|
28
|
+
* @param {Function} props.onSelect - (id) => void — a tool (or a tool variant) was armed
|
|
29
|
+
* @param {Function} props.onAction - (id) => void — an action (or an action variant) was run
|
|
30
|
+
* @param {'xs'|'sm'|'md'|'lg'} props.size - The rung — 22 · 26 · 32 · 40 (default: 'md', the source's 32)
|
|
31
|
+
* @param {ElementType} props.iconComponent - Glyph renderer receiving `{ name, size, className, style }` (default: DS `Icon`)
|
|
32
|
+
* @param {string} props.className - Additional classes on the row
|
|
33
|
+
*/
|
|
34
|
+
const blur = (e) => e.currentTarget.blur()
|
|
35
|
+
|
|
36
|
+
const ToolPalette = ({
|
|
37
|
+
items = [],
|
|
38
|
+
activeId,
|
|
39
|
+
onSelect,
|
|
40
|
+
onAction,
|
|
41
|
+
size = 'md',
|
|
42
|
+
iconComponent = Icon,
|
|
43
|
+
className = '',
|
|
44
|
+
}) => (
|
|
45
|
+
<div className={`flex items-center gap-1 min-w-0 overflow-x-auto ${className}`.trim()}>
|
|
46
|
+
{items.map((it, i) => {
|
|
47
|
+
if (it.kind === 'divider') return <Divider key={`d${i}`} variant="vertical" height={20} className="mx-1.5 shrink-0" />
|
|
48
|
+
const tip = it.shortcut ? `${it.label} (${it.shortcut})` : it.label
|
|
49
|
+
if (it.kind === 'split') {
|
|
50
|
+
const variants = (it.variants ?? []).map((v) => (v.action && !it.action ? { ...v, onSelect: () => onAction?.(v.id) } : v))
|
|
51
|
+
const run = (id) => (it.action ? onAction : onSelect)?.(id)
|
|
52
|
+
return (
|
|
53
|
+
<SplitToolButton key={it.id} variants={variants} value={activeId} size={size} iconComponent={iconComponent}
|
|
54
|
+
active={!it.action && variants.some((v) => v.id === activeId && !v.action)}
|
|
55
|
+
onChange={run} onTrigger={it.action ? run : undefined}
|
|
56
|
+
disabled={it.disabled} blurOnClick aria-label={it.label} />
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
const isTool = it.kind === 'tool'
|
|
60
|
+
return (
|
|
61
|
+
<Button key={it.id} variant="ghost" size={size} quiet iconOnly={it.icon} iconComponent={iconComponent}
|
|
62
|
+
pressed={isTool ? activeId === it.id : undefined} disabled={it.disabled}
|
|
63
|
+
aria-label={it.label} title={tip}
|
|
64
|
+
onClick={(e) => { (isTool ? onSelect : onAction)?.(it.id); blur(e) }} />
|
|
65
|
+
)
|
|
66
|
+
})}
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
export default ToolPalette
|