@kolkrabbi/kol-component 0.101.0 → 0.102.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-component",
3
- "version": "0.101.0",
3
+ "version": "0.102.1",
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
@@ -137,7 +137,7 @@ export { default as LoaderOverlay } from './utilities/LoaderOverlay.jsx'
137
137
  export { default as MediaLibrary, MediaLibraryProvider, useMediaLibrary, MediaPicker, MediaBrowser } from './organisms/MediaLibrary.jsx'
138
138
  export { default as MediaTileGallery } from './organisms/MediaTileGallery.jsx'
139
139
  export { default as MediaViewer } from './organisms/MediaViewer.jsx'
140
- export { default as SettingsPanel, SettingsRow, SettingsSwitch, SettingsChoice, SettingsChipRow, SettingsFooter } from './organisms/SettingsPanel.jsx'
140
+ export { default as SettingsPanel, SettingsSection, SettingsRow, SettingsSwitch, SettingsChoice, SettingsChipRow, SettingsFooter } from './organisms/SettingsPanel.jsx'
141
141
  export { default as SectionNewsletter } from './organisms/SectionNewsletter.jsx'
142
142
  export { default as NewsletterBand } from './organisms/NewsletterBand.jsx'
143
143
  export { default as ColumnBrowser } from './organisms/ColumnBrowser.jsx'
@@ -107,7 +107,10 @@ export default function ContentCollection({
107
107
  gridTemplateColumns: colsCls
108
108
  ? undefined
109
109
  : form === 'list'
110
- ? (listMin ? `repeat(auto-fill, minmax(${listMin}, 1fr))` : '1fr')
110
+ /* minmax(0, 1fr), never a bare 1fr (= minmax(auto, 1fr)): a truncated
111
+ * nowrap line handed its min-content width to the track and a /work row
112
+ * measured 3586px in a 1232px wall (CollectionItemMinWidth, 2026-08-27) */
113
+ ? (listMin ? `repeat(auto-fill, minmax(${listMin}, 1fr))` : 'minmax(0, 1fr)')
111
114
  : `repeat(auto-fill, minmax(${min}, 1fr))`,
112
115
  gap: g,
113
116
  }}
