@kolkrabbi/kol-component 0.9.0 → 0.10.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 +2 -2
- package/src/atoms/AssetPlaceholder.jsx +3 -2
- package/src/atoms/Button.jsx +5 -1
- package/src/atoms/FullscreenOverlay.jsx +11 -9
- package/src/atoms/Input.jsx +0 -3
- package/src/atoms/LabeledControl.jsx +2 -2
- package/src/atoms/SearchInput.jsx +66 -1
- package/src/atoms/Section.jsx +1 -1
- package/src/atoms/Tag.jsx +1 -1
- package/src/atoms/Textarea.jsx +1 -1
- package/src/atoms/ToggleBracket.jsx +2 -2
- package/src/atoms/ToggleCheckbox.jsx +2 -2
- package/src/atoms/ViewToggle.jsx +3 -3
- package/src/index.js +1 -0
- package/src/molecules/Accordion.jsx +4 -2
- package/src/molecules/CodeBlock.jsx +20 -4
- package/src/molecules/ColorInputRow.jsx +0 -1
- package/src/molecules/Dropdown.jsx +6 -2
- package/src/molecules/MenuItem.jsx +1 -1
- package/src/molecules/TabsRow.jsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@floating-ui/react": "^0.27.19",
|
|
25
25
|
"embla-carousel-react": "^8.6.0",
|
|
26
|
-
"@kolkrabbi/kol-icons": "0.
|
|
26
|
+
"@kolkrabbi/kol-icons": "0.6.1"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"react": "^18.3.0 || ^19.0.0",
|
|
@@ -10,7 +10,7 @@ export default function AssetPlaceholder({
|
|
|
10
10
|
category,
|
|
11
11
|
name,
|
|
12
12
|
aspectRatio = '16 / 9',
|
|
13
|
-
note = '
|
|
13
|
+
note = 'MISSING',
|
|
14
14
|
className = '',
|
|
15
15
|
}) {
|
|
16
16
|
const label = [category, name].filter(Boolean).join(' · ')
|
|
@@ -23,7 +23,8 @@ export default function AssetPlaceholder({
|
|
|
23
23
|
>
|
|
24
24
|
{/* note is single-line → helper scale; label can wrap (category · name
|
|
25
25
|
* in a narrow tile) → line-height-bearing mono scale */}
|
|
26
|
-
|
|
26
|
+
{/* no text-transform (casing law) — note is authored in-case */}
|
|
27
|
+
<span className="kol-asset-placeholder-note kol-helper-12 [letter-spacing:0.12em] text-fg-48">{note}</span>
|
|
27
28
|
{label && <span className="kol-asset-placeholder-label kol-mono-12 text-fg-40 [letter-spacing:0.04em]">{label}</span>}
|
|
28
29
|
</div>
|
|
29
30
|
)
|
package/src/atoms/Button.jsx
CHANGED
|
@@ -10,7 +10,7 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
10
10
|
*
|
|
11
11
|
* @param {Object} props
|
|
12
12
|
* @param {ReactNode} props.children - Button content
|
|
13
|
-
* @param {'primary'|'secondary'|'accent'|'outline'|'ghost'|'control'} props.variant - Visual variant. `control` is an alias for `ghost` (legacy call-sites).
|
|
13
|
+
* @param {'primary'|'secondary'|'accent'|'outline'|'ghost'|'danger'|'grey'|'control'} props.variant - Visual variant. `danger` is the destructive treatment (--ui-error fill); `control` is an alias for `ghost` (legacy call-sites).
|
|
14
14
|
* @param {'sm'|'md'|'lg'} props.size - Button size (default: 'md')
|
|
15
15
|
* @param {string} props.iconLeft - Icon name to display on the left
|
|
16
16
|
* @param {string} props.iconRight - Icon name to display on the right
|
|
@@ -71,6 +71,10 @@ const Button = ({
|
|
|
71
71
|
? 'kol-btn-outline'
|
|
72
72
|
: resolvedVariant === 'ghost'
|
|
73
73
|
? 'kol-btn-ghost'
|
|
74
|
+
: resolvedVariant === 'danger'
|
|
75
|
+
? 'kol-btn-danger'
|
|
76
|
+
: resolvedVariant === 'grey'
|
|
77
|
+
? 'kol-btn-grey'
|
|
74
78
|
: 'kol-btn-secondary'
|
|
75
79
|
|
|
76
80
|
// Add size class — pairs the padding rule with its mono type class.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react'
|
|
2
2
|
|
|
3
|
-
export default function FullscreenOverlay({ open, onClose, children }) {
|
|
3
|
+
export default function FullscreenOverlay({ open, onClose, closeButton = true, children }) {
|
|
4
4
|
const sheetRef = useRef(null)
|
|
5
5
|
|
|
6
6
|
useEffect(() => {
|
|
@@ -24,14 +24,16 @@ export default function FullscreenOverlay({ open, onClose, children }) {
|
|
|
24
24
|
return (
|
|
25
25
|
<div className="kol-overlay" role="dialog" aria-modal="true" onMouseDown={onBackdropClick}>
|
|
26
26
|
<div ref={sheetRef} className="kol-overlay-sheet">
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
{closeButton && (
|
|
28
|
+
<button
|
|
29
|
+
type="button"
|
|
30
|
+
className="kol-overlay-close"
|
|
31
|
+
onClick={onClose}
|
|
32
|
+
aria-label="Close"
|
|
33
|
+
>
|
|
34
|
+
×
|
|
35
|
+
</button>
|
|
36
|
+
)}
|
|
35
37
|
{children}
|
|
36
38
|
</div>
|
|
37
39
|
</div>
|
package/src/atoms/Input.jsx
CHANGED
|
@@ -25,7 +25,6 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
25
25
|
* the shell at text-meta. aria-hidden — affordances, not labels.
|
|
26
26
|
* iconLeft — name of a leading icon rendered inside the shell (e.g.
|
|
27
27
|
* "search-16"). iconSize overrides the size-derived default.
|
|
28
|
-
* uppercase — adds Tailwind `uppercase` to the input element only.
|
|
29
28
|
*
|
|
30
29
|
* Chrome (bg/border/padding/transition/disabled) comes from .kol-control;
|
|
31
30
|
* Input owns prefix/suffix/icon layout + the inner <input> styling.
|
|
@@ -47,7 +46,6 @@ export default function Input({
|
|
|
47
46
|
iconSize = null,
|
|
48
47
|
placeholder,
|
|
49
48
|
disabled = false,
|
|
50
|
-
uppercase = false,
|
|
51
49
|
width,
|
|
52
50
|
className = '',
|
|
53
51
|
inputClassName = '',
|
|
@@ -86,7 +84,6 @@ export default function Input({
|
|
|
86
84
|
* the affordance than to the empty edge, reads off-balance. */
|
|
87
85
|
prefix !== undefined && 'pr-1',
|
|
88
86
|
suffix !== undefined && 'pl-1',
|
|
89
|
-
uppercase && 'uppercase',
|
|
90
87
|
isNumber && 'hide-number-spinners',
|
|
91
88
|
inputClassName,
|
|
92
89
|
].filter(Boolean).join(' ')
|
|
@@ -44,7 +44,7 @@ export default function LabeledControl({
|
|
|
44
44
|
<div className={`flex items-center gap-3 ${className}`}>
|
|
45
45
|
{showLabel && (
|
|
46
46
|
<span
|
|
47
|
-
className="kol-helper-10
|
|
47
|
+
className="kol-helper-10 tracking-widest text-meta shrink-0"
|
|
48
48
|
style={{ width: labelWidth }}
|
|
49
49
|
>
|
|
50
50
|
{labelInner}
|
|
@@ -58,7 +58,7 @@ export default function LabeledControl({
|
|
|
58
58
|
return (
|
|
59
59
|
<div className={`flex flex-col gap-2 ${className}`}>
|
|
60
60
|
{showLabel && (
|
|
61
|
-
<span className="kol-helper-10
|
|
61
|
+
<span className="kol-helper-10 tracking-widest text-meta">{labelInner}</span>
|
|
62
62
|
)}
|
|
63
63
|
{children}
|
|
64
64
|
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react'
|
|
1
2
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -7,6 +8,14 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
7
8
|
* icon, then two mutually exclusive trailing affordances: a caller-authored
|
|
8
9
|
* shortcut kbd chip while the value is empty, a clear × while it isn't.
|
|
9
10
|
*
|
|
11
|
+
* `expanding` is the third body plan (after the shell and `bare`): a round
|
|
12
|
+
* icon-button pill that animates open into an inline field — the /work
|
|
13
|
+
* navbar pattern, extracted from kol-content's WorkViewToggle (2026-07-15).
|
|
14
|
+
* `open` is controllable so a parent can choreograph siblings around it
|
|
15
|
+
* (collapse a toggle, reveal a close button); uncontrolled works too.
|
|
16
|
+
* Escape reports `onOpenChange(false)` — clearing on close stays the
|
|
17
|
+
* parent's call.
|
|
18
|
+
*
|
|
10
19
|
* Always controlled (`value` defaults to ''). Controlled + no onChange =
|
|
11
20
|
* deliberate display-only → readOnly, same convention as Input. Extra props
|
|
12
21
|
* (onKeyDown, autoFocus, role, aria-*) spread onto the inner <input> —
|
|
@@ -23,13 +32,19 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
23
32
|
* @param {string} shortcutHint caller-authored kbd chip (e.g. "⌘K"); shown while the value is empty
|
|
24
33
|
* @param {Function} onFocus (event) => void — input focus
|
|
25
34
|
* @param {string} size 'sm' | 'md' — kol-control size + matched mono type class
|
|
26
|
-
* @param {string} variant 'filled' | 'ghost' | 'outline' — kol-control variant, same chrome as Input (ignored when bare)
|
|
35
|
+
* @param {string} variant 'filled' | 'ghost' | 'outline' — kol-control variant, same chrome as Input (ignored when bare/expanding)
|
|
27
36
|
* @param {boolean} bare borderless inline field for overlay panels (full width, no shell chrome; keeps icon/clear/chip slots)
|
|
37
|
+
* @param {boolean} expanding round icon-button pill that animates open into an inline field
|
|
38
|
+
* @param {boolean} open (expanding) controlled open state; omit for internal state
|
|
39
|
+
* @param {Function} onOpenChange (expanding) (bool) => void — trigger click / Escape
|
|
40
|
+
* @param {number} expandedWidth (expanding) open pill width in px (default 280)
|
|
41
|
+
* @param {string} triggerLabel (expanding) aria-label on the icon trigger (default 'Open search')
|
|
28
42
|
* @param {string} className extra classes on the shell
|
|
29
43
|
*/
|
|
30
44
|
|
|
31
45
|
const SIZE_TYPE = { sm: 'kol-mono-12', md: 'kol-mono-14' }
|
|
32
46
|
const ICON_SIZE = { sm: 14, md: 14 }
|
|
47
|
+
const CUBIC_EASE = 'cubic-bezier(0.16, 1, 0.3, 1)'
|
|
33
48
|
|
|
34
49
|
export default function SearchInput({
|
|
35
50
|
value = '',
|
|
@@ -41,9 +56,59 @@ export default function SearchInput({
|
|
|
41
56
|
size = 'md',
|
|
42
57
|
variant = 'filled',
|
|
43
58
|
bare = false,
|
|
59
|
+
expanding = false,
|
|
60
|
+
open,
|
|
61
|
+
onOpenChange,
|
|
62
|
+
expandedWidth = 280,
|
|
63
|
+
triggerLabel = 'Open search',
|
|
44
64
|
className = '',
|
|
45
65
|
...inputProps
|
|
46
66
|
}) {
|
|
67
|
+
/* expanding body plan — hooks stay unconditional (expanding never flips at runtime) */
|
|
68
|
+
const [internalOpen, setInternalOpen] = useState(false)
|
|
69
|
+
const inputRef = useRef(null)
|
|
70
|
+
const isOpen = open !== undefined ? open : internalOpen
|
|
71
|
+
const setOpen = (next) => {
|
|
72
|
+
if (open === undefined) setInternalOpen(next)
|
|
73
|
+
onOpenChange?.(next)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (expanding && isOpen) inputRef.current?.focus()
|
|
78
|
+
}, [expanding, isOpen])
|
|
79
|
+
|
|
80
|
+
if (expanding) {
|
|
81
|
+
return (
|
|
82
|
+
<div
|
|
83
|
+
className={`flex items-center bg-fg-04 rounded-full h-9 ${className}`.trim()}
|
|
84
|
+
style={{ width: isOpen ? expandedWidth : 36, transition: `width 600ms ${CUBIC_EASE}` }}
|
|
85
|
+
>
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
className={`flex items-center justify-center w-9 h-9 rounded-full text-auto flex-shrink-0 border border-transparent ${isOpen ? '' : 'transition-colors hover:border-fg-12'}`}
|
|
89
|
+
onClick={() => !isOpen && setOpen(true)}
|
|
90
|
+
aria-label={triggerLabel}
|
|
91
|
+
aria-expanded={isOpen}
|
|
92
|
+
>
|
|
93
|
+
<Icon name="search" size={16} className="text-fg-80" />
|
|
94
|
+
</button>
|
|
95
|
+
{isOpen && (
|
|
96
|
+
<input
|
|
97
|
+
ref={inputRef}
|
|
98
|
+
type="search"
|
|
99
|
+
value={value ?? ''}
|
|
100
|
+
onChange={onChange}
|
|
101
|
+
readOnly={!onChange || undefined}
|
|
102
|
+
placeholder={placeholder}
|
|
103
|
+
spellCheck={false}
|
|
104
|
+
className="bg-transparent outline-none kol-helper-14 flex-1 text-fg-80 caret-current pr-4 min-w-0 appearance-none [&::-webkit-search-cancel-button]:hidden"
|
|
105
|
+
onKeyDown={(e) => { if (e.key === 'Escape') setOpen(false) }}
|
|
106
|
+
{...inputProps}
|
|
107
|
+
/>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
47
112
|
const shellCls = [
|
|
48
113
|
bare
|
|
49
114
|
? 'flex w-full gap-2.5 px-4 py-3'
|
package/src/atoms/Section.jsx
CHANGED
|
@@ -8,7 +8,7 @@ export default function Section({ label, children, className = '' }) {
|
|
|
8
8
|
return (
|
|
9
9
|
<div className={`flex flex-col gap-2 ${className}`}>
|
|
10
10
|
{label && (
|
|
11
|
-
<p className="kol-helper-10
|
|
11
|
+
<p className="kol-helper-10 tracking-widest text-meta">{label}</p>
|
|
12
12
|
)}
|
|
13
13
|
{children}
|
|
14
14
|
</div>
|
package/src/atoms/Tag.jsx
CHANGED
package/src/atoms/Textarea.jsx
CHANGED
|
@@ -6,7 +6,7 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
6
6
|
* `--textarea` modifier (display: block).
|
|
7
7
|
*
|
|
8
8
|
* variant="filled" (default) — persistent solid bg
|
|
9
|
-
* variant="ghost" —
|
|
9
|
+
* variant="ghost" — legacy alias, resolves to outline
|
|
10
10
|
* variant="outline" — bordered, transparent bg
|
|
11
11
|
*
|
|
12
12
|
* size="sm" / "md" (default) / "lg" — padding + type class
|
|
@@ -25,8 +25,8 @@ const ToggleBracket = ({
|
|
|
25
25
|
const isPlain = variant === 'plain'
|
|
26
26
|
|
|
27
27
|
const cls = isPlain
|
|
28
|
-
? `inline-flex items-center gap-3 kol-mono-12
|
|
29
|
-
: `kol-control kol-control--filled kol-control-md kol-mono-12
|
|
28
|
+
? `inline-flex items-center gap-3 kol-mono-12 text-meta hover:text-emphasis transition-colors bg-transparent border-0 p-0 cursor-pointer ${className}`
|
|
29
|
+
: `kol-control kol-control--filled kol-control-md kol-mono-12 justify-between gap-3 ${value ? 'toggle-bracket--active' : ''} ${className}`
|
|
30
30
|
|
|
31
31
|
return (
|
|
32
32
|
<button
|
|
@@ -29,9 +29,9 @@ const ToggleCheckbox = ({
|
|
|
29
29
|
<polyline points="1 5 4 8 11 1" fill="none" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
|
|
30
30
|
</svg>
|
|
31
31
|
</span>
|
|
32
|
-
<span className="toggle-checkbox-label kol-helper-12
|
|
32
|
+
<span className="toggle-checkbox-label kol-helper-12 tracking-[0.08em]">
|
|
33
33
|
{label}
|
|
34
|
-
{hint ? <span className="ml-2 opacity-60
|
|
34
|
+
{hint ? <span className="ml-2 opacity-60 tracking-normal kol-helper-10">{hint}</span> : null}
|
|
35
35
|
</span>
|
|
36
36
|
</label>
|
|
37
37
|
)
|
package/src/atoms/ViewToggle.jsx
CHANGED
|
@@ -13,7 +13,7 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
13
13
|
* inactive = off (bare text). Use for compact on/off
|
|
14
14
|
* where a segmented two-button toggle is overkill.
|
|
15
15
|
*
|
|
16
|
-
* Built on the .kol-control shell. Default options use grid
|
|
16
|
+
* Built on the .kol-control shell. Default options use grid / view-list
|
|
17
17
|
* icons; consumers can pass `options` to override. `iconVariant` picks the
|
|
18
18
|
* icon cut for variant="icon" ('stroke' default; 'solid' reads better at
|
|
19
19
|
* 14px). For `variant="single"`, the FIRST option in `options` is the "off"
|
|
@@ -25,8 +25,8 @@ const ViewToggle = ({
|
|
|
25
25
|
variant = 'text',
|
|
26
26
|
iconVariant = 'stroke',
|
|
27
27
|
options = [
|
|
28
|
-
{ value: 'grid', label: 'Grid view', icon: 'grid
|
|
29
|
-
{ value: 'list', label: 'List view', icon: 'list
|
|
28
|
+
{ value: 'grid', label: 'Grid view', icon: 'grid' },
|
|
29
|
+
{ value: 'list', label: 'List view', icon: 'view-list' }
|
|
30
30
|
],
|
|
31
31
|
className = ''
|
|
32
32
|
}) => {
|
package/src/index.js
CHANGED
|
@@ -117,6 +117,7 @@ export { Icon } from '@kolkrabbi/kol-icons'
|
|
|
117
117
|
|
|
118
118
|
// graphics (SVG illustration loader — globs its own ./graphics/svg/**)
|
|
119
119
|
export { default as Graphic, GRAPHICS } from './graphics/Graphic.jsx'
|
|
120
|
+
export { GRAPHIC_RAW } from './graphics/graphicData.js'
|
|
120
121
|
|
|
121
122
|
// hooks
|
|
122
123
|
export { default as usePrefersReducedMotion } from './hooks/usePrefersReducedMotion.js'
|
|
@@ -38,8 +38,10 @@ export function AccordionPanel({
|
|
|
38
38
|
onClick={toggle}
|
|
39
39
|
aria-expanded={open}
|
|
40
40
|
>
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
{/* no text-transform (casing law 2026-07-15): titles render as authored —
|
|
42
|
+
* kol-helper-12 already carries the tracked-caps letterspacing */}
|
|
43
|
+
<span className="kol-accordion-title kol-helper-12">{title}</span>
|
|
44
|
+
{meta && <span className="kol-accordion-meta kol-helper-12 text-fg-48 ml-auto mr-3">{meta}</span>}
|
|
43
45
|
<span className="kol-accordion-chevron kol-helper-16 text-fg-48 min-w-3 transition-colors duration-[120ms] ml-auto" aria-hidden="true">{open ? '−' : '+'}</span>
|
|
44
46
|
</button>
|
|
45
47
|
{open && <div className="kol-accordion-body pt-2 pb-6">{children}</div>}
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import CopyButton from '../atoms/CopyButton.jsx'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* CodeBlock — a chrome'd code block with language chip, filename chip, and copy.
|
|
5
|
+
*
|
|
6
|
+
* Accepts three input shapes (first match wins per field):
|
|
7
|
+
* • Portable Text: `value={{ code, language, filename }}` — the Sanity code-block
|
|
8
|
+
* object can be passed straight through from a portable-text map.
|
|
9
|
+
* • Direct props: `code` / `language` / `filename`.
|
|
10
|
+
* • Children: `<CodeBlock language="js">{'…'}</CodeBlock>` (the original API).
|
|
11
|
+
*
|
|
12
|
+
* DOM order is copy → filename → lang → pre so the `.kol-codeblock-*` + pre
|
|
13
|
+
* adjacency rules in the chrome CSS can pad the pre when chips are present.
|
|
14
|
+
*/
|
|
15
|
+
export default function CodeBlock({ children, code, language, filename, value }) {
|
|
16
|
+
const src = code ?? value?.code ?? children
|
|
17
|
+
const lang = language ?? value?.language
|
|
18
|
+
const file = filename ?? value?.filename
|
|
4
19
|
return (
|
|
5
20
|
<div className="kol-codeblock">
|
|
6
|
-
{
|
|
7
|
-
<
|
|
8
|
-
<
|
|
21
|
+
<CopyButton text={String(src ?? '')} className="kol-codeblock-copy" />
|
|
22
|
+
{file && <span className="kol-codeblock-filename">{file}</span>}
|
|
23
|
+
{lang && <span className="kol-codeblock-lang">{lang}</span>}
|
|
24
|
+
<pre><code>{src}</code></pre>
|
|
9
25
|
</div>
|
|
10
26
|
)
|
|
11
27
|
}
|
|
@@ -13,6 +13,8 @@ import { PopoverPanel, usePopover } from '../atoms/Popover.jsx'
|
|
|
13
13
|
*
|
|
14
14
|
* variant="primary" (default) — filled trigger; open panel continues the
|
|
15
15
|
* same fill (one piece: no border, no gap, hairline divider inside)
|
|
16
|
+
* variant="grey" — oq-12 filled trigger (opaque per the fill
|
|
17
|
+
* law); panel continues it
|
|
16
18
|
* variant="outline" — bordered trigger; open panel carries the
|
|
17
19
|
* same border, trigger's bottom edge acts as the divider
|
|
18
20
|
*
|
|
@@ -110,7 +112,9 @@ const Dropdown = ({
|
|
|
110
112
|
|
|
111
113
|
const triggerCls = [
|
|
112
114
|
'kol-btn',
|
|
113
|
-
|
|
115
|
+
/* grey is dropdown-only chrome (2026-07-15) — no kol-btn-* class so it
|
|
116
|
+
* never leaks into Button's variant set */
|
|
117
|
+
resolvedVariant === 'grey' ? 'kol-dd-trigger--grey' : `kol-btn-${resolvedVariant}`,
|
|
114
118
|
`kol-btn-${resolvedSize}`,
|
|
115
119
|
SIZE_TYPE[resolvedSize],
|
|
116
120
|
'kol-dd-trigger',
|
|
@@ -154,7 +158,7 @@ const Dropdown = ({
|
|
|
154
158
|
focus={false}
|
|
155
159
|
className={`kol-dd-panel kol-dd-panel--${resolvedVariant}`}
|
|
156
160
|
>
|
|
157
|
-
{resolvedVariant === 'primary' && <div className="kol-dd-div" />}
|
|
161
|
+
{(resolvedVariant === 'primary' || resolvedVariant === 'grey') && <div className="kol-dd-div" />}
|
|
158
162
|
|
|
159
163
|
<div className="flex max-h-[300px] flex-col items-stretch overflow-y-auto" role="listbox">
|
|
160
164
|
{options.map((option) => {
|
|
@@ -95,7 +95,7 @@ export function MenuDropdownItem({ onClick, disabled, prefix, iconLeft, shortcut
|
|
|
95
95
|
onClick={onClick}
|
|
96
96
|
disabled={disabled}
|
|
97
97
|
role="menuitem"
|
|
98
|
-
className="w-full kol-helper-12 px-3 h-8 inline-flex items-center gap-2 text-body hover:text-emphasis disabled:opacity-40 disabled:cursor-not-allowed text-left"
|
|
98
|
+
className="w-full kol-helper-12 px-3 h-8 shrink-0 inline-flex items-center gap-2 text-body hover:text-emphasis disabled:opacity-40 disabled:cursor-not-allowed text-left"
|
|
99
99
|
>
|
|
100
100
|
{prefix && <span className="shrink-0 inline-flex items-center">{prefix}</span>}
|
|
101
101
|
{iconLeft && <span className="shrink-0 w-4 inline-flex items-center justify-center">{iconLeft}</span>}
|