@kolkrabbi/kol-component 0.29.0 → 0.30.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.29.0",
3
+ "version": "0.30.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",
@@ -24,7 +24,7 @@
24
24
  "@floating-ui/react": "^0.27.19",
25
25
  "embla-carousel-react": "^8.6.0",
26
26
  "react-syntax-highlighter": "^16.1.1",
27
- "@kolkrabbi/kol-icons": "0.10.0"
27
+ "@kolkrabbi/kol-icons": "0.11.1"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "framer-motion": "^12.0.0",
@@ -68,11 +68,15 @@ export function usePopover({
68
68
  middleware.push(
69
69
  sizeMw({
70
70
  apply({ rects, elements }) {
71
- /* EXACT width, not a floor (2026-08-09) "one piece means one
72
- * width": with minWidth alone, a wide row let the panel outgrow the
73
- * trigger it claims to continue. Sole consumer is Dropdown. */
71
+ /* FLOOR again, not exact (2026-08-09 evening, user: "if you have
72
+ * to truncate 'centered' then its too narrow" overrides the
73
+ * morning's exact-width reading of the one-piece ruling for the
74
+ * narrow case). The panel is never NARROWER than the trigger, and
75
+ * grows when its rows carry chrome the trigger doesn't (the
76
+ * selected row's check). Sole consumer is Dropdown. */
74
77
  Object.assign(elements.floating.style, {
75
- width: `${rects.reference.width}px`,
78
+ minWidth: `${rects.reference.width}px`,
79
+ width: '',
76
80
  })
77
81
  },
78
82
  })
@@ -61,12 +61,15 @@ export function StatusChip({ value, options = [], onChange, size = 'sm' }) {
61
61
  >
62
62
  {activeOpt?.label ?? value}
63
63
  {interactive && (
64
- <Icon
65
- name="chevron-down"
66
- size={INDICATOR[size] ?? INDICATOR.sm}
67
- className={`transition-transform duration-150${open ? ' rotate-180' : ''}`}
64
+ /* plain inline-flex wrapper — Icon's own element can render
65
+ * inline-block and ride the baseline (the misaligned caret,
66
+ * 2026-08-09); the flex span centres it against the label. */
67
+ <span
68
+ className={`inline-flex shrink-0 transition-transform duration-150${open ? ' rotate-180' : ''}`}
68
69
  aria-hidden="true"
69
- />
70
+ >
71
+ <Icon name="chevron-down" size={INDICATOR[size] ?? INDICATOR.sm} />
72
+ </span>
70
73
  )}
71
74
  </Tag>
72
75
  )
@@ -7,9 +7,31 @@ import ToggleCheckbox from '../atoms/ToggleCheckbox'
7
7
  import ShellDrawer from '../molecules/ShellDrawer'
8
8
  import Dropdown from '../molecules/Dropdown'
9
9
  import FieldRow, { StatusChip } from '../molecules/FieldRow'
10
+ import { Tooltip } from '../atoms/Popover'
10
11
  import Table from './Table'
11
12
  import MediaLibrary from './MediaLibrary'
12
13
 
14
+ /* TOOLBAR ICONS ARE BARE GLYPHS (user ruling 2026-08-09, verbatim: "did I
15
+ * ask for a button?? … the only thing that should hover is full opacity and
16
+ * popover"): no Button chrome, no fill, no box — body ink at rest, emphasis
17
+ * on hover, a Tooltip as the name. */
18
+ const ToolbarIcon = ({ name, label, onClick, active = false, triggerClassName }) => (
19
+ <Tooltip label={label} placement="bottom" triggerClassName={triggerClassName}>
20
+ <button
21
+ type="button"
22
+ aria-label={label}
23
+ aria-pressed={active}
24
+ onClick={onClick}
25
+ /* PRESSED gets the box (user ruling, reference frame 2026-08-09): a
26
+ * snug rounded fill hugging the glyph — "container relative to the
27
+ * icon, and only when pressed". Rest is bare; hover is ink only. */
28
+ className={`inline-flex cursor-pointer rounded-sm border-0 p-1 transition-colors duration-150 ${active ? 'bg-fg-08 text-emphasis' : 'bg-transparent text-body hover:text-emphasis'}`}
29
+ >
30
+ <Icon name={name} size={16} />
31
+ </button>
32
+ </Tooltip>
33
+ )
34
+
13
35
  /**
14
36
  * RecordManager — full-screen CMS record surface (lobby: RecordManager,
15
37
  * reference: Framer CMS). A reorderable record table plus a slide-over detail
@@ -61,9 +83,14 @@ export default function RecordManager({
61
83
  onOverflow,
62
84
  mediaClient,
63
85
  reorderLabel = (n) => `Reorder row ${n}`,
86
+ /* pressed states for the toolbar's sort/filter controls — the consumer owns
87
+ * those panels, so it tells the toolbar which control is open */
88
+ sortActive = false,
89
+ filterActive = false,
64
90
  className = '',
65
91
  }) {
66
92
  const wrapRef = useRef(null)
93
+ const [searchOpen, setSearchOpen] = useState(false)
67
94
  const dragRef = useRef(null)
68
95
  const [drag, setDrag] = useState(null) // { from, over, x, y } during a drag
69
96
  const [active, setActive] = useState(null)
@@ -233,26 +260,28 @@ export default function RecordManager({
233
260
  return (
234
261
  <div ref={wrapRef} className={`flex flex-col min-w-0 ${className}`}>
235
262
  {hasToolbar && (
236
- <div className="flex items-center gap-1 pb-3">
237
- {onAdd && <Button variant="nav" size="md" iconOnly="plus" iconSize={14} onClick={onAdd} aria-label="Add" />}
238
- {/* the reference cluster (user screenshots 2026-08-09): ↑↓ pair,
239
- * lines, and search as an ICON that expands into the field
240
- * (SearchInput's expanding mode) not an always-open input. */}
241
- {onSortToggle && <Button variant="nav" size="md" iconOnly="arrows-vertical" iconSize={14} onClick={onSortToggle} aria-label="Sort" />}
242
- {onFilterToggle && <Button variant="nav" size="md" iconOnly="filter-lines" iconSize={14} onClick={onFilterToggle} aria-label="Filter" />}
243
- {onQueryChange && (
263
+ /* the reference cluster (user screenshots 2026-08-09): bare + · ↑↓ ·
264
+ * · 🔍, search expanding into a COMPACT field on press, … far right */
265
+ <div className="flex items-center gap-2 pb-3">
266
+ {onAdd && <ToolbarIcon name="plus" label="Add" onClick={onAdd} />}
267
+ {onSortToggle && <ToolbarIcon name="arrows-vertical" label="Sort" active={sortActive} onClick={onSortToggle} />}
268
+ {onFilterToggle && <ToolbarIcon name="filter-lines" label="Filter" active={filterActive} onClick={onFilterToggle} />}
269
+ {onQueryChange && (searchOpen || query ? (
244
270
  <SearchInput
245
- expanding
271
+ autoFocus
272
+ size="sm"
273
+ className="w-56"
246
274
  value={query ?? ''}
247
275
  onChange={(e) => onQueryChange(e?.target ? e.target.value : e)}
248
276
  placeholder={searchPlaceholder}
249
- size="sm"
277
+ onClear={() => { onQueryChange(''); setSearchOpen(false) }}
278
+ onBlur={() => { if (!query) setSearchOpen(false) }}
250
279
  />
251
- )}
252
- {/* trailing overflow the reference toolbar's far-right … (surface
253
- * scope: called with null, vs the drawer header's per-record call) */}
280
+ ) : (
281
+ <ToolbarIcon name="search" label="Search" onClick={() => setSearchOpen(true)} />
282
+ ))}
254
283
  {onOverflow && (
255
- <Button variant="nav" size="md" iconOnly="more" iconSize={14} className="ml-auto" onClick={() => onOverflow(null)} aria-label="More" />
284
+ <ToolbarIcon name="more" label="More" triggerClassName="ml-auto" onClick={() => onOverflow(null)} />
256
285
  )}
257
286
  </div>
258
287
  )}
@@ -289,10 +318,10 @@ export default function RecordManager({
289
318
  header={
290
319
  <div className="flex items-center gap-2 min-w-0">
291
320
  {onOverflow && (
292
- <Button variant="nav" size="md" iconOnly="more" iconSize={14} onClick={() => onOverflow(record)} aria-label="More" />
321
+ <ToolbarIcon name="more" label="More" onClick={() => onOverflow(record)} />
293
322
  )}
294
323
  {onPreview && (
295
- <Button variant="nav" size="md" iconOnly="play" iconSize={14} onClick={() => onPreview(record)} aria-label="Preview" />
324
+ <ToolbarIcon name="play" label="Preview" onClick={() => onPreview(record)} />
296
325
  )}
297
326
  {saveState != null && <span className="kol-helper-10 text-meta ml-auto truncate">{saveState}</span>}
298
327
  {onPublish && (