@kolkrabbi/kol-component 0.31.0 → 0.32.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.31.0",
3
+ "version": "0.32.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.12.0"
27
+ "@kolkrabbi/kol-icons": "0.12.1"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "framer-motion": "^12.0.0",
@@ -74,9 +74,14 @@ export function usePopover({
74
74
  * narrow case). The panel is never NARROWER than the trigger, and
75
75
  * grows when its rows carry chrome the trigger doesn't (the
76
76
  * selected row's check). Sole consumer is Dropdown. */
77
+ /* width: max-content is LOAD-BEARING — an abs-positioned float
78
+ * with auto width shrink-to-fits its available space, and the
79
+ * w-full rows inside then blew the panel to ~2× (the broken
80
+ * dropdown, 2026-08-09 evening). max-content sizes it to the
81
+ * widest row; minWidth keeps the trigger as the floor. */
77
82
  Object.assign(elements.floating.style, {
78
83
  minWidth: `${rects.reference.width}px`,
79
- width: '',
84
+ width: 'max-content',
80
85
  })
81
86
  },
82
87
  })
@@ -50,28 +50,29 @@ export function StatusChip({ value, options = [], onChange, size = 'sm' }) {
50
50
  const norm = options.map((opt) => (typeof opt === 'object' ? opt : { value: opt, label: opt }))
51
51
  const activeOpt = norm.find((o) => o.value === value)
52
52
  const toneCls = activeOpt?.tone ? ` kol-status-chip--${activeOpt.tone}` : ''
53
+ /* BUILT ON THE COVER-FOCUS SPECS (user ruling 2026-08-09: "same component
54
+ * as cover focus"): the chip wears the Dropdown-sm trigger metrics — the
55
+ * kol-btn-sm box (py-1 px-3) and mono-12 type — as a pill with the tone
56
+ * tint. The caret is a direct child of the flex row, centred by
57
+ * items-center; it cannot sit off the label's middle. */
53
58
  const chip = (interactive) => (
54
- <Tag
55
- variant="primary"
56
- size={size}
57
- hash={false}
58
- active={interactive && open}
59
- className={`kol-status-chip${toneCls}`}
59
+ <button
60
+ type="button"
61
+ disabled={!interactive || undefined}
62
+ aria-expanded={interactive ? open : undefined}
63
+ className={`kol-status-chip${toneCls} kol-mono-12 inline-flex items-center gap-1 rounded-full border-0 px-3 py-1 leading-none${interactive ? ' cursor-pointer' : ''}`}
60
64
  onClick={interactive ? () => setOpen((o) => !o) : undefined}
61
65
  >
62
66
  {activeOpt?.label ?? value}
63
67
  {interactive && (
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
68
  <span
68
69
  className={`inline-flex shrink-0 transition-transform duration-150${open ? ' rotate-180' : ''}`}
69
70
  aria-hidden="true"
70
71
  >
71
- <Icon name="chevron-down" size={INDICATOR[size] ?? INDICATOR.sm} />
72
+ <Icon name="chevron-down" size={INDICATOR.sm} />
72
73
  </span>
73
74
  )}
74
- </Tag>
75
+ </button>
75
76
  )
76
77
  return (
77
78
  <>
@@ -113,9 +114,9 @@ export function StatusChip({ value, options = [], onChange, size = 'sm' }) {
113
114
  )
114
115
  }
115
116
 
116
- /* Media/file thumbnail — same small fixed-aspect rounded treatment as
117
- * MediaRow's thumb cell. */
118
- const THUMB_CLS = 'h-10 w-14 rounded object-cover bg-fg-04'
117
+ /* Media thumbnail — the reference panel's proportions (frame 39,
118
+ * 2026-08-09): a generous rounded tile, remove × inset at its corner. */
119
+ const THUMB_CLS = 'h-16 w-28 rounded-lg object-cover bg-fg-04'
119
120
 
120
121
  export default function FieldRow({
121
122
  label,
@@ -138,9 +139,9 @@ export default function FieldRow({
138
139
  aria-label={label}
139
140
  disabled={disabled}
140
141
  onClick={onPick}
141
- className="flex h-14 w-20 cursor-pointer items-center justify-center rounded border border-fg-16 bg-fg-02 text-body transition-colors duration-150 hover:text-emphasis"
142
+ className="flex h-16 w-28 cursor-pointer items-center justify-center rounded-lg border border-fg-16 bg-fg-02 text-body transition-colors duration-150 hover:text-emphasis"
142
143
  >
143
- <Icon name="plus" size={14} />
144
+ <Icon name="plus" size={16} />
144
145
  </button>
145
146
  ) : dash
146
147
  const fileEmpty = onPick ? (
@@ -184,7 +185,7 @@ export default function FieldRow({
184
185
  iconOnly="x"
185
186
  iconSize={10}
186
187
  aria-label={label}
187
- className="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 focus-visible:opacity-100"
188
+ className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 focus-visible:opacity-100"
188
189
  onClick={() => onChange(null)}
189
190
  />
190
191
  )}
@@ -25,9 +25,11 @@ const ToolbarIcon = ({ name, label, onClick, active = false, triggerClassName })
25
25
  /* PRESSED gets the box (user ruling, reference frame 2026-08-09): a
26
26
  * snug rounded fill hugging the glyph — "container relative to the
27
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'}`}
28
+ /* 20px glyph, half-step pad "TOO SMALL, TOO MUCH PADDING" (user
29
+ * frames 2026-08-09): the reference icons nearly fill their box. */
30
+ className={`inline-flex cursor-pointer rounded-sm border-0 p-0.5 transition-colors duration-150 ${active ? 'bg-fg-08 text-emphasis' : 'bg-transparent text-body hover:text-emphasis'}`}
29
31
  >
30
- <Icon name={name} size={16} />
32
+ <Icon name={name} size={20} />
31
33
  </button>
32
34
  </Tooltip>
33
35
  )
@@ -155,10 +157,10 @@ export default function RecordManager({
155
157
  <button
156
158
  type="button"
157
159
  aria-label="Toggle overlay"
158
- className="shrink-0 inline-flex cursor-pointer rounded-sm border border-fg-16 bg-transparent p-1 text-body opacity-0 transition-opacity duration-150 group-hover:opacity-100 focus-visible:opacity-100 hover:text-emphasis"
160
+ className="shrink-0 inline-flex cursor-pointer rounded-md border border-fg-16 bg-transparent p-1 text-body opacity-0 transition-opacity duration-150 group-hover:opacity-100 focus-visible:opacity-100 hover:text-emphasis"
159
161
  onClick={() => openRecord(row)}
160
162
  >
161
- <Icon name="toggle-overlay" size={14} />
163
+ <Icon name="toggle-overlay" size={18} />
162
164
  </button>
163
165
  </span>
164
166
  ),
@@ -289,6 +291,7 @@ export default function RecordManager({
289
291
  columns={builtColumns}
290
292
  rows={rows}
291
293
  width="column"
294
+ compact
292
295
  rowClassName={(row, i) =>
293
296
  `group${drag && i === drag.from ? ' opacity-40' : ''}${
294
297
  drag && i === drag.over && drag.over !== drag.from ? ' bg-fg-04' : ''
@@ -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 }) => {
36
+ const Table = ({ caption, columns, rows, variant = 'default', className = '', width = 'panel', rowClassName, compact = false }) => {
37
37
  const [sort, setSort] = useState({ key: null, dir: null })
38
38
 
39
39
  const cycle = (key) =>
@@ -56,7 +56,9 @@ const Table = ({ caption, columns, rows, variant = 'default', className = '', wi
56
56
  }, [rows, columns, sort])
57
57
 
58
58
  const variantClass = variant === 'simple' ? 'kol-table--simple' : ''
59
- const wrapperClass = ['kol-table-wrapper', variantClass, WIDTHS[width] ?? WIDTHS.panel, className].filter(Boolean).join(' ')
59
+ /* compact record-surface density (RecordManager, 2026-08-09): the default
60
+ * 12/16 cell box read as bloated rows against the reference table. */
61
+ const wrapperClass = ['kol-table-wrapper', variantClass, compact && 'kol-table--compact', WIDTHS[width] ?? WIDTHS.panel, className].filter(Boolean).join(' ')
60
62
  return (
61
63
  <div className={wrapperClass}>
62
64
  <table className="kol-table">