@adea-ai/ui 0.82.0 → 0.82.2
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/dist/components/composites/appearance-editor/appearance-choices.d.ts.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-choices.js +3 -3
- package/dist/components/composites/appearance-editor/appearance-choices.js.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-rows.d.ts.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-rows.js +13 -2
- package/dist/components/composites/appearance-editor/appearance-rows.js.map +1 -1
- package/dist/components/layout/side-rail/side-rail.d.ts +1 -0
- package/dist/components/layout/side-rail/side-rail.d.ts.map +1 -1
- package/dist/components/layout/side-rail/side-rail.js +6 -2
- package/dist/components/layout/side-rail/side-rail.js.map +1 -1
- package/dist/components/ui/command/command.d.ts +5 -6
- package/dist/components/ui/command/command.d.ts.map +1 -1
- package/dist/components/ui/command/command.js +73 -15
- package/dist/components/ui/command/command.js.map +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts +3 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.js +7 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/ui/status-chip/status-chip.d.ts +2 -0
- package/dist/components/ui/status-chip/status-chip.d.ts.map +1 -1
- package/dist/components/ui/status-chip/status-chip.js +26 -17
- package/dist/components/ui/status-chip/status-chip.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/overlay.js +1 -1
- package/dist/lib/overlay.js.map +1 -1
- package/dist/lint/index.d.ts +2 -0
- package/dist/lint/index.d.ts.map +1 -1
- package/dist/lint.js +82 -0
- package/dist/lint.js.map +1 -1
- package/dist/r/command.json +2 -0
- package/dist/r/registry.json +2 -0
- package/dist/r/src/components/composites/appearance-editor/appearance-choices.tsx +3 -5
- package/dist/r/src/components/composites/appearance-editor/appearance-rows.tsx +9 -3
- package/dist/r/src/components/layout/side-rail/side-rail.tsx +8 -3
- package/dist/r/src/components/ui/command/command.tsx +81 -29
- package/dist/r/src/components/ui/dropdown-menu/dropdown-menu.tsx +5 -1
- package/dist/r/src/components/ui/status-chip/status-chip.tsx +11 -2
- package/dist/r/src/lib/overlay.ts +1 -1
- package/package.json +1 -1
- package/registry.json +2 -0
- package/src/components/composites/appearance-editor/appearance-choices.tsx +3 -5
- package/src/components/composites/appearance-editor/appearance-rows.tsx +9 -3
- package/src/components/layout/side-rail/side-rail.tsx +8 -3
- package/src/components/ui/command/command.tsx +81 -29
- package/src/components/ui/dropdown-menu/dropdown-menu.tsx +5 -1
- package/src/components/ui/status-chip/status-chip.tsx +11 -2
- package/src/lib/overlay.ts +1 -1
- package/src/lint/index.ts +95 -0
|
@@ -10,17 +10,20 @@ import {
|
|
|
10
10
|
CommandSeparator as CmdkSeparator,
|
|
11
11
|
useCommandState,
|
|
12
12
|
} from 'cmdk-solid'
|
|
13
|
+
import { Dialog as KobalteDialog } from '@kobalte/core/dialog'
|
|
13
14
|
import { Search } from 'lucide-solid'
|
|
14
15
|
import type { ComponentProps } from 'solid-js'
|
|
15
16
|
import { createEffect, createSignal, splitProps } from 'solid-js'
|
|
17
|
+
import { createDialogFocusRestoration } from '../dialog/dialog'
|
|
16
18
|
import { menuItem } from '#lib/overlay'
|
|
17
19
|
import { cn } from '#lib/utils'
|
|
18
20
|
|
|
19
|
-
// cmdk emits data-disabled="false" on enabled items
|
|
20
|
-
//
|
|
21
|
+
// cmdk emits data-disabled="false" on enabled items, unlike the presence-only
|
|
22
|
+
// attributes Kobalte menu rows use. Apply those shared disabled styles only when
|
|
23
|
+
// cmdk's value is true.
|
|
21
24
|
const commandItem = menuItem
|
|
22
25
|
.replace('data-[disabled]:pointer-events-none', '')
|
|
23
|
-
.replace('data-[disabled]:
|
|
26
|
+
.replace('data-[disabled]:text-muted-foreground', '')
|
|
24
27
|
|
|
25
28
|
/**
|
|
26
29
|
* Command.
|
|
@@ -52,26 +55,76 @@ export function Command(props: ComponentProps<typeof CmdkRoot>) {
|
|
|
52
55
|
)
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
58
|
+
type CommandDialogProps = ComponentProps<typeof CmdkDialog> &
|
|
59
|
+
Pick<ComponentProps<typeof KobalteDialog.Content>, 'onCloseAutoFocus' | 'onOpenAutoFocus'>
|
|
60
|
+
|
|
61
|
+
export function CommandDialog(props: CommandDialogProps) {
|
|
62
|
+
// cmdk-solid's Dialog does not forward Content autofocus props: unknown
|
|
63
|
+
// props fall through to its Command root. Compose Kobalte's dialog surface
|
|
64
|
+
// here so Escape and other close paths can restore focus to the real opener.
|
|
65
|
+
// Preserve the shared wrapper's `class` contract on the dialog surface;
|
|
66
|
+
// `contentClassName` adds to it, while the nested Command keeps its own API.
|
|
67
|
+
const [local, dialogProps, commandProps] = splitProps(
|
|
68
|
+
props,
|
|
69
|
+
[
|
|
70
|
+
'class',
|
|
71
|
+
'contentClassName',
|
|
72
|
+
'overlayClassName',
|
|
73
|
+
'container',
|
|
74
|
+
'onCloseAutoFocus',
|
|
75
|
+
'onOpenAutoFocus',
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
'open',
|
|
79
|
+
'defaultOpen',
|
|
80
|
+
'onOpenChange',
|
|
81
|
+
'id',
|
|
82
|
+
'modal',
|
|
83
|
+
'preventScroll',
|
|
84
|
+
'forceMount',
|
|
85
|
+
'translations',
|
|
86
|
+
]
|
|
87
|
+
)
|
|
88
|
+
const [isOpen, setIsOpen] = createSignal(dialogProps.open ?? dialogProps.defaultOpen ?? false)
|
|
89
|
+
|
|
90
|
+
createEffect(() => {
|
|
91
|
+
if (dialogProps.open !== undefined) setIsOpen(dialogProps.open)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const focus = createDialogFocusRestoration({
|
|
95
|
+
open: isOpen,
|
|
96
|
+
onCloseAutoFocus: local.onCloseAutoFocus,
|
|
97
|
+
onOpenAutoFocus: local.onOpenAutoFocus,
|
|
98
|
+
})
|
|
63
99
|
|
|
64
100
|
return (
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
101
|
+
<KobalteDialog
|
|
102
|
+
{...dialogProps}
|
|
103
|
+
onOpenChange={(open) => {
|
|
104
|
+
setIsOpen(open)
|
|
105
|
+
dialogProps.onOpenChange?.(open)
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
<KobalteDialog.Portal mount={local.container}>
|
|
109
|
+
<KobalteDialog.Overlay cmdk-overlay="" class={local.overlayClassName} />
|
|
110
|
+
<KobalteDialog.Content
|
|
111
|
+
aria-label={commandProps.label}
|
|
112
|
+
cmdk-dialog=""
|
|
113
|
+
class={cn(
|
|
114
|
+
'bg-popover text-popover-foreground fixed top-1/3 left-1/2 z-(--z-dialog) w-full max-w-lg -translate-x-1/2 -translate-y-1/2',
|
|
115
|
+
'overflow-hidden rounded-xl border border-border shadow-xl',
|
|
116
|
+
'data-expanded:animate-in data-expanded:fade-in-0 data-expanded:zoom-in-95',
|
|
117
|
+
'data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
|
|
118
|
+
local.class,
|
|
119
|
+
local.contentClassName
|
|
120
|
+
)}
|
|
121
|
+
onOpenAutoFocus={focus.onOpenAutoFocus}
|
|
122
|
+
onCloseAutoFocus={focus.onCloseAutoFocus}
|
|
123
|
+
>
|
|
124
|
+
<Command {...commandProps} />
|
|
125
|
+
</KobalteDialog.Content>
|
|
126
|
+
</KobalteDialog.Portal>
|
|
127
|
+
</KobalteDialog>
|
|
75
128
|
)
|
|
76
129
|
}
|
|
77
130
|
|
|
@@ -188,8 +241,8 @@ export function CommandItem(props: ComponentProps<typeof CmdkItem>) {
|
|
|
188
241
|
<CmdkItem
|
|
189
242
|
class={cn(
|
|
190
243
|
commandItem,
|
|
191
|
-
'data-[disabled=true]:pointer-events-none data-[disabled=true]:
|
|
192
|
-
'text-popover-foreground data-[selected=true]:bg-
|
|
244
|
+
'data-[disabled=true]:pointer-events-none data-[disabled=true]:text-muted-foreground',
|
|
245
|
+
'group/command-item text-popover-foreground data-[selected=true]:bg-card data-[selected=true]:text-popover-foreground',
|
|
193
246
|
local.class
|
|
194
247
|
)}
|
|
195
248
|
{...rest}
|
|
@@ -212,18 +265,17 @@ export function CommandLoading(props: ComponentProps<typeof CmdkLoading>) {
|
|
|
212
265
|
)
|
|
213
266
|
}
|
|
214
267
|
|
|
215
|
-
/**
|
|
216
|
-
* The keyboard hint a palette item usually carries.
|
|
217
|
-
* Muted canvas text can fail contrast on a raised or selected command row;
|
|
218
|
-
* hierarchy comes from its size and monospace face, with the row's readable ink.
|
|
219
|
-
*/
|
|
268
|
+
/** The keyboard hint a palette item usually carries. */
|
|
220
269
|
export function CommandShortcut(props: {
|
|
221
270
|
class?: string
|
|
222
271
|
children?: ComponentProps<'span'>['children']
|
|
223
272
|
}) {
|
|
224
273
|
return (
|
|
225
274
|
<span
|
|
226
|
-
class={cn(
|
|
275
|
+
class={cn(
|
|
276
|
+
'text-muted-foreground ms-auto font-mono text-2xs tracking-widest group-data-[selected=true]/command-item:text-foreground',
|
|
277
|
+
props.class
|
|
278
|
+
)}
|
|
227
279
|
>
|
|
228
280
|
{props.children}
|
|
229
281
|
</span>
|
|
@@ -25,13 +25,17 @@ import { cn } from '#lib/utils'
|
|
|
25
25
|
* Kobalte supplies the full menu contract — arrow keys, typeahead, the roving
|
|
26
26
|
* highlight, Escape to close and return focus — which is exactly what a
|
|
27
27
|
* hand-rolled `div` with click handlers gets wrong.
|
|
28
|
+
* Menus are nonmodal by default so their generated focus guards do not become
|
|
29
|
+
* invalid direct children of the ARIA menu. Set `modal` explicitly when the
|
|
30
|
+
* surrounding interaction needs modal containment.
|
|
28
31
|
*
|
|
29
32
|
* The `destructive` variant marks the row with a status-coloured edge and
|
|
30
33
|
* highlighted tint while keeping its label on the body foreground. Pairing it
|
|
31
34
|
* with an AlertDialog is the caller's job.
|
|
32
35
|
*/
|
|
33
36
|
export function DropdownMenu(props: ComponentProps<typeof KobalteDropdownMenu>) {
|
|
34
|
-
|
|
37
|
+
const [local, rest] = splitProps(props, ['modal'])
|
|
38
|
+
return <KobalteDropdownMenu modal={local.modal ?? false} {...rest} />
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
export function DropdownMenuTrigger(props: ComponentProps<typeof KobalteDropdownMenu.Trigger>) {
|
|
@@ -59,6 +59,8 @@ export type StatusTone = 'neutral' | 'success' | 'warning' | 'danger' | 'info' |
|
|
|
59
59
|
export type StatusChipProps = Omit<ComponentProps<'span'>, 'children'> & {
|
|
60
60
|
tone?: StatusTone
|
|
61
61
|
label: string
|
|
62
|
+
/** Visually hide the text while preserving it for assistive technology. */
|
|
63
|
+
compact?: boolean
|
|
62
64
|
/** Why the status is what it is. Shown in a tooltip; omit for a self-evident label. */
|
|
63
65
|
detail?: string
|
|
64
66
|
/** A trailing slot: a timestamp, a retry affordance, a count. */
|
|
@@ -66,7 +68,14 @@ export type StatusChipProps = Omit<ComponentProps<'span'>, 'children'> & {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
export function StatusChip(props: StatusChipProps) {
|
|
69
|
-
const [local, rest] = splitProps(props, [
|
|
71
|
+
const [local, rest] = splitProps(props, [
|
|
72
|
+
'tone',
|
|
73
|
+
'label',
|
|
74
|
+
'compact',
|
|
75
|
+
'detail',
|
|
76
|
+
'trailing',
|
|
77
|
+
'class',
|
|
78
|
+
])
|
|
70
79
|
|
|
71
80
|
const chip = (
|
|
72
81
|
<span
|
|
@@ -78,7 +87,7 @@ export function StatusChip(props: StatusChipProps) {
|
|
|
78
87
|
{...rest}
|
|
79
88
|
>
|
|
80
89
|
<span class={statusDotVariants({ tone: local.tone })} aria-hidden="true" />
|
|
81
|
-
{local.label}
|
|
90
|
+
<span class={cn(local.compact && 'sr-only')}>{local.label}</span>
|
|
82
91
|
{local.trailing}
|
|
83
92
|
</span>
|
|
84
93
|
)
|
package/src/lib/overlay.ts
CHANGED
|
@@ -54,7 +54,7 @@ export const menuItem = [
|
|
|
54
54
|
'relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none',
|
|
55
55
|
'transition-colors ease-out',
|
|
56
56
|
'data-[highlighted]:bg-surface-hover data-[highlighted]:text-foreground',
|
|
57
|
-
'data-[disabled]:pointer-events-none data-[disabled]:
|
|
57
|
+
'data-[disabled]:pointer-events-none data-[disabled]:text-muted-foreground',
|
|
58
58
|
'[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4',
|
|
59
59
|
'[&_svg:not([class*=text-])]:text-muted-foreground',
|
|
60
60
|
].join(' ')
|
package/src/lint/index.ts
CHANGED
|
@@ -50,6 +50,99 @@ interface LintContext {
|
|
|
50
50
|
}) => void
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/** Reads a static object-property key without evaluating a computed expression. */
|
|
54
|
+
const staticPropertyKey = (node: any, computed: boolean): string | null => {
|
|
55
|
+
if (!computed && node?.type === 'Identifier') return node.name
|
|
56
|
+
if (node?.type === 'Literal' && typeof node.value === 'string') return node.value
|
|
57
|
+
if (node?.type === 'TemplateLiteral' && node.expressions.length === 0)
|
|
58
|
+
return node.quasis[0].value.cooked ?? node.quasis[0].value.raw
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Detects named props in a literal JSX spread, including nested literal spreads. */
|
|
63
|
+
const objectHasStaticProperty = (node: any, name: string, seen = new Set<unknown>()): boolean => {
|
|
64
|
+
if (!node || seen.has(node)) return false
|
|
65
|
+
seen.add(node)
|
|
66
|
+
if (
|
|
67
|
+
node.type === 'TSAsExpression' ||
|
|
68
|
+
node.type === 'TSSatisfiesExpression' ||
|
|
69
|
+
node.type === 'TSNonNullExpression'
|
|
70
|
+
)
|
|
71
|
+
return objectHasStaticProperty(node.expression, name, seen)
|
|
72
|
+
if (node.type !== 'ObjectExpression') return false
|
|
73
|
+
return node.properties.some((property: any) => {
|
|
74
|
+
if (property.type === 'Property')
|
|
75
|
+
return staticPropertyKey(property.key, property.computed === true) === name
|
|
76
|
+
if (property.type === 'SpreadElement')
|
|
77
|
+
return objectHasStaticProperty(property.argument, name, seen)
|
|
78
|
+
return false
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const noInlineStyles: RuleModule = {
|
|
83
|
+
meta: {
|
|
84
|
+
type: 'problem',
|
|
85
|
+
docs: {
|
|
86
|
+
description:
|
|
87
|
+
'Consumer components cannot set appearance or layout with JSX style props; use shared variants and token-backed classes.',
|
|
88
|
+
},
|
|
89
|
+
schema: [],
|
|
90
|
+
messages: {
|
|
91
|
+
inlineStyle:
|
|
92
|
+
'JSX style attributes are not permitted, including layout values and CSS custom properties. Use shared variants and token-backed classes.',
|
|
93
|
+
inlineStyleElement:
|
|
94
|
+
'A JSX style element bypasses the shared design system. Use shared variants and token-backed classes.',
|
|
95
|
+
inlineStyleSpread:
|
|
96
|
+
'A JSX spread contains a style prop. Pass named shared component props instead of hiding inline styles in a spread.',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
create(context) {
|
|
100
|
+
return {
|
|
101
|
+
JSXAttribute(node: any) {
|
|
102
|
+
if (node.name?.type === 'JSXIdentifier' && node.name.name === 'style')
|
|
103
|
+
context.report({ node, messageId: 'inlineStyle' })
|
|
104
|
+
},
|
|
105
|
+
JSXOpeningElement(node: any) {
|
|
106
|
+
if (node.name?.type === 'JSXIdentifier' && node.name.name === 'style')
|
|
107
|
+
context.report({ node, messageId: 'inlineStyleElement' })
|
|
108
|
+
},
|
|
109
|
+
JSXSpreadAttribute(node: any) {
|
|
110
|
+
if (objectHasStaticProperty(node.argument, 'style'))
|
|
111
|
+
context.report({ node, messageId: 'inlineStyleSpread' })
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const noClassList: RuleModule = {
|
|
118
|
+
meta: {
|
|
119
|
+
type: 'problem',
|
|
120
|
+
docs: {
|
|
121
|
+
description:
|
|
122
|
+
'Solid classList props hide conditional classes from static design-system lint; use cn() with its object-key form.',
|
|
123
|
+
},
|
|
124
|
+
schema: [],
|
|
125
|
+
messages: {
|
|
126
|
+
classList:
|
|
127
|
+
'classList hides classes from design-system lint. Use cn() with its object-key form and statically readable class names.',
|
|
128
|
+
classListSpread:
|
|
129
|
+
'A JSX spread contains classList. Use cn() with its object-key form instead of hiding classes in a spread.',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
create(context) {
|
|
133
|
+
return {
|
|
134
|
+
JSXAttribute(node: any) {
|
|
135
|
+
if (node.name?.type === 'JSXIdentifier' && node.name.name === 'classList')
|
|
136
|
+
context.report({ node, messageId: 'classList' })
|
|
137
|
+
},
|
|
138
|
+
JSXSpreadAttribute(node: any) {
|
|
139
|
+
if (objectHasStaticProperty(node.argument, 'classList'))
|
|
140
|
+
context.report({ node, messageId: 'classListSpread' })
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
}
|
|
145
|
+
|
|
53
146
|
interface RawElementOptions {
|
|
54
147
|
/** Element names this rule should not report (rare; prefer fixing the file). */
|
|
55
148
|
allow?: string[]
|
|
@@ -386,6 +479,8 @@ const noPrimitiveLibraryImports: RuleModule = {
|
|
|
386
479
|
const plugin = {
|
|
387
480
|
meta: { name: 'adea' },
|
|
388
481
|
rules: {
|
|
482
|
+
'no-inline-styles': noInlineStyles,
|
|
483
|
+
'no-class-list': noClassList,
|
|
389
484
|
'no-raw-interactive-elements': noRawInteractiveElements,
|
|
390
485
|
'no-primitive-library-imports': noPrimitiveLibraryImports,
|
|
391
486
|
'no-interactive-wrappers': noInteractiveWrappers,
|