@kolkrabbi/kol-component 0.234.0 → 0.236.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
|
@@ -135,9 +135,20 @@ const readBack = (n) => {
|
|
|
135
135
|
*
|
|
136
136
|
* STATE is untouched: collapsed/expanded still persists via
|
|
137
137
|
* `stateKey`, unconditionally, as it always did. The ticket says the
|
|
138
|
-
* two are separate questions and only asks about the width.
|
|
138
|
+
* two are separate questions and only asks about the width.
|
|
139
|
+
* variant — how the handle DRAWS and what a click does (2026-10-01, user: *"we should make another
|
|
140
|
+
* drag to resize variant: on hover this pointer, and either hover or click to highlight
|
|
141
|
+
* the border, with double click expanding (like if sidebar with icons)"*):
|
|
142
|
+
* 'pill' (default) — the short pill that wakes as the pointer nears; a CLICK toggles.
|
|
143
|
+
* 'line' — the whole edge lights up on hover and while dragging; a click does
|
|
144
|
+
* nothing and a DOUBLE-CLICK toggles expand ↔ collapse (the two
|
|
145
|
+
* cannot share one gesture — see "double-click reset is gone" above,
|
|
146
|
+
* which is why this is a variant and not an addition). */
|
|
139
147
|
export default function useDragResize(ref, options = {}) {
|
|
140
|
-
const { token = 'kol-sidenav', side = 'left', defaultCollapsed = false, persistWidth = false } = options
|
|
148
|
+
const { token = 'kol-sidenav', side = 'left', defaultCollapsed = false, persistWidth = false, variant = 'pill' } = options
|
|
149
|
+
const line = variant === 'line'
|
|
150
|
+
const lineRef = useRef(line)
|
|
151
|
+
lineRef.current = line
|
|
141
152
|
/* -1 on a right-hand rail: the same rightward pointer travel that widens a
|
|
142
153
|
* left rail must NARROW a right one, because its handle faces the canvas. */
|
|
143
154
|
const dir = side === 'right' ? -1 : 1
|
|
@@ -221,7 +232,7 @@ export default function useDragResize(ref, options = {}) {
|
|
|
221
232
|
root().removeAttribute(names.draggingAttr)
|
|
222
233
|
document.body.style.cursor = ''
|
|
223
234
|
document.body.style.userSelect = ''
|
|
224
|
-
if (!moved) { toggleCollapsed(); return } // a click, not a drag
|
|
235
|
+
if (!moved) { if (!lineRef.current) toggleCollapsed(); return } // a click, not a drag — `line` toggles on double-click
|
|
225
236
|
/* Snap-to-default: release near the stylesheet default clears the
|
|
226
237
|
* override entirely. */
|
|
227
238
|
const { collapsed: c, widthPx: w } = readBack(names)
|
|
@@ -304,7 +315,8 @@ export default function useDragResize(ref, options = {}) {
|
|
|
304
315
|
/* the pill is drawn by `.kol-rail-grab` (kol-animation.css) — the hook
|
|
305
316
|
* only supplies `is-near` and `--kol-rail-grab-y`. A consumer's own
|
|
306
317
|
* className, spread after this, still wins. */
|
|
307
|
-
className: 'kol-rail-grab',
|
|
318
|
+
className: line ? 'kol-rail-grab kol-rail-grab--line' : 'kol-rail-grab',
|
|
319
|
+
...(line ? { onDoubleClick: toggleCollapsed } : {}),
|
|
308
320
|
role: 'separator',
|
|
309
321
|
'aria-orientation': 'vertical',
|
|
310
322
|
'aria-label': 'Resize navigation',
|
package/src/index.js
CHANGED
|
@@ -128,6 +128,8 @@ export { default as ToolPalette } from './organisms/ToolPalette.jsx'
|
|
|
128
128
|
export { default as DocumentEditor } from './organisms/DocumentEditor.jsx'
|
|
129
129
|
export { default as SwatchControls, SwatchStack, EyedropPick } from './molecules/SwatchControls.jsx'
|
|
130
130
|
export { default as TabsRow } from './molecules/TabsRow.jsx'
|
|
131
|
+
/* TabChips — tabs as chips, for inside a toolbar (2026-10-01; was the showcase's local DocTabs) */
|
|
132
|
+
export { default as TabChips } from './molecules/TabChips.jsx'
|
|
131
133
|
|
|
132
134
|
// organisms
|
|
133
135
|
/* monorepo sets (P6–P10) — organism members. Foundry members live in the
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TabChips — Tabs as a row of chips. the active tab is a filled chip, the rest are quiet text.
|
|
3
|
+
*
|
|
4
|
+
* WHY IT EXISTS (user, 2026-10-01, on the showcase's Preview / Code and pnpm / npm / yarn / bun rows:
|
|
5
|
+
* *"what is this button? its not DS?"* → *"we can just make a tab chip for this purpose exactly
|
|
6
|
+
* like this"*). It was `DocTabs`, a strip local to the showcase, built there because neither
|
|
7
|
+
* shipped tab control fits a toolbar: `SegmentedToggle` is a joined strip with radiogroup
|
|
8
|
+
* semantics, `TabsRow` is an underline strip that wants a rule beneath it. This is the third tab
|
|
9
|
+
* idiom — small, unframed, sits inside a bar — promoted as it was, class for class.
|
|
10
|
+
*
|
|
11
|
+
* Labels render as authored. Selection is the parent's.
|
|
12
|
+
*
|
|
13
|
+
* @param {Array} tabs `[{ id, label }]`
|
|
14
|
+
* @param {string} value id of the active tab (controlled)
|
|
15
|
+
* @param {Function} onChange `(id) => void`
|
|
16
|
+
* @param {string} [ariaLabel] accessible name for the strip
|
|
17
|
+
* @param {string} [className] extra classes on the row
|
|
18
|
+
*/
|
|
19
|
+
export default function TabChips({ tabs = [], value, onChange, ariaLabel, className = '' }) {
|
|
20
|
+
return (
|
|
21
|
+
<div className={`flex items-center gap-1 ${className}`.trim()} role="tablist" aria-label={ariaLabel}>
|
|
22
|
+
{tabs.map((t) => {
|
|
23
|
+
const active = t.id === value
|
|
24
|
+
return (
|
|
25
|
+
<button
|
|
26
|
+
key={t.id}
|
|
27
|
+
type="button"
|
|
28
|
+
role="tab"
|
|
29
|
+
aria-selected={active}
|
|
30
|
+
onClick={() => onChange?.(t.id)}
|
|
31
|
+
className={`kol-mono-12 rounded-[var(--kol-radius-sm)] px-3 py-1 transition-colors [@media(pointer:coarse)]:min-h-8 ${active ? 'bg-fg-08 text-emphasis' : 'text-meta hover:text-emphasis'}`}
|
|
32
|
+
>
|
|
33
|
+
{t.label}
|
|
34
|
+
</button>
|
|
35
|
+
)
|
|
36
|
+
})}
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -69,7 +69,7 @@ export function HighlightMatch({ label, query, ranges }) {
|
|
|
69
69
|
* The ⌘K binding itself lives in the shell's key handler, not here.
|
|
70
70
|
*
|
|
71
71
|
* Keyboard: ArrowUp/ArrowDown rove the active row (mouse hover roves too),
|
|
72
|
-
* Enter
|
|
72
|
+
* Enter opens it (the top row from the first keystroke), ⌘/Ctrl+Enter opens every hit,
|
|
73
73
|
* Escape closes. Focus trap: focus moves into the input on open, returns to
|
|
74
74
|
* the opener on close, and Tab is pinned — rows are combobox options driven
|
|
75
75
|
* via aria-activedescendant, never tab stops.
|
|
@@ -168,13 +168,12 @@ export default function ShellSearchOverlay({
|
|
|
168
168
|
/* ⌘ENTER OPENS EVERY HIT (user 2026-09-30: *"enter would focus searched item, and maybe
|
|
169
169
|
* command enter would open the results page"*) — only where the consumer has a place for them. */
|
|
170
170
|
if ((e.metaKey || e.ctrlKey) && onOpenResults && query) { onOpenResults(query); onClose?.(); return }
|
|
171
|
-
/*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
|
|
177
|
-
if (active >= 0 && (navigated || !query)) select(results[active])
|
|
171
|
+
/* ENTER OPENS THE HIGHLIGHTED ROW (user 2026-10-01: *"I think enter should also take you to
|
|
172
|
+
* the atom but something like command enter take you to the index"* — reverses the
|
|
173
|
+
* 2026-08-01 "Enter commits, it only selects once you have arrowed"). The top row is drawn
|
|
174
|
+
* highlighted from the first keystroke, so Enter goes where the highlight says. With no
|
|
175
|
+
* rows there is nothing to open and Enter commits the query. */
|
|
176
|
+
if (active >= 0) select(results[active])
|
|
178
177
|
else onExpand?.()
|
|
179
178
|
} else if (e.key === 'Tab') {
|
|
180
179
|
/* Focus trap — the input is the palette's only tab stop. */
|
|
@@ -283,17 +282,28 @@ export default function ShellSearchOverlay({
|
|
|
283
282
|
</ul>
|
|
284
283
|
)}
|
|
285
284
|
{/* THE FOOTER SAYS WHAT ENTER DOES — always, not only once typing. */}
|
|
286
|
-
{!expanded && (
|
|
285
|
+
{!expanded && (results.length > 0 || (query && (enterLabel || onOpenResults))) && (
|
|
287
286
|
<div className="border-t border-fg-08 py-2">
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
287
|
+
{/* Enter's line names what Enter does NOW: a row is highlighted → it opens that row;
|
|
288
|
+
* no rows → it commits the query. */}
|
|
289
|
+
{(results.length > 0 || enterLabel) && (
|
|
290
|
+
<p className="flex items-center gap-2 kol-helper-12 text-fg-48 mx-2 px-4 border-x border-transparent">
|
|
291
|
+
<Kbd icon="corner-down-left" />
|
|
292
|
+
{results.length > 0 ? 'Go to page' : enterLabel}
|
|
293
|
+
</p>
|
|
294
|
+
)}
|
|
295
|
+
{/* THE RESULTS LINE IS A LINK (user 2026-10-01: *"make 'all results...' at the bottom
|
|
296
|
+
* also be a link to the results page"*) — the same door ⌘Enter opens. */}
|
|
292
297
|
{query && onOpenResults && (
|
|
293
|
-
<
|
|
298
|
+
<button
|
|
299
|
+
type="button"
|
|
300
|
+
className="flex items-center gap-2 kol-helper-12 text-fg-48 hover:text-fg-default transition-colors mx-2 px-4 border-x border-transparent pt-1"
|
|
301
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
302
|
+
onClick={() => { onOpenResults(query); onClose?.() }}
|
|
303
|
+
>
|
|
294
304
|
<Kbd icon="command"><Icon name="corner-down-left" size={12} /></Kbd>
|
|
295
305
|
{resultsLabel}
|
|
296
|
-
</
|
|
306
|
+
</button>
|
|
297
307
|
)}
|
|
298
308
|
</div>
|
|
299
309
|
)}
|
package/src/organisms/Table.jsx
CHANGED
|
@@ -33,7 +33,7 @@ const WIDTHS = {
|
|
|
33
33
|
column: '',
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const Table = ({ caption, columns, rows, variant = 'default', className = '', width = 'panel', rowClassName, compact = false }) => {
|
|
36
|
+
const Table = ({ caption, columns, rows, variant = 'default', className = '', width = 'panel', rowClassName, compact = false, lastRule = false }) => {
|
|
37
37
|
const [sort, setSort] = useState({ key: null, dir: null })
|
|
38
38
|
|
|
39
39
|
const cycle = (key) =>
|
|
@@ -58,7 +58,9 @@ const Table = ({ caption, columns, rows, variant = 'default', className = '', wi
|
|
|
58
58
|
const variantClass = variant === 'simple' ? 'kol-table--simple' : ''
|
|
59
59
|
/* compact — record-surface density (RecordManager, 2026-08-09): the default
|
|
60
60
|
* 12/16 cell box read as bloated rows against the reference table. */
|
|
61
|
-
|
|
61
|
+
/* lastRule — `simple` only: the rule under the LAST row. Off by default (2026-10-01): the
|
|
62
|
+
* table is usually followed by a divider of its own, and the two read as a double line. */
|
|
63
|
+
const wrapperClass = ['kol-table-wrapper', variantClass, lastRule && 'kol-table--last-rule', compact && 'kol-table--compact', WIDTHS[width] ?? WIDTHS.panel, className].filter(Boolean).join(' ')
|
|
62
64
|
return (
|
|
63
65
|
<div className={wrapperClass}>
|
|
64
66
|
<table className="kol-table">
|