@@ -116,7 +119,10 @@ export default function ContentCollection({
116
119
  child == null ? null : (
117
120
  <li
118
121
  key={child.key ?? i}
119
- className={animate ? 'kol-collection-item' : undefined}
122
+ /* min-w-0 on every item: a grid item's min-width is auto by default,
123
+ * and auto = min-content, which is exactly what a truncated line must
124
+ * not get to claim */
125
+ className={`min-w-0 ${animate ? 'kol-collection-item' : ''}`.trim()}
120
126
  style={animate ? { animationDelay: `${i * 40}ms` } : undefined}
121
127
  >
122
128
  {child}
@@ -1,5 +1,6 @@
1
1
  import Button from '../atoms/Button.jsx'
2
- import SegmentedToggle from '../atoms/SegmentedToggle.jsx'
2
+ import Dropdown from '../molecules/Dropdown.jsx'
3
+ import SectionLabel from '../atoms/SectionLabel.jsx'
3
4
  import ToggleSwitch from '../atoms/ToggleSwitch.jsx'
4
5
  import ShellDrawer from '../molecules/ShellDrawer.jsx'
5
6
  import FullscreenOverlay from '../utilities/FullscreenOverlay.jsx'
@@ -21,7 +22,7 @@ import FullscreenOverlay from '../utilities/FullscreenOverlay.jsx'
21
22
  * where the DS already owned one.
22
23
  *
23
24
  * The CONTROLS are the DS controls, not the source's word-buttons: a switch
24
- * row is `ToggleSwitch`, a choice row is `SegmentedToggle` — the source's
25
+ * row is `ToggleSwitch`, a choice row is a `Dropdown` (2026-08-27) — the source's
25
26
  * segmented variant was flagged by the user as the thing to fix here, not to
26
27
  * copy. Sections are the DS `Section` (pass `divided` on every one and the
27
28
  * hairline lands between siblings on its own).
@@ -129,11 +130,26 @@ export function SettingsSwitch({ on = false, onChange, disabled = false, disable
129
130
  )
130
131
  }
131
132
 
132
- /** SettingsChoice — the row's one-of-N control: the DS SegmentedToggle, sm.
133
- * Options are values or `{ value, label }`. */
134
- export function SettingsChoice({ options = [], value, onChange, ariaLabel }) {
133
+ /** SettingsSectiona section of the panel: the estate's section label (the DS
134
+ * `SectionLabel`, sm SettingsPanelEyebrowAndDropdowns, user 2026-08-27: "it's
135
+ * literally SECTION LABEL, SUPER COMMON PATTERN") over its rows.
136
+ * @param {string} label · @param {boolean} divided a hairline above (between sections) */
137
+ export function SettingsSection({ label, divided = false, children, className = '' }) {
138
+ return (
139
+ <div className={`flex flex-col gap-3 ${divided ? 'kol-section--divided' : ''} ${className}`.replace(/\s+/g, ' ').trim()}>
140
+ {label && <SectionLabel text={label} size="sm" />}
141
+ {children}
142
+ </div>
143
+ )
144
+ }
145
+
146
+ /** SettingsChoice — the row's one-of-N control: the DS Dropdown, sm · primary
147
+ * (SettingsPanelEyebrowAndDropdowns — user: "put the toggles inside a dropdown,
148
+ * because it's super messy like it is"). Options are values or `{ value, label }`;
149
+ * width is the call site's (`className="w-40"`). */
150
+ export function SettingsChoice({ options = [], value, onChange, ariaLabel, className = '' }) {
135
151
  const opts = options.map((o) => (o != null && typeof o === 'object' ? o : { value: o, label: String(o) }))
136
- return <SegmentedToggle size="sm" value={value} onChange={onChange} options={opts} ariaLabel={ariaLabel} />
152
+ return <Dropdown size="sm" variant="primary" value={value} onChange={onChange} options={opts} className={className} aria-label={ariaLabel} />
137
153
  }
138
154
 
139
155
  /* THE CONTROL CHIP (SettingsPanelCompliance, user 2026-08-27: "WROOOONG" on the
@@ -146,15 +162,26 @@ export const chipCls = (on) => `${CHIP_CLS} ${on ? 'kol-control--filled' : 'text
146
162
 
147
163
  /**
148
164
  * SettingsChipRow — a wrap of toggle chips with optional counts (an
149
- * allow-list: every chip sets a default, never a gate).
165
+ * allow-list: every chip sets a default, never a gate). `allChip` puts an
166
+ * "all" chip first — the same control chip, filled when every option is on
167
+ * (SettingsPanelEyebrowAndDropdowns — user: "Show all in ghost mode? makes no
168
+ * sense"); `onAll(nextAllOn)` fires when it is clicked.
150
169
  * @param {Array} options [{ value, label, count? }]
151
170
  * @param {Array|Set} selected the values that are on
152
171
  * @param {Function} onToggle (value) => void
172
+ * @param {boolean|string} allChip render the "all" chip (a string = its label)
173
+ * @param {Function} onAll (nextAllOn: boolean) => void
153
174
  */
154
- export function SettingsChipRow({ options = [], selected = [], onToggle }) {
175
+ export function SettingsChipRow({ options = [], selected = [], onToggle, allChip = false, onAll }) {
155
176
  const on = selected instanceof Set ? selected : new Set(selected)
177
+ const allOn = options.length > 0 && options.every((o) => on.has(o.value))
156
178
  return (
157
179
  <div className="flex flex-wrap gap-1">
180
+ {allChip && (
181
+ <button type="button" aria-pressed={allOn} onClick={() => onAll?.(!allOn)} className={chipCls(allOn)}>
182
+ {typeof allChip === 'string' ? allChip : 'all'}
183
+ </button>
184
+ )}
158
185
  {options.map((o) => (
159
186
  <button
160
187
  key={String(o.value)}