@adea-ai/ui 0.83.1 → 0.85.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/README.md +79 -0
- package/dist/NOTICE +26 -0
- package/dist/components/composites/catalog-browser/catalog-browser-group.d.ts +20 -0
- package/dist/components/composites/catalog-browser/catalog-browser-group.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-browser-group.js +195 -0
- package/dist/components/composites/catalog-browser/catalog-browser-group.js.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-browser.d.ts +3 -0
- package/dist/components/composites/catalog-browser/catalog-browser.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-browser.js +378 -0
- package/dist/components/composites/catalog-browser/catalog-browser.js.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-detail.d.ts +21 -0
- package/dist/components/composites/catalog-browser/catalog-detail.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-detail.js +92 -0
- package/dist/components/composites/catalog-browser/catalog-detail.js.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-types.d.ts +78 -0
- package/dist/components/composites/catalog-browser/catalog-types.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/index.d.ts +4 -0
- package/dist/components/composites/catalog-browser/index.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/index.js +3 -0
- package/dist/components/layout/sidebar-nav/index.d.ts +1 -0
- package/dist/components/layout/sidebar-nav/index.d.ts.map +1 -1
- package/dist/components/layout/sidebar-nav/index.js +2 -1
- package/dist/components/layout/sidebar-nav/sidebar-nav-resize-handle.d.ts +21 -0
- package/dist/components/layout/sidebar-nav/sidebar-nav-resize-handle.d.ts.map +1 -0
- package/dist/components/layout/sidebar-nav/sidebar-nav-resize-handle.js +84 -0
- package/dist/components/layout/sidebar-nav/sidebar-nav-resize-handle.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/lint/index.d.ts +1 -0
- package/dist/lint/index.d.ts.map +1 -1
- package/dist/lint.js +171 -59
- package/dist/lint.js.map +1 -1
- package/dist/r/catalog-browser.json +47 -0
- package/dist/r/registry.json +53 -0
- package/dist/r/sidebar-nav.json +6 -0
- package/dist/r/src/components/composites/catalog-browser/catalog-browser-group.tsx +148 -0
- package/dist/r/src/components/composites/catalog-browser/catalog-browser.tsx +258 -0
- package/dist/r/src/components/composites/catalog-browser/catalog-detail.tsx +64 -0
- package/dist/r/src/components/composites/catalog-browser/catalog-types.ts +83 -0
- package/dist/r/src/components/composites/catalog-browser/index.ts +10 -0
- package/dist/r/src/components/layout/sidebar-nav/index.ts +4 -0
- package/dist/r/src/components/layout/sidebar-nav/sidebar-nav-resize-handle.tsx +59 -0
- package/package.json +1 -1
- package/registry.json +53 -0
- package/src/components/composites/catalog-browser/catalog-browser-group.tsx +148 -0
- package/src/components/composites/catalog-browser/catalog-browser.tsx +258 -0
- package/src/components/composites/catalog-browser/catalog-detail.tsx +64 -0
- package/src/components/composites/catalog-browser/catalog-types.ts +83 -0
- package/src/components/composites/catalog-browser/index.ts +10 -0
- package/src/components/layout/sidebar-nav/index.ts +4 -0
- package/src/components/layout/sidebar-nav/sidebar-nav-resize-handle.tsx +59 -0
- package/src/index.ts +1 -0
- package/src/lint/index.ts +168 -19
package/src/lint/index.ts
CHANGED
|
@@ -79,6 +79,28 @@ const objectHasStaticProperty = (node: any, name: string, seen = new Set<unknown
|
|
|
79
79
|
})
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/** Returns literal values for a statically named property in an object spread. */
|
|
83
|
+
const staticObjectPropertyValues = (node: any, name: string, seen = new Set<unknown>()): any[] => {
|
|
84
|
+
if (!node || seen.has(node)) return []
|
|
85
|
+
seen.add(node)
|
|
86
|
+
if (
|
|
87
|
+
node.type === 'TSAsExpression' ||
|
|
88
|
+
node.type === 'TSSatisfiesExpression' ||
|
|
89
|
+
node.type === 'TSNonNullExpression'
|
|
90
|
+
)
|
|
91
|
+
return staticObjectPropertyValues(node.expression, name, seen)
|
|
92
|
+
if (node.type !== 'ObjectExpression') return []
|
|
93
|
+
return node.properties.flatMap((property: any) => {
|
|
94
|
+
if (property.type === 'Property')
|
|
95
|
+
return staticPropertyKey(property.key, property.computed === true) === name
|
|
96
|
+
? [property.value]
|
|
97
|
+
: []
|
|
98
|
+
if (property.type === 'SpreadElement')
|
|
99
|
+
return staticObjectPropertyValues(property.argument, name, seen)
|
|
100
|
+
return []
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
const noInlineStyles: RuleModule = {
|
|
83
105
|
meta: {
|
|
84
106
|
type: 'problem',
|
|
@@ -297,6 +319,14 @@ const staticStrings = (node: any): string[] => {
|
|
|
297
319
|
|
|
298
320
|
const roleOf = (node: any): string | null => {
|
|
299
321
|
for (const attribute of node.attributes ?? []) {
|
|
322
|
+
if (attribute.type === 'JSXSpreadAttribute') {
|
|
323
|
+
const role = staticObjectPropertyValues(attribute.argument, 'role')
|
|
324
|
+
.flatMap((value) => staticStrings(value))
|
|
325
|
+
.flatMap((value) => value.split(/\s+/))
|
|
326
|
+
.find((value) => value in ROLE_PRIMITIVES)
|
|
327
|
+
if (role) return role
|
|
328
|
+
continue
|
|
329
|
+
}
|
|
300
330
|
if (attribute.type !== 'JSXAttribute' || attribute.name?.name !== 'role') continue
|
|
301
331
|
return (
|
|
302
332
|
staticStrings(attribute.value)
|
|
@@ -311,31 +341,37 @@ const roleOf = (node: any): string | null => {
|
|
|
311
341
|
const has = (node: any, name: string): boolean =>
|
|
312
342
|
(node.attributes ?? []).some(
|
|
313
343
|
(attribute: any) =>
|
|
314
|
-
attribute.type === 'JSXAttribute' &&
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
344
|
+
(attribute.type === 'JSXAttribute' &&
|
|
345
|
+
(attribute.name?.type === 'JSXNamespacedName'
|
|
346
|
+
? `${attribute.name.namespace.name}:${attribute.name.name.name}`
|
|
347
|
+
: attribute.name?.name) === name) ||
|
|
348
|
+
(attribute.type === 'JSXSpreadAttribute' && objectHasStaticProperty(attribute.argument, name))
|
|
318
349
|
)
|
|
319
350
|
|
|
351
|
+
const tabStopValue = (value: any): boolean => {
|
|
352
|
+
const expression = value?.type === 'JSXExpressionContainer' ? value.expression : value
|
|
353
|
+
if (expression?.type === 'Literal') return !(Number(expression.value) < 0)
|
|
354
|
+
if (
|
|
355
|
+
expression?.type === 'UnaryExpression' &&
|
|
356
|
+
expression.operator === '-' &&
|
|
357
|
+
expression.argument?.type === 'Literal'
|
|
358
|
+
)
|
|
359
|
+
return !(Number(expression.argument.value) > 0)
|
|
360
|
+
return true
|
|
361
|
+
}
|
|
362
|
+
|
|
320
363
|
const hasTabStop = (node: any): boolean =>
|
|
321
364
|
(node.attributes ?? []).some((attribute: any) => {
|
|
322
365
|
if (
|
|
323
|
-
attribute.type
|
|
324
|
-
|
|
325
|
-
)
|
|
326
|
-
return false
|
|
327
|
-
const value =
|
|
328
|
-
attribute.value?.type === 'JSXExpressionContainer'
|
|
329
|
-
? attribute.value.expression
|
|
330
|
-
: attribute.value
|
|
331
|
-
if (value?.type === 'Literal') return !(Number(value.value) < 0)
|
|
332
|
-
if (
|
|
333
|
-
value?.type === 'UnaryExpression' &&
|
|
334
|
-
value.operator === '-' &&
|
|
335
|
-
value.argument?.type === 'Literal'
|
|
366
|
+
attribute.type === 'JSXAttribute' &&
|
|
367
|
+
['tabIndex', 'tabindex'].includes(attribute.name?.name)
|
|
336
368
|
)
|
|
337
|
-
return
|
|
338
|
-
|
|
369
|
+
return tabStopValue(attribute.value)
|
|
370
|
+
if (attribute.type === 'JSXSpreadAttribute')
|
|
371
|
+
return ['tabIndex', 'tabindex'].some((name) =>
|
|
372
|
+
staticObjectPropertyValues(attribute.argument, name).some(tabStopValue)
|
|
373
|
+
)
|
|
374
|
+
return false
|
|
339
375
|
})
|
|
340
376
|
|
|
341
377
|
const noInteractiveWrappers: RuleModule = {
|
|
@@ -415,6 +451,118 @@ const noInteractiveWrappers: RuleModule = {
|
|
|
415
451
|
},
|
|
416
452
|
}
|
|
417
453
|
|
|
454
|
+
const BUTTON_MODULE = '@adea-ai/ui/components/ui/button'
|
|
455
|
+
const ACTION_BUTTON_MODULE = '@adea-ai/ui/components/composites/action-button'
|
|
456
|
+
const ICON_BUTTON_SIZES: ReadonlySet<string> = new Set([
|
|
457
|
+
'icon-2xs',
|
|
458
|
+
'icon-xs',
|
|
459
|
+
'icon-sm',
|
|
460
|
+
'icon-md',
|
|
461
|
+
'icon-lg',
|
|
462
|
+
'icon-xl',
|
|
463
|
+
])
|
|
464
|
+
|
|
465
|
+
const staticJsxValues = (value: any): any[] =>
|
|
466
|
+
value?.type === 'JSXExpressionContainer' ? [value.expression] : value ? [value] : []
|
|
467
|
+
|
|
468
|
+
const attributeValues = (node: any, name: string): any[] => {
|
|
469
|
+
const values: any[] = []
|
|
470
|
+
for (const attribute of node.attributes ?? []) {
|
|
471
|
+
if (attribute.type === 'JSXAttribute' && attribute.name?.name === name)
|
|
472
|
+
values.splice(0, values.length, ...staticJsxValues(attribute.value))
|
|
473
|
+
if (attribute.type === 'JSXSpreadAttribute') {
|
|
474
|
+
const spreadValues = staticObjectPropertyValues(attribute.argument, name)
|
|
475
|
+
if (spreadValues.length > 0) values.splice(0, values.length, spreadValues.at(-1))
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return values
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const staticStringValue = (node: any): string | null => {
|
|
482
|
+
if (node?.type === 'Literal' && typeof node.value === 'string') return node.value
|
|
483
|
+
if (node?.type === 'TemplateLiteral' && node.expressions.length === 0)
|
|
484
|
+
return node.quasis[0].value.cooked ?? node.quasis[0].value.raw
|
|
485
|
+
return null
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const isIconButtonSize = (node: any): boolean =>
|
|
489
|
+
attributeValues(node, 'size').some((value) => {
|
|
490
|
+
const size = staticStringValue(value)
|
|
491
|
+
return size !== null && ICON_BUTTON_SIZES.has(size)
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
const actionTooltipStatus = (node: any): 'valid' | 'blank' | 'unknown' | 'missing' => {
|
|
495
|
+
const values = attributeValues(node, 'tooltip')
|
|
496
|
+
if (values.length === 0) return 'missing'
|
|
497
|
+
const strings = values.map(staticStringValue)
|
|
498
|
+
if (strings.some((value) => value !== null && value.trim().length > 0)) return 'valid'
|
|
499
|
+
if (strings.every((value) => value !== null)) return 'blank'
|
|
500
|
+
return 'unknown'
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const isImportedComponent = (identifier: any, imports: Set<string>): boolean =>
|
|
504
|
+
identifier?.type === 'Identifier' && imports.has(identifier.name)
|
|
505
|
+
|
|
506
|
+
/** Requires shared icon actions to carry the shared explanatory-tooltip contract. */
|
|
507
|
+
const requireActionButtonTooltip: RuleModule = {
|
|
508
|
+
meta: {
|
|
509
|
+
type: 'problem',
|
|
510
|
+
docs: {
|
|
511
|
+
description:
|
|
512
|
+
'Consumer icon-size shared Button actions use ActionButton with a nonblank tooltip; dynamic values remain a review boundary.',
|
|
513
|
+
},
|
|
514
|
+
schema: [],
|
|
515
|
+
messages: {
|
|
516
|
+
useActionButton:
|
|
517
|
+
'Icon-size Button actions use ActionButton with a nonblank tooltip. Keep the control name in aria-label; the tooltip explains the action.',
|
|
518
|
+
missingTooltip:
|
|
519
|
+
'An icon-size ActionButton needs a supplied nonblank tooltip. Keep its accessible name in aria-label as well.',
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
create(context) {
|
|
523
|
+
const buttons = new Set<string>()
|
|
524
|
+
const actionButtons = new Set<string>()
|
|
525
|
+
const recordImports = (node: any) => {
|
|
526
|
+
const source = node.source?.value
|
|
527
|
+
const target =
|
|
528
|
+
source === BUTTON_MODULE ? buttons : source === ACTION_BUTTON_MODULE ? actionButtons : null
|
|
529
|
+
if (!target) return
|
|
530
|
+
for (const specifier of node.specifiers ?? []) {
|
|
531
|
+
if (specifier.type !== 'ImportSpecifier') continue
|
|
532
|
+
const imported = specifier.imported?.name ?? specifier.imported?.value
|
|
533
|
+
if (
|
|
534
|
+
(source === BUTTON_MODULE && imported === 'Button') ||
|
|
535
|
+
(source === ACTION_BUTTON_MODULE && imported === 'ActionButton')
|
|
536
|
+
)
|
|
537
|
+
target.add(specifier.local?.name)
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return {
|
|
542
|
+
ImportDeclaration: recordImports,
|
|
543
|
+
JSXOpeningElement(node: any) {
|
|
544
|
+
if (!isIconButtonSize(node)) return
|
|
545
|
+
const name = node.name?.type === 'JSXIdentifier' ? node.name.name : null
|
|
546
|
+
const isButton = name !== null && buttons.has(name)
|
|
547
|
+
const isActionButton = name !== null && actionButtons.has(name)
|
|
548
|
+
const asValues = attributeValues(node, 'as')
|
|
549
|
+
const targetsButton = asValues.some((value) => isImportedComponent(value, buttons))
|
|
550
|
+
const targetsActionButton = asValues.some((value) =>
|
|
551
|
+
isImportedComponent(value, actionButtons)
|
|
552
|
+
)
|
|
553
|
+
if (isButton || targetsButton) {
|
|
554
|
+
context.report({ node, messageId: 'useActionButton' })
|
|
555
|
+
return
|
|
556
|
+
}
|
|
557
|
+
if (!isActionButton && !targetsActionButton) return
|
|
558
|
+
const status = actionTooltipStatus(node)
|
|
559
|
+
if (status === 'missing' || status === 'blank')
|
|
560
|
+
context.report({ node, messageId: 'missingTooltip' })
|
|
561
|
+
},
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
}
|
|
565
|
+
|
|
418
566
|
const noPrimitiveLibraryImports: RuleModule = {
|
|
419
567
|
meta: {
|
|
420
568
|
type: 'problem',
|
|
@@ -484,6 +632,7 @@ const plugin = {
|
|
|
484
632
|
'no-raw-interactive-elements': noRawInteractiveElements,
|
|
485
633
|
'no-primitive-library-imports': noPrimitiveLibraryImports,
|
|
486
634
|
'no-interactive-wrappers': noInteractiveWrappers,
|
|
635
|
+
'require-action-button-tooltip': requireActionButtonTooltip,
|
|
487
636
|
},
|
|
488
637
|
}
|
|
489
638
|
|