@adea-ai/ui 0.94.0 → 0.95.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 +43 -1
- package/dist/NOTICE +10 -0
- package/dist/components/composites/action-button/action-button.d.ts.map +1 -1
- package/dist/components/composites/action-button/action-button.js +2 -0
- package/dist/components/composites/action-button/action-button.js.map +1 -1
- package/dist/components/composites/scene-controls/index.d.ts +3 -0
- package/dist/components/composites/scene-controls/index.d.ts.map +1 -0
- package/dist/components/composites/scene-controls/index.js +2 -0
- package/dist/components/composites/scene-controls/scene-controls.d.ts +41 -0
- package/dist/components/composites/scene-controls/scene-controls.d.ts.map +1 -0
- package/dist/components/composites/scene-controls/scene-controls.js +291 -0
- package/dist/components/composites/scene-controls/scene-controls.js.map +1 -0
- package/dist/components/ui/button/button.d.ts +2 -1
- package/dist/components/ui/button/button.d.ts.map +1 -1
- package/dist/components/ui/button/button.js +9 -3
- package/dist/components/ui/button/button.js.map +1 -1
- package/dist/components/ui/toggle/toggle.d.ts +1 -1
- package/dist/components/ui/toggle/toggle.d.ts.map +1 -1
- package/dist/components/ui/toggle/toggle.js +3 -1
- package/dist/components/ui/toggle/toggle.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/lib/tokens.d.ts.map +1 -1
- package/dist/lib/tokens.js +10 -0
- package/dist/lib/tokens.js.map +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +2 -0
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/variants.d.ts +5 -2
- package/dist/lib/variants.d.ts.map +1 -1
- package/dist/lib/variants.js +7 -3
- package/dist/lib/variants.js.map +1 -1
- package/dist/lint/index.d.ts.map +1 -1
- package/dist/lint.js +2 -1
- package/dist/lint.js.map +1 -1
- package/dist/r/registry.json +24 -0
- package/dist/r/scene-controls.json +24 -0
- package/dist/r/src/components/composites/action-button/action-button.tsx +2 -0
- package/dist/r/src/components/composites/scene-controls/index.ts +7 -0
- package/dist/r/src/components/composites/scene-controls/scene-controls.tsx +282 -0
- package/dist/r/src/components/ui/button/button.tsx +20 -2
- package/dist/r/src/components/ui/toggle/toggle.tsx +2 -0
- package/dist/r/src/lib/tokens.ts +10 -0
- package/dist/r/src/lib/utils.ts +2 -0
- package/dist/r/src/lib/variants.ts +5 -2
- package/dist/r/src/styles/base.css +29 -9
- package/dist/r/src/styles/theme.css +8 -2
- package/package.json +2 -1
- package/registry.json +24 -0
- package/src/components/composites/action-button/action-button.tsx +2 -0
- package/src/components/composites/scene-controls/index.ts +7 -0
- package/src/components/composites/scene-controls/scene-controls.tsx +282 -0
- package/src/components/ui/button/button.tsx +20 -2
- package/src/components/ui/toggle/toggle.tsx +2 -0
- package/src/index.ts +1 -0
- package/src/lib/tokens.ts +10 -0
- package/src/lib/utils.ts +2 -0
- package/src/lib/variants.ts +5 -2
- package/src/lint/index.ts +1 -0
- package/src/styles/base.css +29 -9
- package/src/styles/theme.css +8 -2
package/dist/lint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint.js","names":[],"sources":["../src/lint/index.ts"],"sourcesContent":["/*\n * The design system's own oxlint plugin.\n *\n * The styling rules live in `@shadcn/lint`; what that plugin cannot know is which\n * *elements* the design system owns. This is the other half of the contract, for\n * the applications that consume the library: an interactive element this package\n * provides is not re-implemented at the application layer, and the primitive\n * libraries this package is built from are not imported around it.\n *\n * Loaded as an oxlint jsPlugin:\n *\n * // .oxlintrc.json\n * { \"jsPlugins\": [\"@adea-ai/ui/lint\"], \"rules\": { \"adea/no-raw-interactive-elements\": \"error\" } }\n *\n * Rules are errors by intent. A violation means an application composed its own\n * control out of raw markup — unreachable focus behaviour, an absent keyboard\n * story, a second implementation of a decision this package exists to make once.\n */\n\ntype Json = string | number | boolean | null | Json[] | { [key: string]: Json }\n\n/** The raw elements this package replaces, and the primitive that replaces them. */\nconst ELEMENT_PRIMITIVES: Readonly<Record<string, { primitive: string; path: string }>> =\n Object.freeze({\n button: { primitive: 'Button', path: '@adea-ai/ui/components/ui/button' },\n input: { primitive: 'Input', path: '@adea-ai/ui/components/ui/input' },\n textarea: { primitive: 'Textarea', path: '@adea-ai/ui/components/ui/textarea' },\n select: { primitive: 'Select', path: '@adea-ai/ui/components/ui/select' },\n option: { primitive: 'Select', path: '@adea-ai/ui/components/ui/select' },\n label: { primitive: 'Label', path: '@adea-ai/ui/components/ui/label' },\n })\n\ntype RuleModule = {\n meta: {\n type: 'problem'\n docs: { description: string }\n schema: Json[]\n messages: Record<string, string>\n }\n create: (context: LintContext) => Record<string, (node: any) => void>\n}\n\ninterface LintContext {\n options?: unknown[]\n report: (descriptor: {\n node: unknown\n messageId?: string\n message?: string\n data?: Record<string, Json>\n }) => void\n}\n\n/** Reads a static object-property key without evaluating a computed expression. */\nconst staticPropertyKey = (node: any, computed: boolean): string | null => {\n if (!computed && node?.type === 'Identifier') return node.name\n if (node?.type === 'Literal' && typeof node.value === 'string') return node.value\n if (node?.type === 'TemplateLiteral' && node.expressions.length === 0)\n return node.quasis[0].value.cooked ?? node.quasis[0].value.raw\n return null\n}\n\n/** Detects named props in a literal JSX spread, including nested literal spreads. */\nconst objectHasStaticProperty = (node: any, name: string, seen = new Set<unknown>()): boolean => {\n if (!node || seen.has(node)) return false\n seen.add(node)\n if (\n node.type === 'TSAsExpression' ||\n node.type === 'TSSatisfiesExpression' ||\n node.type === 'TSNonNullExpression'\n )\n return objectHasStaticProperty(node.expression, name, seen)\n if (node.type !== 'ObjectExpression') return false\n return node.properties.some((property: any) => {\n if (property.type === 'Property')\n return staticPropertyKey(property.key, property.computed === true) === name\n if (property.type === 'SpreadElement')\n return objectHasStaticProperty(property.argument, name, seen)\n return false\n })\n}\n\n/** Returns literal values for a statically named property in an object spread. */\nconst staticObjectPropertyValues = (node: any, name: string, seen = new Set<unknown>()): any[] => {\n if (!node || seen.has(node)) return []\n seen.add(node)\n if (\n node.type === 'TSAsExpression' ||\n node.type === 'TSSatisfiesExpression' ||\n node.type === 'TSNonNullExpression'\n )\n return staticObjectPropertyValues(node.expression, name, seen)\n if (node.type !== 'ObjectExpression') return []\n return node.properties.flatMap((property: any) => {\n if (property.type === 'Property')\n return staticPropertyKey(property.key, property.computed === true) === name\n ? [property.value]\n : []\n if (property.type === 'SpreadElement')\n return staticObjectPropertyValues(property.argument, name, seen)\n return []\n })\n}\n\nconst noInlineStyles: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Consumer components cannot set appearance or layout with JSX style props; use shared variants and token-backed classes.',\n },\n schema: [],\n messages: {\n inlineStyle:\n 'JSX style attributes are not permitted, including layout values and CSS custom properties. Use shared variants and token-backed classes.',\n inlineStyleElement:\n 'A JSX style element bypasses the shared design system. Use shared variants and token-backed classes.',\n inlineStyleSpread:\n 'A JSX spread contains a style prop. Pass named shared component props instead of hiding inline styles in a spread.',\n },\n },\n create(context) {\n return {\n JSXAttribute(node: any) {\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'style')\n context.report({ node, messageId: 'inlineStyle' })\n },\n JSXOpeningElement(node: any) {\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'style')\n context.report({ node, messageId: 'inlineStyleElement' })\n },\n JSXSpreadAttribute(node: any) {\n if (objectHasStaticProperty(node.argument, 'style'))\n context.report({ node, messageId: 'inlineStyleSpread' })\n },\n }\n },\n}\n\nconst noClassList: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Solid classList props hide conditional classes from static design-system lint; use cn() with its object-key form.',\n },\n schema: [],\n messages: {\n classList:\n 'classList hides classes from design-system lint. Use cn() with its object-key form and statically readable class names.',\n classListSpread:\n 'A JSX spread contains classList. Use cn() with its object-key form instead of hiding classes in a spread.',\n },\n },\n create(context) {\n return {\n JSXAttribute(node: any) {\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'classList')\n context.report({ node, messageId: 'classList' })\n },\n JSXSpreadAttribute(node: any) {\n if (objectHasStaticProperty(node.argument, 'classList'))\n context.report({ node, messageId: 'classListSpread' })\n },\n }\n },\n}\n\ninterface RawElementOptions {\n /** Element names this rule should not report (rare; prefer fixing the file). */\n allow?: string[]\n /** Replaces the default report message. */\n message?: string\n}\n\nconst noRawInteractiveElements: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Interactive elements this package provides (button, input, textarea, select, option, label) are composed from the package, not written as raw markup.',\n },\n schema: [\n {\n type: 'object',\n properties: {\n allow: { type: 'array', items: { type: 'string' } },\n message: { type: 'string' },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n rawElement:\n '{{element}} is provided by the design system: use {{primitive}} from {{path}}. A raw element has no keyboard story, no focus behaviour and no token styling — that is the work the primitive already did.',\n },\n },\n create(context) {\n const options = (context.options?.[0] ?? {}) as RawElementOptions\n const allowed = new Set(options.allow ?? [])\n const emit = (node: unknown, element: string) => {\n const target = ELEMENT_PRIMITIVES[element]!\n context.report({\n node,\n messageId: 'rawElement',\n data: { element, primitive: target.primitive, path: target.path },\n ...(options.message ? { message: options.message } : {}),\n })\n }\n return {\n JSXOpeningElement(node: any) {\n if (node.name?.type !== 'JSXIdentifier') return\n const element = node.name.name\n if (!(element in ELEMENT_PRIMITIVES) || allowed.has(element)) return\n emit(node, element)\n },\n }\n },\n}\n\ninterface PrimitiveImportOptions {\n /** Package prefixes this rule reports. Defaults to the primitive libraries the ui package wraps. */\n packages?: string[]\n /** Replaces the default report message. */\n message?: string\n}\n\nconst DEFAULT_PRIMITIVE_PACKAGES = Object.freeze([\n '@kobalte/core',\n '@corvu',\n 'cmdk-solid',\n '@ark-ui',\n '@zag-js',\n '@radix-ui',\n 'radix-ui',\n '@base-ui-components',\n '@base-ui',\n])\n\n/**\n * The interactive ARIA roles an application can dress a generic element in, and\n * the primitive that owns each role. `link` is deliberately absent: an `<a>` is\n * natively interactive and Button itself supports `as=\"a\"` for the link-styled\n * case.\n */\nconst ROLE_PRIMITIVES: Readonly<Record<string, { primitive: string; path: string }>> =\n Object.freeze({\n button: { primitive: 'Button', path: '@adea-ai/ui/components/ui/button' },\n checkbox: { primitive: 'Checkbox', path: '@adea-ai/ui/components/ui/checkbox' },\n radio: { primitive: 'RadioGroup', path: '@adea-ai/ui/components/ui/radio-group' },\n switch: { primitive: 'Switch', path: '@adea-ai/ui/components/ui/switch' },\n tab: { primitive: 'Tabs', path: '@adea-ai/ui/components/ui/tabs' },\n option: { primitive: 'Select', path: '@adea-ai/ui/components/ui/select' },\n menuitem: { primitive: 'DropdownMenu', path: '@adea-ai/ui/components/ui/dropdown-menu' },\n textbox: { primitive: 'Input', path: '@adea-ai/ui/components/ui/input' },\n })\n\n/**\n * The elements whose only job is layout or text. A click handler or a tabindex on\n * one of these is a control being hand-built out of a non-control.\n */\nconst GENERIC_ELEMENTS: ReadonlySet<string> = new Set([\n 'div',\n 'span',\n 'li',\n 'ul',\n 'ol',\n 'td',\n 'th',\n 'p',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'section',\n 'article',\n 'aside',\n 'header',\n 'footer',\n 'nav',\n 'main',\n 'img',\n 'svg',\n])\n\nconst CLICK_HANDLERS: ReadonlySet<string> = new Set([\n 'onClick',\n 'onDoubleClick',\n 'onPointerDown',\n 'onPointerUp',\n 'onMouseDown',\n 'onMouseUp',\n 'on:click',\n 'on:dblclick',\n 'on:pointerdown',\n 'on:pointerup',\n 'on:mousedown',\n 'on:mouseup',\n])\n\ninterface InteractiveWrapperOptions {\n /** Element names this rule should not report (rare; prefer fixing the file). */\n allow?: string[]\n /** Replaces the default report message. */\n message?: string\n}\n\n/** Only statically knowable branches; unresolved roles remain a review boundary. */\nconst staticStrings = (node: any): string[] => {\n if (node?.type === 'Literal' && typeof node.value === 'string') return [node.value]\n if (node?.type === 'JSXExpressionContainer') return staticStrings(node.expression)\n if (node?.type === 'ConditionalExpression')\n return [...staticStrings(node.consequent), ...staticStrings(node.alternate)]\n if (node?.type === 'TemplateLiteral' && node.expressions.length === 0)\n return [node.quasis[0].value.cooked ?? node.quasis[0].value.raw]\n return []\n}\n\nconst roleOf = (node: any): string | null => {\n for (const attribute of node.attributes ?? []) {\n if (attribute.type === 'JSXSpreadAttribute') {\n const role = staticObjectPropertyValues(attribute.argument, 'role')\n .flatMap((value) => staticStrings(value))\n .flatMap((value) => value.split(/\\s+/))\n .find((value) => value in ROLE_PRIMITIVES)\n if (role) return role\n continue\n }\n if (attribute.type !== 'JSXAttribute' || attribute.name?.name !== 'role') continue\n return (\n staticStrings(attribute.value)\n .flatMap((role) => role.split(/\\s+/))\n .find((role) => role in ROLE_PRIMITIVES) ?? null\n )\n }\n return null\n}\n\n/** Whether the element carries the attribute, by name. */\nconst has = (node: any, name: string): boolean =>\n (node.attributes ?? []).some(\n (attribute: any) =>\n (attribute.type === 'JSXAttribute' &&\n (attribute.name?.type === 'JSXNamespacedName'\n ? `${attribute.name.namespace.name}:${attribute.name.name.name}`\n : attribute.name?.name) === name) ||\n (attribute.type === 'JSXSpreadAttribute' && objectHasStaticProperty(attribute.argument, name))\n )\n\nconst tabStopValue = (value: any): boolean => {\n const expression = value?.type === 'JSXExpressionContainer' ? value.expression : value\n if (expression?.type === 'Literal') return !(Number(expression.value) < 0)\n if (\n expression?.type === 'UnaryExpression' &&\n expression.operator === '-' &&\n expression.argument?.type === 'Literal'\n )\n return !(Number(expression.argument.value) > 0)\n return true\n}\n\nconst hasTabStop = (node: any): boolean =>\n (node.attributes ?? []).some((attribute: any) => {\n if (\n attribute.type === 'JSXAttribute' &&\n ['tabIndex', 'tabindex'].includes(attribute.name?.name)\n )\n return tabStopValue(attribute.value)\n if (attribute.type === 'JSXSpreadAttribute')\n return ['tabIndex', 'tabindex'].some((name) =>\n staticObjectPropertyValues(attribute.argument, name).some(tabStopValue)\n )\n return false\n })\n\nconst noInteractiveWrappers: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Generic elements dressed as interactive controls — an interactive role, a click handler or a tabindex on a layout element — re-implement a primitive by hand. Compose the real component.',\n },\n schema: [\n {\n type: 'object',\n properties: {\n allow: { type: 'array', items: { type: 'string' } },\n message: { type: 'string' },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n roleWrapper:\n 'A {{element}} with role=\"{{role}}\" hand-builds {{primitive}}: use {{primitive}} from {{path}}. The primitive brings keyboard activation, focus behaviour and ARIA wiring; the wrapper re-implements them badly.',\n clickWrapper:\n 'A click handler on {{element}} composes {{primitive}} by hand: use {{primitive}} from {{path}}. A div that reacts to clicks is a button that cannot be reached by keyboard.',\n tabindexWrapper:\n 'A tabindex on {{element}} makes a layout element focusable, which is focus management the primitives already own: compose the real component instead.',\n },\n },\n create(context) {\n const options = (context.options?.[0] ?? {}) as InteractiveWrapperOptions\n const allowed = new Set(options.allow ?? [])\n return {\n JSXOpeningElement(node: any) {\n if (node.name?.type !== 'JSXIdentifier') return\n const element = node.name.name\n if (allowed.has(element) || element in ELEMENT_PRIMITIVES) return\n // A capitalized element is a component — often a design-system primitive\n // composing a role deliberately (a Button carrying role=\"option\" inside a\n // custom listbox, for example). That is composition, not impersonation.\n if (element[0] === element[0]?.toUpperCase()) return\n\n const role = roleOf(node)\n if (role && role in ROLE_PRIMITIVES) {\n const target = ROLE_PRIMITIVES[role]!\n context.report({\n node,\n messageId: 'roleWrapper',\n data: { element, role, primitive: target.primitive, path: target.path },\n ...(options.message ? { message: options.message } : {}),\n })\n return\n }\n\n if (GENERIC_ELEMENTS.has(element)) {\n const click = [...CLICK_HANDLERS].find((handler) => has(node, handler))\n if (click) {\n const target = ROLE_PRIMITIVES.button!\n context.report({\n node,\n messageId: 'clickWrapper',\n data: { element, primitive: target.primitive, path: target.path },\n ...(options.message ? { message: options.message } : {}),\n })\n return\n }\n if (hasTabStop(node)) {\n context.report({\n node,\n messageId: 'tabindexWrapper',\n data: { element },\n ...(options.message ? { message: options.message } : {}),\n })\n }\n }\n },\n }\n },\n}\n\nconst BUTTON_MODULE = '@adea-ai/ui/components/ui/button'\nconst ACTION_BUTTON_MODULE = '@adea-ai/ui/components/composites/action-button'\nconst ICON_BUTTON_SIZES: ReadonlySet<string> = new Set([\n 'icon-2xs',\n 'icon-xs',\n 'icon-sm',\n 'icon-md',\n 'icon-lg',\n 'icon-xl',\n])\n\nconst staticJsxValues = (value: any): any[] =>\n value?.type === 'JSXExpressionContainer' ? [value.expression] : value ? [value] : []\n\nconst attributeValues = (node: any, name: string): any[] => {\n const values: any[] = []\n for (const attribute of node.attributes ?? []) {\n if (attribute.type === 'JSXAttribute' && attribute.name?.name === name)\n values.splice(0, values.length, ...staticJsxValues(attribute.value))\n if (attribute.type === 'JSXSpreadAttribute') {\n const spreadValues = staticObjectPropertyValues(attribute.argument, name)\n if (spreadValues.length > 0) values.splice(0, values.length, spreadValues.at(-1))\n }\n }\n return values\n}\n\nconst staticStringValue = (node: any): string | null => {\n if (node?.type === 'Literal' && typeof node.value === 'string') return node.value\n if (node?.type === 'TemplateLiteral' && node.expressions.length === 0)\n return node.quasis[0].value.cooked ?? node.quasis[0].value.raw\n return null\n}\n\nconst isIconButtonSize = (node: any): boolean =>\n attributeValues(node, 'size').some((value) => {\n const size = staticStringValue(value)\n return size !== null && ICON_BUTTON_SIZES.has(size)\n })\n\nconst actionTooltipStatus = (node: any): 'valid' | 'blank' | 'unknown' | 'missing' => {\n const values = attributeValues(node, 'tooltip')\n if (values.length === 0) return 'missing'\n const strings = values.map(staticStringValue)\n if (strings.some((value) => value !== null && value.trim().length > 0)) return 'valid'\n if (strings.every((value) => value !== null)) return 'blank'\n return 'unknown'\n}\n\nconst isImportedComponent = (identifier: any, imports: Set<string>): boolean =>\n identifier?.type === 'Identifier' && imports.has(identifier.name)\n\n/** Requires shared icon actions to carry the shared explanatory-tooltip contract. */\nconst requireActionButtonTooltip: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Consumer icon-size shared Button actions use ActionButton with a nonblank tooltip; dynamic values remain a review boundary.',\n },\n schema: [],\n messages: {\n useActionButton:\n 'Icon-size Button actions use ActionButton with a nonblank tooltip. Keep the control name in aria-label; the tooltip explains the action.',\n missingTooltip:\n 'An icon-size ActionButton needs a supplied nonblank tooltip. Keep its accessible name in aria-label as well.',\n },\n },\n create(context) {\n const buttons = new Set<string>()\n const actionButtons = new Set<string>()\n const recordImports = (node: any) => {\n const source = node.source?.value\n const target =\n source === BUTTON_MODULE ? buttons : source === ACTION_BUTTON_MODULE ? actionButtons : null\n if (!target) return\n for (const specifier of node.specifiers ?? []) {\n if (specifier.type !== 'ImportSpecifier') continue\n const imported = specifier.imported?.name ?? specifier.imported?.value\n if (\n (source === BUTTON_MODULE && imported === 'Button') ||\n (source === ACTION_BUTTON_MODULE && imported === 'ActionButton')\n )\n target.add(specifier.local?.name)\n }\n }\n\n return {\n ImportDeclaration: recordImports,\n JSXOpeningElement(node: any) {\n if (!isIconButtonSize(node)) return\n const name = node.name?.type === 'JSXIdentifier' ? node.name.name : null\n const isButton = name !== null && buttons.has(name)\n const isActionButton = name !== null && actionButtons.has(name)\n const asValues = attributeValues(node, 'as')\n const targetsButton = asValues.some((value) => isImportedComponent(value, buttons))\n const targetsActionButton = asValues.some((value) =>\n isImportedComponent(value, actionButtons)\n )\n if (isButton || targetsButton) {\n context.report({ node, messageId: 'useActionButton' })\n return\n }\n if (!isActionButton && !targetsActionButton) return\n const status = actionTooltipStatus(node)\n if (status === 'missing' || status === 'blank')\n context.report({ node, messageId: 'missingTooltip' })\n },\n }\n },\n}\n\nconst noPrimitiveLibraryImports: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'The primitive libraries the design system is built on (Kobalte and friends) are its internal affair — compose the exported components instead of importing the primitives.',\n },\n schema: [\n {\n type: 'object',\n properties: {\n packages: { type: 'array', items: { type: 'string' } },\n message: { type: 'string' },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n primitiveImport:\n 'Import from the design system, not from {{package}}: the primitives it wraps carry the focus, keyboard and ARIA behaviour the components exist to provide.',\n },\n },\n create(context) {\n const options = (context.options?.[0] ?? {}) as PrimitiveImportOptions\n const prefixes = options.packages ?? DEFAULT_PRIMITIVE_PACKAGES\n const offender = (source: unknown): string | null => {\n const name = String(source ?? '')\n return prefixes.find((prefix) => name === prefix || name.startsWith(`${prefix}/`)) ?? null\n }\n const report = (node: unknown, source: unknown) => {\n const pkg = offender(source)!\n context.report({\n node,\n messageId: 'primitiveImport',\n data: { package: pkg },\n ...(options.message ? { message: options.message } : {}),\n })\n }\n return {\n ImportDeclaration(node: any) {\n if (offender(node.source?.value)) report(node, node.source.value)\n },\n ImportExpression(node: any) {\n if (typeof node.source?.value === 'string' && offender(node.source.value))\n report(node, node.source.value)\n },\n ExportNamedDeclaration(node: any) {\n if (offender(node.source?.value)) report(node, node.source.value)\n },\n ExportAllDeclaration(node: any) {\n if (offender(node.source?.value)) report(node, node.source.value)\n },\n CallExpression(node: any) {\n if (node.callee?.type !== 'Identifier' || node.callee.name !== 'require') return\n const source = node.arguments?.[0]?.value\n if (typeof source === 'string' && offender(source)) report(node, source)\n },\n }\n },\n}\n\nconst plugin = {\n meta: { name: 'adea' },\n rules: {\n 'no-inline-styles': noInlineStyles,\n 'no-class-list': noClassList,\n 'no-raw-interactive-elements': noRawInteractiveElements,\n 'no-primitive-library-imports': noPrimitiveLibraryImports,\n 'no-interactive-wrappers': noInteractiveWrappers,\n 'require-action-button-tooltip': requireActionButtonTooltip,\n },\n}\n\nexport default plugin\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lint.js","names":[],"sources":["../src/lint/index.ts"],"sourcesContent":["/*\n * The design system's own oxlint plugin.\n *\n * The styling rules live in `@shadcn/lint`; what that plugin cannot know is which\n * *elements* the design system owns. This is the other half of the contract, for\n * the applications that consume the library: an interactive element this package\n * provides is not re-implemented at the application layer, and the primitive\n * libraries this package is built from are not imported around it.\n *\n * Loaded as an oxlint jsPlugin:\n *\n * // .oxlintrc.json\n * { \"jsPlugins\": [\"@adea-ai/ui/lint\"], \"rules\": { \"adea/no-raw-interactive-elements\": \"error\" } }\n *\n * Rules are errors by intent. A violation means an application composed its own\n * control out of raw markup — unreachable focus behaviour, an absent keyboard\n * story, a second implementation of a decision this package exists to make once.\n */\n\ntype Json = string | number | boolean | null | Json[] | { [key: string]: Json }\n\n/** The raw elements this package replaces, and the primitive that replaces them. */\nconst ELEMENT_PRIMITIVES: Readonly<Record<string, { primitive: string; path: string }>> =\n Object.freeze({\n button: { primitive: 'Button', path: '@adea-ai/ui/components/ui/button' },\n input: { primitive: 'Input', path: '@adea-ai/ui/components/ui/input' },\n textarea: { primitive: 'Textarea', path: '@adea-ai/ui/components/ui/textarea' },\n select: { primitive: 'Select', path: '@adea-ai/ui/components/ui/select' },\n option: { primitive: 'Select', path: '@adea-ai/ui/components/ui/select' },\n label: { primitive: 'Label', path: '@adea-ai/ui/components/ui/label' },\n })\n\ntype RuleModule = {\n meta: {\n type: 'problem'\n docs: { description: string }\n schema: Json[]\n messages: Record<string, string>\n }\n create: (context: LintContext) => Record<string, (node: any) => void>\n}\n\ninterface LintContext {\n options?: unknown[]\n report: (descriptor: {\n node: unknown\n messageId?: string\n message?: string\n data?: Record<string, Json>\n }) => void\n}\n\n/** Reads a static object-property key without evaluating a computed expression. */\nconst staticPropertyKey = (node: any, computed: boolean): string | null => {\n if (!computed && node?.type === 'Identifier') return node.name\n if (node?.type === 'Literal' && typeof node.value === 'string') return node.value\n if (node?.type === 'TemplateLiteral' && node.expressions.length === 0)\n return node.quasis[0].value.cooked ?? node.quasis[0].value.raw\n return null\n}\n\n/** Detects named props in a literal JSX spread, including nested literal spreads. */\nconst objectHasStaticProperty = (node: any, name: string, seen = new Set<unknown>()): boolean => {\n if (!node || seen.has(node)) return false\n seen.add(node)\n if (\n node.type === 'TSAsExpression' ||\n node.type === 'TSSatisfiesExpression' ||\n node.type === 'TSNonNullExpression'\n )\n return objectHasStaticProperty(node.expression, name, seen)\n if (node.type !== 'ObjectExpression') return false\n return node.properties.some((property: any) => {\n if (property.type === 'Property')\n return staticPropertyKey(property.key, property.computed === true) === name\n if (property.type === 'SpreadElement')\n return objectHasStaticProperty(property.argument, name, seen)\n return false\n })\n}\n\n/** Returns literal values for a statically named property in an object spread. */\nconst staticObjectPropertyValues = (node: any, name: string, seen = new Set<unknown>()): any[] => {\n if (!node || seen.has(node)) return []\n seen.add(node)\n if (\n node.type === 'TSAsExpression' ||\n node.type === 'TSSatisfiesExpression' ||\n node.type === 'TSNonNullExpression'\n )\n return staticObjectPropertyValues(node.expression, name, seen)\n if (node.type !== 'ObjectExpression') return []\n return node.properties.flatMap((property: any) => {\n if (property.type === 'Property')\n return staticPropertyKey(property.key, property.computed === true) === name\n ? [property.value]\n : []\n if (property.type === 'SpreadElement')\n return staticObjectPropertyValues(property.argument, name, seen)\n return []\n })\n}\n\nconst noInlineStyles: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Consumer components cannot set appearance or layout with JSX style props; use shared variants and token-backed classes.',\n },\n schema: [],\n messages: {\n inlineStyle:\n 'JSX style attributes are not permitted, including layout values and CSS custom properties. Use shared variants and token-backed classes.',\n inlineStyleElement:\n 'A JSX style element bypasses the shared design system. Use shared variants and token-backed classes.',\n inlineStyleSpread:\n 'A JSX spread contains a style prop. Pass named shared component props instead of hiding inline styles in a spread.',\n },\n },\n create(context) {\n return {\n JSXAttribute(node: any) {\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'style')\n context.report({ node, messageId: 'inlineStyle' })\n },\n JSXOpeningElement(node: any) {\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'style')\n context.report({ node, messageId: 'inlineStyleElement' })\n },\n JSXSpreadAttribute(node: any) {\n if (objectHasStaticProperty(node.argument, 'style'))\n context.report({ node, messageId: 'inlineStyleSpread' })\n },\n }\n },\n}\n\nconst noClassList: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Solid classList props hide conditional classes from static design-system lint; use cn() with its object-key form.',\n },\n schema: [],\n messages: {\n classList:\n 'classList hides classes from design-system lint. Use cn() with its object-key form and statically readable class names.',\n classListSpread:\n 'A JSX spread contains classList. Use cn() with its object-key form instead of hiding classes in a spread.',\n },\n },\n create(context) {\n return {\n JSXAttribute(node: any) {\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'classList')\n context.report({ node, messageId: 'classList' })\n },\n JSXSpreadAttribute(node: any) {\n if (objectHasStaticProperty(node.argument, 'classList'))\n context.report({ node, messageId: 'classListSpread' })\n },\n }\n },\n}\n\ninterface RawElementOptions {\n /** Element names this rule should not report (rare; prefer fixing the file). */\n allow?: string[]\n /** Replaces the default report message. */\n message?: string\n}\n\nconst noRawInteractiveElements: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Interactive elements this package provides (button, input, textarea, select, option, label) are composed from the package, not written as raw markup.',\n },\n schema: [\n {\n type: 'object',\n properties: {\n allow: { type: 'array', items: { type: 'string' } },\n message: { type: 'string' },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n rawElement:\n '{{element}} is provided by the design system: use {{primitive}} from {{path}}. A raw element has no keyboard story, no focus behaviour and no token styling — that is the work the primitive already did.',\n },\n },\n create(context) {\n const options = (context.options?.[0] ?? {}) as RawElementOptions\n const allowed = new Set(options.allow ?? [])\n const emit = (node: unknown, element: string) => {\n const target = ELEMENT_PRIMITIVES[element]!\n context.report({\n node,\n messageId: 'rawElement',\n data: { element, primitive: target.primitive, path: target.path },\n ...(options.message ? { message: options.message } : {}),\n })\n }\n return {\n JSXOpeningElement(node: any) {\n if (node.name?.type !== 'JSXIdentifier') return\n const element = node.name.name\n if (!(element in ELEMENT_PRIMITIVES) || allowed.has(element)) return\n emit(node, element)\n },\n }\n },\n}\n\ninterface PrimitiveImportOptions {\n /** Package prefixes this rule reports. Defaults to the primitive libraries the ui package wraps. */\n packages?: string[]\n /** Replaces the default report message. */\n message?: string\n}\n\nconst DEFAULT_PRIMITIVE_PACKAGES = Object.freeze([\n '@kobalte/core',\n '@corvu',\n 'cmdk-solid',\n '@ark-ui',\n '@zag-js',\n '@radix-ui',\n 'radix-ui',\n '@base-ui-components',\n '@base-ui',\n])\n\n/**\n * The interactive ARIA roles an application can dress a generic element in, and\n * the primitive that owns each role. `link` is deliberately absent: an `<a>` is\n * natively interactive and Button itself supports `as=\"a\"` for the link-styled\n * case.\n */\nconst ROLE_PRIMITIVES: Readonly<Record<string, { primitive: string; path: string }>> =\n Object.freeze({\n button: { primitive: 'Button', path: '@adea-ai/ui/components/ui/button' },\n checkbox: { primitive: 'Checkbox', path: '@adea-ai/ui/components/ui/checkbox' },\n radio: { primitive: 'RadioGroup', path: '@adea-ai/ui/components/ui/radio-group' },\n switch: { primitive: 'Switch', path: '@adea-ai/ui/components/ui/switch' },\n tab: { primitive: 'Tabs', path: '@adea-ai/ui/components/ui/tabs' },\n option: { primitive: 'Select', path: '@adea-ai/ui/components/ui/select' },\n menuitem: { primitive: 'DropdownMenu', path: '@adea-ai/ui/components/ui/dropdown-menu' },\n textbox: { primitive: 'Input', path: '@adea-ai/ui/components/ui/input' },\n })\n\n/**\n * The elements whose only job is layout or text. A click handler or a tabindex on\n * one of these is a control being hand-built out of a non-control.\n */\nconst GENERIC_ELEMENTS: ReadonlySet<string> = new Set([\n 'div',\n 'span',\n 'li',\n 'ul',\n 'ol',\n 'td',\n 'th',\n 'p',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'section',\n 'article',\n 'aside',\n 'header',\n 'footer',\n 'nav',\n 'main',\n 'img',\n 'svg',\n])\n\nconst CLICK_HANDLERS: ReadonlySet<string> = new Set([\n 'onClick',\n 'onDoubleClick',\n 'onPointerDown',\n 'onPointerUp',\n 'onMouseDown',\n 'onMouseUp',\n 'on:click',\n 'on:dblclick',\n 'on:pointerdown',\n 'on:pointerup',\n 'on:mousedown',\n 'on:mouseup',\n])\n\ninterface InteractiveWrapperOptions {\n /** Element names this rule should not report (rare; prefer fixing the file). */\n allow?: string[]\n /** Replaces the default report message. */\n message?: string\n}\n\n/** Only statically knowable branches; unresolved roles remain a review boundary. */\nconst staticStrings = (node: any): string[] => {\n if (node?.type === 'Literal' && typeof node.value === 'string') return [node.value]\n if (node?.type === 'JSXExpressionContainer') return staticStrings(node.expression)\n if (node?.type === 'ConditionalExpression')\n return [...staticStrings(node.consequent), ...staticStrings(node.alternate)]\n if (node?.type === 'TemplateLiteral' && node.expressions.length === 0)\n return [node.quasis[0].value.cooked ?? node.quasis[0].value.raw]\n return []\n}\n\nconst roleOf = (node: any): string | null => {\n for (const attribute of node.attributes ?? []) {\n if (attribute.type === 'JSXSpreadAttribute') {\n const role = staticObjectPropertyValues(attribute.argument, 'role')\n .flatMap((value) => staticStrings(value))\n .flatMap((value) => value.split(/\\s+/))\n .find((value) => value in ROLE_PRIMITIVES)\n if (role) return role\n continue\n }\n if (attribute.type !== 'JSXAttribute' || attribute.name?.name !== 'role') continue\n return (\n staticStrings(attribute.value)\n .flatMap((role) => role.split(/\\s+/))\n .find((role) => role in ROLE_PRIMITIVES) ?? null\n )\n }\n return null\n}\n\n/** Whether the element carries the attribute, by name. */\nconst has = (node: any, name: string): boolean =>\n (node.attributes ?? []).some(\n (attribute: any) =>\n (attribute.type === 'JSXAttribute' &&\n (attribute.name?.type === 'JSXNamespacedName'\n ? `${attribute.name.namespace.name}:${attribute.name.name.name}`\n : attribute.name?.name) === name) ||\n (attribute.type === 'JSXSpreadAttribute' && objectHasStaticProperty(attribute.argument, name))\n )\n\nconst tabStopValue = (value: any): boolean => {\n const expression = value?.type === 'JSXExpressionContainer' ? value.expression : value\n if (expression?.type === 'Literal') return !(Number(expression.value) < 0)\n if (\n expression?.type === 'UnaryExpression' &&\n expression.operator === '-' &&\n expression.argument?.type === 'Literal'\n )\n return !(Number(expression.argument.value) > 0)\n return true\n}\n\nconst hasTabStop = (node: any): boolean =>\n (node.attributes ?? []).some((attribute: any) => {\n if (\n attribute.type === 'JSXAttribute' &&\n ['tabIndex', 'tabindex'].includes(attribute.name?.name)\n )\n return tabStopValue(attribute.value)\n if (attribute.type === 'JSXSpreadAttribute')\n return ['tabIndex', 'tabindex'].some((name) =>\n staticObjectPropertyValues(attribute.argument, name).some(tabStopValue)\n )\n return false\n })\n\nconst noInteractiveWrappers: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Generic elements dressed as interactive controls — an interactive role, a click handler or a tabindex on a layout element — re-implement a primitive by hand. Compose the real component.',\n },\n schema: [\n {\n type: 'object',\n properties: {\n allow: { type: 'array', items: { type: 'string' } },\n message: { type: 'string' },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n roleWrapper:\n 'A {{element}} with role=\"{{role}}\" hand-builds {{primitive}}: use {{primitive}} from {{path}}. The primitive brings keyboard activation, focus behaviour and ARIA wiring; the wrapper re-implements them badly.',\n clickWrapper:\n 'A click handler on {{element}} composes {{primitive}} by hand: use {{primitive}} from {{path}}. A div that reacts to clicks is a button that cannot be reached by keyboard.',\n tabindexWrapper:\n 'A tabindex on {{element}} makes a layout element focusable, which is focus management the primitives already own: compose the real component instead.',\n },\n },\n create(context) {\n const options = (context.options?.[0] ?? {}) as InteractiveWrapperOptions\n const allowed = new Set(options.allow ?? [])\n return {\n JSXOpeningElement(node: any) {\n if (node.name?.type !== 'JSXIdentifier') return\n const element = node.name.name\n if (allowed.has(element) || element in ELEMENT_PRIMITIVES) return\n // A capitalized element is a component — often a design-system primitive\n // composing a role deliberately (a Button carrying role=\"option\" inside a\n // custom listbox, for example). That is composition, not impersonation.\n if (element[0] === element[0]?.toUpperCase()) return\n\n const role = roleOf(node)\n if (role && role in ROLE_PRIMITIVES) {\n const target = ROLE_PRIMITIVES[role]!\n context.report({\n node,\n messageId: 'roleWrapper',\n data: { element, role, primitive: target.primitive, path: target.path },\n ...(options.message ? { message: options.message } : {}),\n })\n return\n }\n\n if (GENERIC_ELEMENTS.has(element)) {\n const click = [...CLICK_HANDLERS].find((handler) => has(node, handler))\n if (click) {\n const target = ROLE_PRIMITIVES.button!\n context.report({\n node,\n messageId: 'clickWrapper',\n data: { element, primitive: target.primitive, path: target.path },\n ...(options.message ? { message: options.message } : {}),\n })\n return\n }\n if (hasTabStop(node)) {\n context.report({\n node,\n messageId: 'tabindexWrapper',\n data: { element },\n ...(options.message ? { message: options.message } : {}),\n })\n }\n }\n },\n }\n },\n}\n\nconst BUTTON_MODULE = '@adea-ai/ui/components/ui/button'\nconst ACTION_BUTTON_MODULE = '@adea-ai/ui/components/composites/action-button'\nconst ICON_BUTTON_SIZES: ReadonlySet<string> = new Set([\n 'icon-2xs',\n 'icon-xs',\n 'icon-sm',\n 'icon-md',\n 'icon-lg',\n 'icon-xl',\n 'icon-2xl',\n])\n\nconst staticJsxValues = (value: any): any[] =>\n value?.type === 'JSXExpressionContainer' ? [value.expression] : value ? [value] : []\n\nconst attributeValues = (node: any, name: string): any[] => {\n const values: any[] = []\n for (const attribute of node.attributes ?? []) {\n if (attribute.type === 'JSXAttribute' && attribute.name?.name === name)\n values.splice(0, values.length, ...staticJsxValues(attribute.value))\n if (attribute.type === 'JSXSpreadAttribute') {\n const spreadValues = staticObjectPropertyValues(attribute.argument, name)\n if (spreadValues.length > 0) values.splice(0, values.length, spreadValues.at(-1))\n }\n }\n return values\n}\n\nconst staticStringValue = (node: any): string | null => {\n if (node?.type === 'Literal' && typeof node.value === 'string') return node.value\n if (node?.type === 'TemplateLiteral' && node.expressions.length === 0)\n return node.quasis[0].value.cooked ?? node.quasis[0].value.raw\n return null\n}\n\nconst isIconButtonSize = (node: any): boolean =>\n attributeValues(node, 'size').some((value) => {\n const size = staticStringValue(value)\n return size !== null && ICON_BUTTON_SIZES.has(size)\n })\n\nconst actionTooltipStatus = (node: any): 'valid' | 'blank' | 'unknown' | 'missing' => {\n const values = attributeValues(node, 'tooltip')\n if (values.length === 0) return 'missing'\n const strings = values.map(staticStringValue)\n if (strings.some((value) => value !== null && value.trim().length > 0)) return 'valid'\n if (strings.every((value) => value !== null)) return 'blank'\n return 'unknown'\n}\n\nconst isImportedComponent = (identifier: any, imports: Set<string>): boolean =>\n identifier?.type === 'Identifier' && imports.has(identifier.name)\n\n/** Requires shared icon actions to carry the shared explanatory-tooltip contract. */\nconst requireActionButtonTooltip: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Consumer icon-size shared Button actions use ActionButton with a nonblank tooltip; dynamic values remain a review boundary.',\n },\n schema: [],\n messages: {\n useActionButton:\n 'Icon-size Button actions use ActionButton with a nonblank tooltip. Keep the control name in aria-label; the tooltip explains the action.',\n missingTooltip:\n 'An icon-size ActionButton needs a supplied nonblank tooltip. Keep its accessible name in aria-label as well.',\n },\n },\n create(context) {\n const buttons = new Set<string>()\n const actionButtons = new Set<string>()\n const recordImports = (node: any) => {\n const source = node.source?.value\n const target =\n source === BUTTON_MODULE ? buttons : source === ACTION_BUTTON_MODULE ? actionButtons : null\n if (!target) return\n for (const specifier of node.specifiers ?? []) {\n if (specifier.type !== 'ImportSpecifier') continue\n const imported = specifier.imported?.name ?? specifier.imported?.value\n if (\n (source === BUTTON_MODULE && imported === 'Button') ||\n (source === ACTION_BUTTON_MODULE && imported === 'ActionButton')\n )\n target.add(specifier.local?.name)\n }\n }\n\n return {\n ImportDeclaration: recordImports,\n JSXOpeningElement(node: any) {\n if (!isIconButtonSize(node)) return\n const name = node.name?.type === 'JSXIdentifier' ? node.name.name : null\n const isButton = name !== null && buttons.has(name)\n const isActionButton = name !== null && actionButtons.has(name)\n const asValues = attributeValues(node, 'as')\n const targetsButton = asValues.some((value) => isImportedComponent(value, buttons))\n const targetsActionButton = asValues.some((value) =>\n isImportedComponent(value, actionButtons)\n )\n if (isButton || targetsButton) {\n context.report({ node, messageId: 'useActionButton' })\n return\n }\n if (!isActionButton && !targetsActionButton) return\n const status = actionTooltipStatus(node)\n if (status === 'missing' || status === 'blank')\n context.report({ node, messageId: 'missingTooltip' })\n },\n }\n },\n}\n\nconst noPrimitiveLibraryImports: RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'The primitive libraries the design system is built on (Kobalte and friends) are its internal affair — compose the exported components instead of importing the primitives.',\n },\n schema: [\n {\n type: 'object',\n properties: {\n packages: { type: 'array', items: { type: 'string' } },\n message: { type: 'string' },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n primitiveImport:\n 'Import from the design system, not from {{package}}: the primitives it wraps carry the focus, keyboard and ARIA behaviour the components exist to provide.',\n },\n },\n create(context) {\n const options = (context.options?.[0] ?? {}) as PrimitiveImportOptions\n const prefixes = options.packages ?? DEFAULT_PRIMITIVE_PACKAGES\n const offender = (source: unknown): string | null => {\n const name = String(source ?? '')\n return prefixes.find((prefix) => name === prefix || name.startsWith(`${prefix}/`)) ?? null\n }\n const report = (node: unknown, source: unknown) => {\n const pkg = offender(source)!\n context.report({\n node,\n messageId: 'primitiveImport',\n data: { package: pkg },\n ...(options.message ? { message: options.message } : {}),\n })\n }\n return {\n ImportDeclaration(node: any) {\n if (offender(node.source?.value)) report(node, node.source.value)\n },\n ImportExpression(node: any) {\n if (typeof node.source?.value === 'string' && offender(node.source.value))\n report(node, node.source.value)\n },\n ExportNamedDeclaration(node: any) {\n if (offender(node.source?.value)) report(node, node.source.value)\n },\n ExportAllDeclaration(node: any) {\n if (offender(node.source?.value)) report(node, node.source.value)\n },\n CallExpression(node: any) {\n if (node.callee?.type !== 'Identifier' || node.callee.name !== 'require') return\n const source = node.arguments?.[0]?.value\n if (typeof source === 'string' && offender(source)) report(node, source)\n },\n }\n },\n}\n\nconst plugin = {\n meta: { name: 'adea' },\n rules: {\n 'no-inline-styles': noInlineStyles,\n 'no-class-list': noClassList,\n 'no-raw-interactive-elements': noRawInteractiveElements,\n 'no-primitive-library-imports': noPrimitiveLibraryImports,\n 'no-interactive-wrappers': noInteractiveWrappers,\n 'require-action-button-tooltip': requireActionButtonTooltip,\n },\n}\n\nexport default plugin\n"],"mappings":"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"}
|
package/dist/r/registry.json
CHANGED
|
@@ -1962,6 +1962,30 @@
|
|
|
1962
1962
|
}
|
|
1963
1963
|
]
|
|
1964
1964
|
},
|
|
1965
|
+
{
|
|
1966
|
+
"name": "scene-controls",
|
|
1967
|
+
"type": "registry:ui",
|
|
1968
|
+
"title": "Scene Controls",
|
|
1969
|
+
"description": "Shared host-neutral scene movement, jump and zoom controls.",
|
|
1970
|
+
"dependencies": [
|
|
1971
|
+
"lucide-solid@^1.48.0"
|
|
1972
|
+
],
|
|
1973
|
+
"registryDependencies": [
|
|
1974
|
+
"@adea-ai/ui/action-button"
|
|
1975
|
+
],
|
|
1976
|
+
"files": [
|
|
1977
|
+
{
|
|
1978
|
+
"path": "src/components/composites/scene-controls/index.ts",
|
|
1979
|
+
"type": "registry:ui",
|
|
1980
|
+
"target": "components/composites/scene-controls/index.ts"
|
|
1981
|
+
},
|
|
1982
|
+
{
|
|
1983
|
+
"path": "src/components/composites/scene-controls/scene-controls.tsx",
|
|
1984
|
+
"type": "registry:ui",
|
|
1985
|
+
"target": "components/composites/scene-controls/scene-controls.tsx"
|
|
1986
|
+
}
|
|
1987
|
+
]
|
|
1988
|
+
},
|
|
1965
1989
|
{
|
|
1966
1990
|
"name": "settings",
|
|
1967
1991
|
"type": "registry:ui",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "scene-controls",
|
|
3
|
+
"type": "registry:ui",
|
|
4
|
+
"title": "Scene Controls",
|
|
5
|
+
"description": "Shared host-neutral scene movement, jump and zoom controls.",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"lucide-solid@^1.48.0"
|
|
8
|
+
],
|
|
9
|
+
"registryDependencies": [
|
|
10
|
+
"@adea-ai/ui/action-button"
|
|
11
|
+
],
|
|
12
|
+
"files": [
|
|
13
|
+
{
|
|
14
|
+
"path": "src/components/composites/scene-controls/index.ts",
|
|
15
|
+
"type": "registry:ui",
|
|
16
|
+
"target": "components/composites/scene-controls/index.ts"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "src/components/composites/scene-controls/scene-controls.tsx",
|
|
20
|
+
"type": "registry:ui",
|
|
21
|
+
"target": "components/composites/scene-controls/scene-controls.tsx"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -84,6 +84,7 @@ export function ActionButton<T extends ValidComponent = 'button'>(props: ActionB
|
|
|
84
84
|
'as',
|
|
85
85
|
'variant',
|
|
86
86
|
'size',
|
|
87
|
+
'touchTarget',
|
|
87
88
|
'class',
|
|
88
89
|
'children',
|
|
89
90
|
'disabled',
|
|
@@ -106,6 +107,7 @@ export function ActionButton<T extends ValidComponent = 'button'>(props: ActionB
|
|
|
106
107
|
...rest,
|
|
107
108
|
variant: local.variant,
|
|
108
109
|
size: local.size,
|
|
110
|
+
touchTarget: local.touchTarget,
|
|
109
111
|
class: [local.class, local.tooltip && disabled() && 'aria-disabled:pointer-events-auto']
|
|
110
112
|
.filter(Boolean)
|
|
111
113
|
.join(' '),
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, ZoomIn, ZoomOut } from 'lucide-solid'
|
|
2
|
+
import { createSignal, onCleanup, Show, type JSX } from 'solid-js'
|
|
3
|
+
import { ActionButton } from '../action-button'
|
|
4
|
+
|
|
5
|
+
/** A host-neutral direction reported while a movement control is held. */
|
|
6
|
+
export type SceneMovementDirection = 'forward' | 'left' | 'backward' | 'right'
|
|
7
|
+
|
|
8
|
+
/** Accessible names and tooltip text for the shared scene controls. */
|
|
9
|
+
export type SceneControlLabels = {
|
|
10
|
+
movementGroup: string
|
|
11
|
+
forward: string
|
|
12
|
+
forwardTooltip: string
|
|
13
|
+
left: string
|
|
14
|
+
leftTooltip: string
|
|
15
|
+
backward: string
|
|
16
|
+
backwardTooltip: string
|
|
17
|
+
right: string
|
|
18
|
+
rightTooltip: string
|
|
19
|
+
jump: string
|
|
20
|
+
jumpTooltip: string
|
|
21
|
+
zoomGroup: string
|
|
22
|
+
zoomOut: string
|
|
23
|
+
zoomIn: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DEFAULT_LABELS: SceneControlLabels = {
|
|
27
|
+
movementGroup: 'Scene movement controls',
|
|
28
|
+
forward: 'Move forward',
|
|
29
|
+
forwardTooltip: 'Hold to move forward',
|
|
30
|
+
left: 'Move left',
|
|
31
|
+
leftTooltip: 'Hold to move left',
|
|
32
|
+
backward: 'Move backward',
|
|
33
|
+
backwardTooltip: 'Hold to move backward',
|
|
34
|
+
right: 'Move right',
|
|
35
|
+
rightTooltip: 'Hold to move right',
|
|
36
|
+
jump: 'Jump',
|
|
37
|
+
jumpTooltip: 'Hold to jump',
|
|
38
|
+
zoomGroup: 'Camera zoom',
|
|
39
|
+
zoomOut: 'Zoom out',
|
|
40
|
+
zoomIn: 'Zoom in',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const pointerSource = (pointerId: number) => `pointer:${pointerId}`
|
|
44
|
+
const keySource = (key: string) => `key:${key}`
|
|
45
|
+
|
|
46
|
+
export type SceneControlsProps = {
|
|
47
|
+
/** Called on the transition into and out of each held movement direction. */
|
|
48
|
+
onMovementChange?: (direction: SceneMovementDirection, pressed: boolean) => void
|
|
49
|
+
/** Called when the jump action becomes pressed or released. */
|
|
50
|
+
onJumpChange?: (pressed: boolean) => void
|
|
51
|
+
/** Called once when zoom in is activated. */
|
|
52
|
+
onZoomIn?: () => void
|
|
53
|
+
/** Called once when zoom out is activated. */
|
|
54
|
+
onZoomOut?: () => void
|
|
55
|
+
/** Override the default English accessible names and tooltip copy. */
|
|
56
|
+
labels?: Partial<SceneControlLabels>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type HoldActionProps = {
|
|
60
|
+
label: string
|
|
61
|
+
tooltip: string
|
|
62
|
+
createChangeCallback: () => (pressed: boolean) => void
|
|
63
|
+
children: JSX.Element
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A held action that reports one down/up pair across pointer and keyboard input.
|
|
68
|
+
*
|
|
69
|
+
* The arrangement, rounded scrim group surfaces and release policy translate
|
|
70
|
+
* Adea's on-screen scene controls; host key synthesis and scene APIs are replaced
|
|
71
|
+
* by callbacks.
|
|
72
|
+
*/
|
|
73
|
+
function HoldAction(props: HoldActionProps) {
|
|
74
|
+
const [pressed, setPressed] = createSignal(false)
|
|
75
|
+
const sources = new Set<string>()
|
|
76
|
+
let activeChangeCallback: ((pressed: boolean) => void) | undefined
|
|
77
|
+
|
|
78
|
+
const press = (source: string) => {
|
|
79
|
+
if (sources.has(source)) return
|
|
80
|
+
const wasPressed = sources.size > 0
|
|
81
|
+
sources.add(source)
|
|
82
|
+
if (!wasPressed) {
|
|
83
|
+
setPressed(true)
|
|
84
|
+
activeChangeCallback = props.createChangeCallback()
|
|
85
|
+
activeChangeCallback(true)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const release = (source: string) => {
|
|
90
|
+
if (!sources.delete(source) || sources.size > 0) return
|
|
91
|
+
setPressed(false)
|
|
92
|
+
const callback = activeChangeCallback
|
|
93
|
+
activeChangeCallback = undefined
|
|
94
|
+
callback?.(false)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const releaseAll = () => {
|
|
98
|
+
if (sources.size === 0) return
|
|
99
|
+
sources.clear()
|
|
100
|
+
setPressed(false)
|
|
101
|
+
const callback = activeChangeCallback
|
|
102
|
+
activeChangeCallback = undefined
|
|
103
|
+
callback?.(false)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const onWindowPointerUp = (event: PointerEvent) => release(pointerSource(event.pointerId))
|
|
107
|
+
const onWindowBlur = () => releaseAll()
|
|
108
|
+
|
|
109
|
+
if (typeof window !== 'undefined') {
|
|
110
|
+
window.addEventListener('pointerup', onWindowPointerUp)
|
|
111
|
+
window.addEventListener('blur', onWindowBlur)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
onCleanup(() => {
|
|
115
|
+
releaseAll()
|
|
116
|
+
if (typeof window !== 'undefined') {
|
|
117
|
+
window.removeEventListener('pointerup', onWindowPointerUp)
|
|
118
|
+
window.removeEventListener('blur', onWindowBlur)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<ActionButton
|
|
124
|
+
type="button"
|
|
125
|
+
variant="secondary"
|
|
126
|
+
size="icon-2xl"
|
|
127
|
+
aria-label={props.label}
|
|
128
|
+
aria-pressed={pressed()}
|
|
129
|
+
tooltip={props.tooltip}
|
|
130
|
+
class="touch-none select-none"
|
|
131
|
+
onContextMenu={(event) => event.preventDefault()}
|
|
132
|
+
onPointerDown={(event) => {
|
|
133
|
+
if (event.button !== 0) return
|
|
134
|
+
event.preventDefault()
|
|
135
|
+
const source = pointerSource(event.pointerId)
|
|
136
|
+
if (sources.has(source)) return
|
|
137
|
+
try {
|
|
138
|
+
event.currentTarget.setPointerCapture(event.pointerId)
|
|
139
|
+
} catch {
|
|
140
|
+
// Pointer capture is not available in every embedded browser. The
|
|
141
|
+
// window pointerup fallback still closes this held action.
|
|
142
|
+
}
|
|
143
|
+
press(source)
|
|
144
|
+
}}
|
|
145
|
+
onPointerUp={(event) => release(pointerSource(event.pointerId))}
|
|
146
|
+
onPointerCancel={(event) => release(pointerSource(event.pointerId))}
|
|
147
|
+
onLostPointerCapture={(event) => release(pointerSource(event.pointerId))}
|
|
148
|
+
onKeyDown={(event) => {
|
|
149
|
+
if (event.key !== ' ' && event.key !== 'Enter') return
|
|
150
|
+
event.preventDefault()
|
|
151
|
+
press(keySource(event.key))
|
|
152
|
+
}}
|
|
153
|
+
onKeyUp={(event) => {
|
|
154
|
+
if (event.key !== ' ' && event.key !== 'Enter') return
|
|
155
|
+
event.preventDefault()
|
|
156
|
+
release(keySource(event.key))
|
|
157
|
+
}}
|
|
158
|
+
onBlur={releaseAll}
|
|
159
|
+
onClick={(event) => {
|
|
160
|
+
// Assistive technology can activate a button without pointer or keyboard
|
|
161
|
+
// down/up events. Treat that activation as one short held action.
|
|
162
|
+
if (event.detail !== 0 || sources.size > 0) return
|
|
163
|
+
press('activation')
|
|
164
|
+
release('activation')
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
{props.children}
|
|
168
|
+
</ActionButton>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Shared movement, jump and zoom controls for a host-rendered scene.
|
|
174
|
+
*
|
|
175
|
+
* Movement and jump callbacks receive pressed/released state from pointer and
|
|
176
|
+
* keyboard input. Zoom callbacks fire on activation. The host owns positioning,
|
|
177
|
+
* scene state and camera behavior.
|
|
178
|
+
*/
|
|
179
|
+
export function SceneControls(props: SceneControlsProps) {
|
|
180
|
+
const labels = () => ({ ...DEFAULT_LABELS, ...props.labels })
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<div class="flex min-w-0 max-w-full flex-wrap items-end gap-3">
|
|
184
|
+
<Show when={props.onMovementChange}>
|
|
185
|
+
<div
|
|
186
|
+
role="group"
|
|
187
|
+
aria-label={labels().movementGroup}
|
|
188
|
+
class="glass-scrim grid min-w-0 max-w-full grid-cols-3 gap-1.5 rounded-3xl p-2"
|
|
189
|
+
>
|
|
190
|
+
<span aria-hidden="true" />
|
|
191
|
+
<HoldAction
|
|
192
|
+
label={labels().forward}
|
|
193
|
+
tooltip={labels().forwardTooltip}
|
|
194
|
+
createChangeCallback={() => {
|
|
195
|
+
const callback = props.onMovementChange
|
|
196
|
+
return (pressed) => callback?.('forward', pressed)
|
|
197
|
+
}}
|
|
198
|
+
>
|
|
199
|
+
<ArrowUp aria-hidden="true" />
|
|
200
|
+
</HoldAction>
|
|
201
|
+
<span aria-hidden="true" />
|
|
202
|
+
<HoldAction
|
|
203
|
+
label={labels().left}
|
|
204
|
+
tooltip={labels().leftTooltip}
|
|
205
|
+
createChangeCallback={() => {
|
|
206
|
+
const callback = props.onMovementChange
|
|
207
|
+
return (pressed) => callback?.('left', pressed)
|
|
208
|
+
}}
|
|
209
|
+
>
|
|
210
|
+
<ArrowLeft aria-hidden="true" />
|
|
211
|
+
</HoldAction>
|
|
212
|
+
<HoldAction
|
|
213
|
+
label={labels().backward}
|
|
214
|
+
tooltip={labels().backwardTooltip}
|
|
215
|
+
createChangeCallback={() => {
|
|
216
|
+
const callback = props.onMovementChange
|
|
217
|
+
return (pressed) => callback?.('backward', pressed)
|
|
218
|
+
}}
|
|
219
|
+
>
|
|
220
|
+
<ArrowDown aria-hidden="true" />
|
|
221
|
+
</HoldAction>
|
|
222
|
+
<HoldAction
|
|
223
|
+
label={labels().right}
|
|
224
|
+
tooltip={labels().rightTooltip}
|
|
225
|
+
createChangeCallback={() => {
|
|
226
|
+
const callback = props.onMovementChange
|
|
227
|
+
return (pressed) => callback?.('right', pressed)
|
|
228
|
+
}}
|
|
229
|
+
>
|
|
230
|
+
<ArrowRight aria-hidden="true" />
|
|
231
|
+
</HoldAction>
|
|
232
|
+
</div>
|
|
233
|
+
</Show>
|
|
234
|
+
<div class="ml-auto flex min-w-0 max-w-full flex-col items-end gap-2">
|
|
235
|
+
<Show when={props.onZoomIn || props.onZoomOut}>
|
|
236
|
+
<div
|
|
237
|
+
role="group"
|
|
238
|
+
aria-label={labels().zoomGroup}
|
|
239
|
+
class="glass-scrim flex min-w-0 max-w-full flex-wrap gap-1.5 rounded-2xl p-1.5"
|
|
240
|
+
>
|
|
241
|
+
<Show when={props.onZoomOut}>
|
|
242
|
+
<ActionButton
|
|
243
|
+
type="button"
|
|
244
|
+
variant="secondary"
|
|
245
|
+
size="icon-2xl"
|
|
246
|
+
aria-label={labels().zoomOut}
|
|
247
|
+
tooltip={labels().zoomOut}
|
|
248
|
+
onClick={() => props.onZoomOut?.()}
|
|
249
|
+
>
|
|
250
|
+
<ZoomOut aria-hidden="true" />
|
|
251
|
+
</ActionButton>
|
|
252
|
+
</Show>
|
|
253
|
+
<Show when={props.onZoomIn}>
|
|
254
|
+
<ActionButton
|
|
255
|
+
type="button"
|
|
256
|
+
variant="secondary"
|
|
257
|
+
size="icon-2xl"
|
|
258
|
+
aria-label={labels().zoomIn}
|
|
259
|
+
tooltip={labels().zoomIn}
|
|
260
|
+
onClick={() => props.onZoomIn?.()}
|
|
261
|
+
>
|
|
262
|
+
<ZoomIn aria-hidden="true" />
|
|
263
|
+
</ActionButton>
|
|
264
|
+
</Show>
|
|
265
|
+
</div>
|
|
266
|
+
</Show>
|
|
267
|
+
<Show when={props.onJumpChange}>
|
|
268
|
+
<HoldAction
|
|
269
|
+
label={labels().jump}
|
|
270
|
+
tooltip={labels().jumpTooltip}
|
|
271
|
+
createChangeCallback={() => {
|
|
272
|
+
const callback = props.onJumpChange
|
|
273
|
+
return (pressed) => callback?.(pressed)
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
<ArrowUp aria-hidden="true" />
|
|
277
|
+
</HoldAction>
|
|
278
|
+
</Show>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
)
|
|
282
|
+
}
|
|
@@ -47,6 +47,7 @@ export const buttonVariants = cva(
|
|
|
47
47
|
md: `${controlSize.md} [&_svg]:size-4`,
|
|
48
48
|
lg: `${controlSize.lg} [&_svg]:size-4`,
|
|
49
49
|
xl: `${controlSize.xl} [&_svg]:size-5`,
|
|
50
|
+
'2xl': `${controlSize['2xl']} [&_svg]:size-6`,
|
|
50
51
|
/* Square, for an icon with no label. The accessible name then has to
|
|
51
52
|
come from `aria-label` or a visually hidden label — an icon-only
|
|
52
53
|
button with neither is the accessibility finding the Storybook lane
|
|
@@ -57,6 +58,11 @@ export const buttonVariants = cva(
|
|
|
57
58
|
'icon-md': 'size-control-md p-0 [&_svg]:size-4',
|
|
58
59
|
'icon-lg': 'size-control-lg p-0 [&_svg]:size-4',
|
|
59
60
|
'icon-xl': 'size-control-xl p-0 [&_svg]:size-5',
|
|
61
|
+
'icon-2xl': 'size-control-2xl p-0 [&_svg]:size-6',
|
|
62
|
+
},
|
|
63
|
+
/** Opt in to a stable touch target on devices with any coarse pointer. */
|
|
64
|
+
touchTarget: {
|
|
65
|
+
comfortable: 'touch-target-comfortable',
|
|
60
66
|
},
|
|
61
67
|
},
|
|
62
68
|
defaultVariants: {
|
|
@@ -76,13 +82,25 @@ export type ButtonProps<T extends ValidComponent = 'button'> = PolymorphicProps<
|
|
|
76
82
|
>
|
|
77
83
|
|
|
78
84
|
export function Button<T extends ValidComponent = 'button'>(props: ButtonProps<T>) {
|
|
79
|
-
const [local, rest] = splitProps(props as ButtonProps, [
|
|
85
|
+
const [local, rest] = splitProps(props as ButtonProps, [
|
|
86
|
+
'variant',
|
|
87
|
+
'size',
|
|
88
|
+
'touchTarget',
|
|
89
|
+
'class',
|
|
90
|
+
])
|
|
80
91
|
|
|
81
92
|
return (
|
|
82
93
|
<Polymorphic
|
|
83
94
|
as="button"
|
|
84
95
|
type="button"
|
|
85
|
-
class={cn(
|
|
96
|
+
class={cn(
|
|
97
|
+
buttonVariants({
|
|
98
|
+
variant: local.variant,
|
|
99
|
+
size: local.size,
|
|
100
|
+
touchTarget: local.touchTarget,
|
|
101
|
+
}),
|
|
102
|
+
local.class
|
|
103
|
+
)}
|
|
86
104
|
{...(rest as ComponentProps<'button'>)}
|
|
87
105
|
/>
|
|
88
106
|
)
|
|
@@ -34,12 +34,14 @@ export const toggleVariants = cva(
|
|
|
34
34
|
md: `${controlSize.md} [&_svg]:size-4`,
|
|
35
35
|
lg: `${controlSize.lg} [&_svg]:size-4`,
|
|
36
36
|
xl: `${controlSize.xl} [&_svg]:size-5`,
|
|
37
|
+
'2xl': `${controlSize['2xl']} [&_svg]:size-6`,
|
|
37
38
|
'icon-2xs': 'size-control-2xs p-0 [&_svg]:size-3',
|
|
38
39
|
'icon-xs': 'size-control-xs p-0 [&_svg]:size-3.5',
|
|
39
40
|
'icon-sm': 'size-control-sm p-0 [&_svg]:size-3.5',
|
|
40
41
|
'icon-md': 'size-control-md p-0 [&_svg]:size-4',
|
|
41
42
|
'icon-lg': 'size-control-lg p-0 [&_svg]:size-4',
|
|
42
43
|
'icon-xl': 'size-control-xl p-0 [&_svg]:size-5',
|
|
44
|
+
'icon-2xl': 'size-control-2xl p-0 [&_svg]:size-6',
|
|
43
45
|
},
|
|
44
46
|
},
|
|
45
47
|
defaultVariants: { variant: 'default', size: 'md' },
|
package/dist/r/src/lib/tokens.ts
CHANGED
|
@@ -480,6 +480,11 @@ export const densityTokens: TokenDefinition[] = [
|
|
|
480
480
|
kind: 'dimension',
|
|
481
481
|
description: '40px — a hero control in an empty state.',
|
|
482
482
|
},
|
|
483
|
+
{
|
|
484
|
+
name: 'control-height-2xl',
|
|
485
|
+
kind: 'dimension',
|
|
486
|
+
description: '48px — a stable touch target, unchanged by compact density.',
|
|
487
|
+
},
|
|
483
488
|
{
|
|
484
489
|
name: 'control-padding-2xs',
|
|
485
490
|
kind: 'dimension',
|
|
@@ -510,6 +515,11 @@ export const densityTokens: TokenDefinition[] = [
|
|
|
510
515
|
kind: 'dimension',
|
|
511
516
|
description: 'Inline padding that pairs with the 40px control.',
|
|
512
517
|
},
|
|
518
|
+
{
|
|
519
|
+
name: 'control-padding-2xl',
|
|
520
|
+
kind: 'dimension',
|
|
521
|
+
description: 'Inline padding that pairs with the stable 48px touch target.',
|
|
522
|
+
},
|
|
513
523
|
{ name: 'row-height-sm', kind: 'dimension', description: '28px — a dense list row.' },
|
|
514
524
|
{ name: 'row-height-md', kind: 'dimension', description: '32px — the default list row.' },
|
|
515
525
|
{ name: 'row-height-lg', kind: 'dimension', description: '36px — a two-line list row.' },
|
package/dist/r/src/lib/utils.ts
CHANGED
|
@@ -36,6 +36,7 @@ export const cn: CnFunction = createCn({
|
|
|
36
36
|
'control-md',
|
|
37
37
|
'control-lg',
|
|
38
38
|
'control-xl',
|
|
39
|
+
'control-2xl',
|
|
39
40
|
'row-sm',
|
|
40
41
|
'row-md',
|
|
41
42
|
'row-lg',
|
|
@@ -61,6 +62,7 @@ export const cn: CnFunction = createCn({
|
|
|
61
62
|
'control-md',
|
|
62
63
|
'control-lg',
|
|
63
64
|
'control-xl',
|
|
65
|
+
'control-2xl',
|
|
64
66
|
'rail-item',
|
|
65
67
|
],
|
|
66
68
|
},
|