@nadicodeai/ui 12.1.102 → 12.1.103
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/accordion.d.ts +38 -12
- package/dist/components/accordion.d.ts.map +1 -1
- package/dist/components/accordion.js +36 -15
- package/dist/components/account-menu.js +2 -2
- package/dist/components/action-menu.d.ts +20 -19
- package/dist/components/action-menu.d.ts.map +1 -1
- package/dist/components/action-menu.js +22 -15
- package/dist/components/action-status.d.ts +14 -10
- package/dist/components/action-status.d.ts.map +1 -1
- package/dist/components/action-status.js +14 -10
- package/dist/components/activity-calendar.d.ts +3 -1
- package/dist/components/activity-calendar.d.ts.map +1 -1
- package/dist/components/activity-calendar.js +16 -2
- package/dist/components/activity-feed.d.ts +2 -2
- package/dist/components/activity-feed.d.ts.map +1 -1
- package/dist/components/activity-feed.js +14 -5
- package/dist/components/agent-card.d.ts +11 -5
- package/dist/components/agent-card.d.ts.map +1 -1
- package/dist/components/agent-card.js +13 -7
- package/dist/components/agent-list-row.js +1 -1
- package/dist/components/agent-orb-appearance.d.ts +7 -1
- package/dist/components/agent-orb-appearance.d.ts.map +1 -1
- package/dist/components/agent-orb-appearance.js +26 -0
- package/dist/components/agent-profile.d.ts +8 -7
- package/dist/components/agent-profile.d.ts.map +1 -1
- package/dist/components/agent-profile.js +6 -6
- package/dist/components/alert-dialog.d.ts +25 -16
- package/dist/components/alert-dialog.d.ts.map +1 -1
- package/dist/components/alert-dialog.js +30 -18
- package/dist/components/alert.d.ts +1 -1
- package/dist/components/avatar.d.ts +83 -17
- package/dist/components/avatar.d.ts.map +1 -1
- package/dist/components/avatar.js +89 -23
- package/dist/components/badge.d.ts +59 -8
- package/dist/components/badge.d.ts.map +1 -1
- package/dist/components/badge.js +57 -14
- package/dist/components/breadcrumb.d.ts +18 -7
- package/dist/components/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb.js +37 -13
- package/dist/components/button-group.d.ts +12 -15
- package/dist/components/button-group.d.ts.map +1 -1
- package/dist/components/button-group.js +13 -17
- package/dist/components/button.d.ts +126 -23
- package/dist/components/button.d.ts.map +1 -1
- package/dist/components/button.js +164 -39
- package/dist/components/calendar.d.ts +26 -15
- package/dist/components/calendar.d.ts.map +1 -1
- package/dist/components/calendar.js +60 -46
- package/dist/components/card-row.d.ts +13 -5
- package/dist/components/card-row.d.ts.map +1 -1
- package/dist/components/card-row.js +24 -10
- package/dist/components/card.d.ts +6 -12
- package/dist/components/card.d.ts.map +1 -1
- package/dist/components/card.js +11 -16
- package/dist/components/carousel.d.ts +3 -3
- package/dist/components/carousel.d.ts.map +1 -1
- package/dist/components/carousel.js +7 -7
- package/dist/components/catalog-drawer.d.ts +7 -7
- package/dist/components/catalog-drawer.d.ts.map +1 -1
- package/dist/components/catalog-drawer.js +6 -6
- package/dist/components/chart-area-stacked.d.ts +1 -1
- package/dist/components/chart-area-stacked.d.ts.map +1 -1
- package/dist/components/chart-area-stacked.js +4 -4
- package/dist/components/chart-axis.d.ts +14 -1
- package/dist/components/chart-axis.d.ts.map +1 -1
- package/dist/components/chart-axis.js +11 -1
- package/dist/components/chart-bar-horizontal.d.ts +2 -2
- package/dist/components/chart-bar-horizontal.d.ts.map +1 -1
- package/dist/components/chart-bar-horizontal.js +14 -5
- package/dist/components/chart-bar-multiple.d.ts +1 -1
- package/dist/components/chart-bar-multiple.d.ts.map +1 -1
- package/dist/components/chart-bar-multiple.js +3 -3
- package/dist/components/chart-bar-stacked.d.ts +1 -1
- package/dist/components/chart-bar-stacked.d.ts.map +1 -1
- package/dist/components/chart-bar-stacked.js +3 -3
- package/dist/components/chart-card.d.ts +17 -6
- package/dist/components/chart-card.d.ts.map +1 -1
- package/dist/components/chart-card.js +11 -4
- package/dist/components/chart-date-range-control.d.ts +18 -9
- package/dist/components/chart-date-range-control.d.ts.map +1 -1
- package/dist/components/chart-date-range-control.js +43 -47
- package/dist/components/chart-sparkline.d.ts +4 -3
- package/dist/components/chart-sparkline.d.ts.map +1 -1
- package/dist/components/chart-sparkline.js +6 -5
- package/dist/components/check-counts.d.ts +44 -0
- package/dist/components/check-counts.d.ts.map +1 -0
- package/dist/components/check-counts.js +51 -0
- package/dist/components/checkbox.d.ts +39 -11
- package/dist/components/checkbox.d.ts.map +1 -1
- package/dist/components/checkbox.js +38 -11
- package/dist/components/choicebox.d.ts +96 -0
- package/dist/components/choicebox.d.ts.map +1 -0
- package/dist/components/choicebox.js +113 -0
- package/dist/components/clearable-input.d.ts +57 -0
- package/dist/components/clearable-input.d.ts.map +1 -0
- package/dist/components/clearable-input.js +58 -0
- package/dist/components/code-block.d.ts +77 -17
- package/dist/components/code-block.d.ts.map +1 -1
- package/dist/components/code-block.js +85 -15
- package/dist/components/collapsible.d.ts +27 -10
- package/dist/components/collapsible.d.ts.map +1 -1
- package/dist/components/collapsible.js +24 -9
- package/dist/components/collection-filters.d.ts +3 -1
- package/dist/components/collection-filters.d.ts.map +1 -1
- package/dist/components/collection-filters.js +7 -9
- package/dist/components/collection-page.d.ts +31 -10
- package/dist/components/collection-page.d.ts.map +1 -1
- package/dist/components/collection-page.js +14 -13
- package/dist/components/collection-search.d.ts +32 -10
- package/dist/components/collection-search.d.ts.map +1 -1
- package/dist/components/collection-search.js +24 -13
- package/dist/components/collection-sort.d.ts.map +1 -1
- package/dist/components/collection-sort.js +2 -2
- package/dist/components/collection-toolbar.d.ts +5 -2
- package/dist/components/collection-toolbar.d.ts.map +1 -1
- package/dist/components/collection-toolbar.js +7 -4
- package/dist/components/combobox.d.ts +97 -22
- package/dist/components/combobox.d.ts.map +1 -1
- package/dist/components/combobox.js +80 -28
- package/dist/components/command.d.ts +34 -21
- package/dist/components/command.d.ts.map +1 -1
- package/dist/components/command.js +33 -25
- package/dist/components/confirm-dialog.d.ts +23 -6
- package/dist/components/confirm-dialog.d.ts.map +1 -1
- package/dist/components/confirm-dialog.js +32 -13
- package/dist/components/context-menu.d.ts +14 -10
- package/dist/components/context-menu.d.ts.map +1 -1
- package/dist/components/context-menu.js +17 -12
- package/dist/components/copy-button.d.ts +5 -2
- package/dist/components/copy-button.d.ts.map +1 -1
- package/dist/components/copy-button.js +16 -8
- package/dist/components/data-table.d.ts +120 -54
- package/dist/components/data-table.d.ts.map +1 -1
- package/dist/components/data-table.js +163 -92
- package/dist/components/desktop-shell.d.ts +4 -2
- package/dist/components/desktop-shell.d.ts.map +1 -1
- package/dist/components/desktop-shell.js +3 -3
- package/dist/components/dialog.d.ts +63 -24
- package/dist/components/dialog.d.ts.map +1 -1
- package/dist/components/dialog.js +52 -31
- package/dist/components/disclosure-row.d.ts +1 -2
- package/dist/components/disclosure-row.d.ts.map +1 -1
- package/dist/components/disclosure-row.js +1 -2
- package/dist/components/disclosure.d.ts +1 -1
- package/dist/components/disclosure.js +1 -1
- package/dist/components/drawer.d.ts +33 -12
- package/dist/components/drawer.d.ts.map +1 -1
- package/dist/components/drawer.js +47 -35
- package/dist/components/dropdown-menu.d.ts +59 -14
- package/dist/components/dropdown-menu.d.ts.map +1 -1
- package/dist/components/dropdown-menu.js +58 -26
- package/dist/components/empty-state.d.ts +35 -12
- package/dist/components/empty-state.d.ts.map +1 -1
- package/dist/components/empty-state.js +35 -17
- package/dist/components/entry-scene.d.ts +4 -4
- package/dist/components/entry-scene.d.ts.map +1 -1
- package/dist/components/entry-scene.js +2 -2
- package/dist/components/field.d.ts +44 -14
- package/dist/components/field.d.ts.map +1 -1
- package/dist/components/field.js +49 -20
- package/dist/components/file-diff.d.ts.map +1 -1
- package/dist/components/file-diff.js +3 -4
- package/dist/components/form-dialog.d.ts +7 -7
- package/dist/components/form-dialog.d.ts.map +1 -1
- package/dist/components/form-dialog.js +13 -11
- package/dist/components/form-drawer.d.ts +10 -7
- package/dist/components/form-drawer.d.ts.map +1 -1
- package/dist/components/form-drawer.js +11 -10
- package/dist/components/gauge.d.ts +64 -0
- package/dist/components/gauge.d.ts.map +1 -0
- package/dist/components/gauge.js +73 -0
- package/dist/components/hover-card.d.ts +72 -14
- package/dist/components/hover-card.d.ts.map +1 -1
- package/dist/components/hover-card.js +51 -17
- package/dist/components/identity-mark.d.ts +8 -1
- package/dist/components/identity-mark.d.ts.map +1 -1
- package/dist/components/identity-mark.js +31 -5
- package/dist/components/input-group.d.ts +44 -21
- package/dist/components/input-group.d.ts.map +1 -1
- package/dist/components/input-group.js +74 -27
- package/dist/components/input.d.ts +64 -12
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +51 -15
- package/dist/components/item.d.ts +37 -15
- package/dist/components/item.d.ts.map +1 -1
- package/dist/components/item.js +41 -22
- package/dist/components/kbd.d.ts +49 -13
- package/dist/components/kbd.d.ts.map +1 -1
- package/dist/components/kbd.js +115 -16
- package/dist/components/kpi-card.d.ts +12 -4
- package/dist/components/kpi-card.d.ts.map +1 -1
- package/dist/components/kpi-card.js +13 -4
- package/dist/components/label.d.ts +17 -10
- package/dist/components/label.d.ts.map +1 -1
- package/dist/components/label.js +18 -11
- package/dist/components/library-shelf.d.ts +6 -6
- package/dist/components/library-shelf.d.ts.map +1 -1
- package/dist/components/library-shelf.js +5 -5
- package/dist/components/load-more-button.d.ts +35 -0
- package/dist/components/load-more-button.d.ts.map +1 -0
- package/dist/components/load-more-button.js +26 -0
- package/dist/components/message-notice.d.ts +8 -13
- package/dist/components/message-notice.d.ts.map +1 -1
- package/dist/components/message-notice.js +25 -22
- package/dist/components/middle-truncate.d.ts +33 -0
- package/dist/components/middle-truncate.d.ts.map +1 -0
- package/dist/components/middle-truncate.js +72 -0
- package/dist/components/multi-select.d.ts +94 -0
- package/dist/components/multi-select.d.ts.map +1 -0
- package/dist/components/multi-select.js +132 -0
- package/dist/components/native-select.d.ts +37 -13
- package/dist/components/native-select.d.ts.map +1 -1
- package/dist/components/native-select.js +27 -11
- package/dist/components/notice-banner.d.ts +17 -10
- package/dist/components/notice-banner.d.ts.map +1 -1
- package/dist/components/notice-banner.js +12 -11
- package/dist/components/numbered-steps.d.ts +10 -4
- package/dist/components/numbered-steps.d.ts.map +1 -1
- package/dist/components/numbered-steps.js +9 -7
- package/dist/components/object-page.d.ts +40 -14
- package/dist/components/object-page.d.ts.map +1 -1
- package/dist/components/object-page.js +12 -10
- package/dist/components/object-workspace.d.ts +29 -10
- package/dist/components/object-workspace.d.ts.map +1 -1
- package/dist/components/object-workspace.js +18 -8
- package/dist/components/overview-page.d.ts +2 -2
- package/dist/components/overview-page.d.ts.map +1 -1
- package/dist/components/overview-page.js +8 -7
- package/dist/components/page-actions.d.ts +28 -8
- package/dist/components/page-actions.d.ts.map +1 -1
- package/dist/components/page-actions.js +22 -25
- package/dist/components/page-header.d.ts +11 -4
- package/dist/components/page-header.d.ts.map +1 -1
- package/dist/components/page-header.js +10 -5
- package/dist/components/pending-label.d.ts +6 -5
- package/dist/components/pending-label.d.ts.map +1 -1
- package/dist/components/pending-label.js +14 -6
- package/dist/components/picture-label.d.ts +2 -2
- package/dist/components/picture-label.d.ts.map +1 -1
- package/dist/components/picture-label.js +3 -3
- package/dist/components/portal-sidebar-frame.d.ts +9 -21
- package/dist/components/portal-sidebar-frame.d.ts.map +1 -1
- package/dist/components/portal-sidebar-frame.js +13 -27
- package/dist/components/progress-checklist.d.ts +46 -0
- package/dist/components/progress-checklist.d.ts.map +1 -0
- package/dist/components/progress-checklist.js +46 -0
- package/dist/components/progress.d.ts +48 -8
- package/dist/components/progress.d.ts.map +1 -1
- package/dist/components/progress.js +92 -16
- package/dist/components/prose.d.ts +19 -0
- package/dist/components/prose.d.ts.map +1 -0
- package/dist/components/prose.js +29 -0
- package/dist/components/radio-group.d.ts +51 -12
- package/dist/components/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group.js +42 -12
- package/dist/components/reading-column.js +1 -1
- package/dist/components/record-identity.d.ts +6 -5
- package/dist/components/record-identity.d.ts.map +1 -1
- package/dist/components/record-identity.js +12 -3
- package/dist/components/record-list.d.ts +117 -27
- package/dist/components/record-list.d.ts.map +1 -1
- package/dist/components/record-list.js +185 -55
- package/dist/components/record-time.d.ts +20 -7
- package/dist/components/record-time.d.ts.map +1 -1
- package/dist/components/record-time.js +38 -19
- package/dist/components/resizable.d.ts +7 -5
- package/dist/components/resizable.d.ts.map +1 -1
- package/dist/components/resizable.js +6 -6
- package/dist/components/scope-switcher.d.ts +2 -0
- package/dist/components/scope-switcher.d.ts.map +1 -1
- package/dist/components/scope-switcher.js +2 -2
- package/dist/components/scroll-area.d.ts +47 -11
- package/dist/components/scroll-area.d.ts.map +1 -1
- package/dist/components/scroll-area.js +72 -14
- package/dist/components/segmented-control.d.ts +82 -0
- package/dist/components/segmented-control.d.ts.map +1 -0
- package/dist/components/segmented-control.js +90 -0
- package/dist/components/select.d.ts +55 -15
- package/dist/components/select.d.ts.map +1 -1
- package/dist/components/select.js +62 -19
- package/dist/components/separator.d.ts +12 -5
- package/dist/components/separator.d.ts.map +1 -1
- package/dist/components/separator.js +11 -6
- package/dist/components/settings-facts.d.ts +34 -14
- package/dist/components/settings-facts.d.ts.map +1 -1
- package/dist/components/settings-facts.js +21 -12
- package/dist/components/settings-page.d.ts +4 -4
- package/dist/components/settings-page.js +5 -5
- package/dist/components/settings-section.d.ts +160 -34
- package/dist/components/settings-section.d.ts.map +1 -1
- package/dist/components/settings-section.js +92 -39
- package/dist/components/sheet.d.ts +47 -16
- package/dist/components/sheet.d.ts.map +1 -1
- package/dist/components/sheet.js +40 -19
- package/dist/components/sidebar-sections.d.ts +16 -12
- package/dist/components/sidebar-sections.d.ts.map +1 -1
- package/dist/components/sidebar-sections.js +30 -15
- package/dist/components/sidebar.d.ts +117 -21
- package/dist/components/sidebar.d.ts.map +1 -1
- package/dist/components/sidebar.js +180 -55
- package/dist/components/skeleton.d.ts +54 -8
- package/dist/components/skeleton.d.ts.map +1 -1
- package/dist/components/skeleton.js +44 -9
- package/dist/components/slider.d.ts +53 -11
- package/dist/components/slider.d.ts.map +1 -1
- package/dist/components/slider.js +71 -17
- package/dist/components/snippet.d.ts +57 -0
- package/dist/components/snippet.d.ts.map +1 -0
- package/dist/components/snippet.js +60 -0
- package/dist/components/sonner.d.ts +13 -14
- package/dist/components/sonner.d.ts.map +1 -1
- package/dist/components/sonner.js +25 -22
- package/dist/components/status-badge.d.ts +37 -11
- package/dist/components/status-badge.d.ts.map +1 -1
- package/dist/components/status-badge.js +63 -15
- package/dist/components/status-dot.d.ts +42 -11
- package/dist/components/status-dot.d.ts.map +1 -1
- package/dist/components/status-dot.js +40 -11
- package/dist/components/switch.d.ts +43 -12
- package/dist/components/switch.d.ts.map +1 -1
- package/dist/components/switch.js +42 -12
- package/dist/components/table-card.d.ts +21 -12
- package/dist/components/table-card.d.ts.map +1 -1
- package/dist/components/table-card.js +17 -9
- package/dist/components/table-layouts.d.ts +40 -8
- package/dist/components/table-layouts.d.ts.map +1 -1
- package/dist/components/table-layouts.js +52 -8
- package/dist/components/table-value.d.ts +4 -3
- package/dist/components/table-value.d.ts.map +1 -1
- package/dist/components/table-value.js +6 -5
- package/dist/components/table.d.ts +62 -25
- package/dist/components/table.d.ts.map +1 -1
- package/dist/components/table.js +62 -26
- package/dist/components/tabs.d.ts +86 -17
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +93 -22
- package/dist/components/text-link.d.ts +4 -1
- package/dist/components/text-link.d.ts.map +1 -1
- package/dist/components/text-link.js +4 -1
- package/dist/components/text-with-copy-button.d.ts +30 -0
- package/dist/components/text-with-copy-button.d.ts.map +1 -0
- package/dist/components/text-with-copy-button.js +32 -0
- package/dist/components/textarea.d.ts +40 -10
- package/dist/components/textarea.d.ts.map +1 -1
- package/dist/components/textarea.js +50 -11
- package/dist/components/theme-mode-switcher.d.ts +18 -1
- package/dist/components/theme-mode-switcher.d.ts.map +1 -1
- package/dist/components/theme-mode-switcher.js +20 -2
- package/dist/components/toggle-group.d.ts +28 -8
- package/dist/components/toggle-group.d.ts.map +1 -1
- package/dist/components/toggle-group.js +12 -6
- package/dist/components/toggle.d.ts +6 -3
- package/dist/components/toggle.d.ts.map +1 -1
- package/dist/components/toggle.js +10 -7
- package/dist/components/tooltip.d.ts +43 -12
- package/dist/components/tooltip.d.ts.map +1 -1
- package/dist/components/tooltip.js +45 -16
- package/dist/components/trend-badge.js +1 -1
- package/dist/components/update-card.js +1 -1
- package/dist/eslint/index.js +82 -0
- package/dist/eslint/rules/badge-is-static-label.js +93 -0
- package/dist/eslint/rules/button-icon-slot.js +102 -0
- package/dist/eslint/rules/button-link-for-navigation.js +146 -0
- package/dist/eslint/rules/button-no-aria-label-with-text.js +75 -0
- package/dist/eslint/rules/button-type-roles-only-in-buttons.js +83 -0
- package/dist/eslint/rules/choice-control-has-a-name.js +98 -0
- package/dist/eslint/rules/choice-group-in-fieldset.js +86 -0
- package/dist/eslint/rules/command-shortcut-single-owner.js +118 -0
- package/dist/eslint/rules/confirm-dialog-words.js +63 -0
- package/dist/eslint/rules/data-table-one-unsized-column.js +96 -0
- package/dist/eslint/rules/destructive-action-placement.js +69 -0
- package/dist/eslint/rules/disabled-choice-says-why.js +71 -0
- package/dist/eslint/rules/ellipsis-opens-a-dialog.js +110 -0
- package/dist/eslint/rules/input-group-owners.js +155 -0
- package/dist/eslint/rules/kbd-children-no-modifier-text.js +128 -0
- package/dist/eslint/rules/literal-text.js +41 -0
- package/dist/eslint/rules/middle-truncate-placement.js +69 -0
- package/dist/eslint/rules/no-bare-button-label.js +55 -0
- package/dist/eslint/rules/no-control-in-read-only-surface.js +106 -0
- package/dist/eslint/rules/no-hand-built-busy.js +104 -0
- package/dist/eslint/rules/no-hand-built-theme-picker.js +144 -0
- package/dist/eslint/rules/no-hand-built-tile.js +41 -0
- package/dist/eslint/rules/no-hand-formatted-date.js +87 -0
- package/dist/eslint/rules/no-hand-stacked-avatars.js +67 -0
- package/dist/eslint/rules/no-nested-collapse.js +60 -0
- package/dist/eslint/rules/no-search-in-form.js +47 -0
- package/dist/eslint/rules/no-shadcn-appearance-override.js +2 -16
- package/dist/eslint/rules/no-shell-prompt-in-snippet.js +74 -0
- package/dist/eslint/rules/no-stock-error-words.js +56 -0
- package/dist/eslint/rules/no-tooltip-around-labelled-field.js +72 -0
- package/dist/eslint/rules/no-weight-class-on-strong-in-type-role.js +57 -0
- package/dist/eslint/rules/one-submenu-level.js +43 -0
- package/dist/eslint/rules/one-toast-per-flow.js +91 -0
- package/dist/eslint/rules/option-count.js +167 -0
- package/dist/eslint/rules/page-command-link-not-render.js +43 -0
- package/dist/eslint/rules/progress-needs-name.js +57 -0
- package/dist/eslint/rules/settings-limit-in-footer.js +94 -0
- package/dist/eslint/rules/skeleton-in-region.js +80 -0
- package/dist/eslint/rules/sortable-heading-uses-sort.js +79 -0
- package/dist/eslint/rules/table-cell-inset-owner.js +107 -0
- package/dist/eslint/rules/tabs-selection-is-controlled.js +47 -0
- package/dist/eslint/rules/tooltip-only-arrow.js +38 -0
- package/dist/eslint/rules/tooltip-trigger-accessible-name.js +114 -0
- package/dist/eslint/strict-design-system.js +21 -7
- package/dist/index.d.ts +13 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -13
- package/dist/internal/assistant-ui/attachment.d.ts.map +1 -1
- package/dist/internal/assistant-ui/attachment.js +2 -2
- package/dist/internal/assistant-ui/reasoning.d.ts +1 -1
- package/dist/internal/assistant-ui/reasoning.d.ts.map +1 -1
- package/dist/internal/assistant-ui/tool-fallback.js +1 -1
- package/dist/internal/assistant-ui/tool-group.d.ts +1 -1
- package/dist/internal/assistant-ui/tool-group.d.ts.map +1 -1
- package/dist/internal/assistant-ui/tooltip-icon-button.d.ts +2 -3
- package/dist/internal/assistant-ui/tooltip-icon-button.d.ts.map +1 -1
- package/dist/internal/assistant-ui/tooltip-icon-button.js +1 -1
- package/dist/internal/catalog-list.js +2 -2
- package/dist/internal/collection-note.d.ts +23 -0
- package/dist/internal/collection-note.d.ts.map +1 -0
- package/dist/internal/collection-note.js +17 -0
- package/dist/internal/collection-rail.d.ts +57 -0
- package/dist/internal/collection-rail.d.ts.map +1 -0
- package/dist/internal/collection-rail.js +33 -0
- package/dist/internal/company-logo.d.ts +18 -0
- package/dist/internal/company-logo.d.ts.map +1 -0
- package/dist/internal/company-logo.js +14 -0
- package/dist/internal/control-name.d.ts +12 -0
- package/dist/internal/control-name.d.ts.map +1 -0
- package/dist/internal/control-name.js +1 -0
- package/dist/internal/diff-line.d.ts +9 -0
- package/dist/internal/diff-line.d.ts.map +1 -0
- package/dist/internal/diff-line.js +8 -0
- package/dist/internal/empty.d.ts +8 -4
- package/dist/internal/empty.d.ts.map +1 -1
- package/dist/internal/empty.js +14 -8
- package/dist/internal/field-frame.d.ts +33 -0
- package/dist/internal/field-frame.d.ts.map +1 -0
- package/dist/internal/field-frame.js +20 -0
- package/dist/internal/final-focus.d.ts +8 -0
- package/dist/internal/final-focus.d.ts.map +1 -0
- package/dist/internal/final-focus.js +10 -0
- package/dist/internal/footer-strip.d.ts.map +1 -1
- package/dist/internal/footer-strip.js +3 -1
- package/dist/internal/form-submit.d.ts +9 -5
- package/dist/internal/form-submit.d.ts.map +1 -1
- package/dist/internal/form-submit.js +31 -2
- package/dist/internal/input-box.d.ts +26 -0
- package/dist/internal/input-box.d.ts.map +1 -0
- package/dist/internal/input-box.js +59 -0
- package/dist/internal/menu-surface.d.ts +10 -3
- package/dist/internal/menu-surface.d.ts.map +1 -1
- package/dist/internal/menu-surface.js +10 -3
- package/dist/internal/notice.d.ts +25 -0
- package/dist/internal/notice.d.ts.map +1 -0
- package/dist/internal/notice.js +33 -0
- package/dist/internal/overlay-stem.d.ts +5 -0
- package/dist/internal/overlay-stem.d.ts.map +1 -0
- package/dist/internal/overlay-stem.js +7 -0
- package/dist/internal/phone-sheet.d.ts +4 -0
- package/dist/internal/phone-sheet.d.ts.map +1 -0
- package/dist/internal/phone-sheet.js +2 -0
- package/dist/internal/present-content.d.ts +3 -0
- package/dist/internal/present-content.d.ts.map +1 -0
- package/dist/internal/present-content.js +1 -0
- package/dist/internal/record-menu-items.d.ts +4 -3
- package/dist/internal/record-menu-items.d.ts.map +1 -1
- package/dist/internal/record-menu-items.js +21 -6
- package/dist/internal/settings-save.d.ts +9 -2
- package/dist/internal/settings-save.d.ts.map +1 -1
- package/dist/internal/settings-save.js +9 -6
- package/dist/internal/table-compact-line.d.ts +38 -0
- package/dist/internal/table-compact-line.d.ts.map +1 -0
- package/dist/internal/table-compact-line.js +35 -0
- package/dist/internal/title-when-cut.d.ts +8 -0
- package/dist/internal/title-when-cut.d.ts.map +1 -0
- package/dist/internal/title-when-cut.js +22 -0
- package/dist/internal/unsaved-guard.d.ts +18 -0
- package/dist/internal/unsaved-guard.d.ts.map +1 -0
- package/dist/internal/unsaved-guard.js +31 -0
- package/dist/lib/merge-tokens.generated.js +2 -2
- package/dist/showcase/foundations.d.ts.map +1 -1
- package/dist/showcase/foundations.js +10 -2
- package/dist/showcase/specimens/actions.d.ts +2 -0
- package/dist/showcase/specimens/actions.d.ts.map +1 -1
- package/dist/showcase/specimens/actions.js +30 -5
- package/dist/showcase/specimens/charts.d.ts.map +1 -1
- package/dist/showcase/specimens/charts.js +22 -13
- package/dist/showcase/specimens/choices.d.ts +14 -3
- package/dist/showcase/specimens/choices.d.ts.map +1 -1
- package/dist/showcase/specimens/choices.js +93 -13
- package/dist/showcase/specimens/compositions.d.ts.map +1 -1
- package/dist/showcase/specimens/compositions.js +136 -23
- package/dist/showcase/specimens/data.d.ts +12 -7
- package/dist/showcase/specimens/data.d.ts.map +1 -1
- package/dist/showcase/specimens/data.js +139 -35
- package/dist/showcase/specimens/feedback.d.ts +2 -0
- package/dist/showcase/specimens/feedback.d.ts.map +1 -1
- package/dist/showcase/specimens/feedback.js +39 -15
- package/dist/showcase/specimens/index.d.ts +20 -12
- package/dist/showcase/specimens/index.d.ts.map +1 -1
- package/dist/showcase/specimens/inputs.d.ts +4 -2
- package/dist/showcase/specimens/inputs.d.ts.map +1 -1
- package/dist/showcase/specimens/inputs.js +62 -19
- package/dist/showcase/specimens/menus.d.ts +0 -1
- package/dist/showcase/specimens/menus.d.ts.map +1 -1
- package/dist/showcase/specimens/menus.js +47 -17
- package/dist/showcase/specimens/navigation.d.ts +6 -4
- package/dist/showcase/specimens/navigation.d.ts.map +1 -1
- package/dist/showcase/specimens/navigation.js +61 -32
- package/dist/showcase/specimens/other.d.ts +1 -3
- package/dist/showcase/specimens/other.d.ts.map +1 -1
- package/dist/showcase/specimens/other.js +17 -13
- package/dist/showcase/specimens/status.d.ts +4 -1
- package/dist/showcase/specimens/status.d.ts.map +1 -1
- package/dist/showcase/specimens/status.js +64 -11
- package/dist/showcase/usage.generated.d.ts +1 -1
- package/dist/showcase/usage.generated.d.ts.map +1 -1
- package/dist/showcase/usage.generated.js +632 -544
- package/dist/standalone/index.js +1 -1
- package/dist/styles/components.css +13 -13
- package/dist/styles/globals.css +25 -0
- package/package.json +2 -3
- package/dist/components/approval-row.d.ts +0 -34
- package/dist/components/approval-row.d.ts.map +0 -1
- package/dist/components/approval-row.js +0 -27
- package/dist/components/channel-view.d.ts +0 -112
- package/dist/components/channel-view.d.ts.map +0 -1
- package/dist/components/channel-view.js +0 -84
- package/dist/components/dashboard-shell.d.ts +0 -51
- package/dist/components/dashboard-shell.d.ts.map +0 -1
- package/dist/components/dashboard-shell.js +0 -89
- package/dist/components/direction.d.ts +0 -2
- package/dist/components/direction.d.ts.map +0 -1
- package/dist/components/direction.js +0 -1
- package/dist/components/expandable-block.d.ts +0 -20
- package/dist/components/expandable-block.d.ts.map +0 -1
- package/dist/components/expandable-block.js +0 -32
- package/dist/components/form.d.ts +0 -42
- package/dist/components/form.d.ts.map +0 -1
- package/dist/components/form.js +0 -91
- package/dist/components/label-system.d.ts +0 -17
- package/dist/components/label-system.d.ts.map +0 -1
- package/dist/components/label-system.js +0 -21
- package/dist/components/memory-item.d.ts +0 -21
- package/dist/components/memory-item.d.ts.map +0 -1
- package/dist/components/memory-item.js +0 -18
- package/dist/components/menubar.d.ts +0 -43
- package/dist/components/menubar.d.ts.map +0 -1
- package/dist/components/menubar.js +0 -69
- package/dist/components/navigation-menu.d.ts +0 -26
- package/dist/components/navigation-menu.d.ts.map +0 -1
- package/dist/components/navigation-menu.js +0 -45
- package/dist/components/pagination.d.ts +0 -33
- package/dist/components/pagination.d.ts.map +0 -1
- package/dist/components/pagination.js +0 -41
- package/dist/components/plugin-slot-boundary.d.ts +0 -42
- package/dist/components/plugin-slot-boundary.d.ts.map +0 -1
- package/dist/components/plugin-slot-boundary.js +0 -41
- package/dist/components/status-row.d.ts +0 -32
- package/dist/components/status-row.d.ts.map +0 -1
- package/dist/components/status-row.js +0 -35
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: `no-restricted-imports` can hold `buttonVariants`
|
|
3
|
+
* back, but not tell the Buzz tree's own `buttonVariants` from the package's, and
|
|
4
|
+
* a `no-restricted-syntax` selector cannot follow a renamed `Button` import to
|
|
5
|
+
* the `onClick` and `render` it reads.
|
|
6
|
+
*/
|
|
7
|
+
import { PACKAGE_BARREL, partOf, renderedImports, writtenName } from "./rendered-import.js";
|
|
8
|
+
|
|
9
|
+
const BUTTON_MODULES = ["@nadicodeai/ui/components/button", PACKAGE_BARREL];
|
|
10
|
+
|
|
11
|
+
const BUTTON = new Map([["Button", BUTTON_MODULES.map((from) => ({ from, part: "Button" }))]]);
|
|
12
|
+
|
|
13
|
+
const LINK_ELEMENTS = new Set(["a", "Link", "NextLink"]);
|
|
14
|
+
const ROUTER_METHODS = new Set(["push", "replace"]);
|
|
15
|
+
const FUNCTIONS = new Set(["FunctionExpression", "ArrowFunctionExpression"]);
|
|
16
|
+
|
|
17
|
+
function variantsBindings(context) {
|
|
18
|
+
const names = new Set();
|
|
19
|
+
const namespaces = new Set();
|
|
20
|
+
for (const statement of context.sourceCode.ast.body) {
|
|
21
|
+
if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
|
|
22
|
+
if (!BUTTON_MODULES.includes(statement.source.value)) continue;
|
|
23
|
+
for (const specifier of statement.specifiers) {
|
|
24
|
+
if (specifier.importKind === "type") continue;
|
|
25
|
+
if (specifier.type === "ImportNamespaceSpecifier") namespaces.add(specifier.local.name);
|
|
26
|
+
else if (
|
|
27
|
+
specifier.type === "ImportSpecifier" &&
|
|
28
|
+
(specifier.imported.name ?? specifier.imported.value) === "buttonVariants"
|
|
29
|
+
) {
|
|
30
|
+
names.add(specifier.local.name);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return (callee) => {
|
|
35
|
+
if (callee.type === "Identifier") return names.has(callee.name);
|
|
36
|
+
return (
|
|
37
|
+
callee.type === "MemberExpression" &&
|
|
38
|
+
!callee.computed &&
|
|
39
|
+
callee.object.type === "Identifier" &&
|
|
40
|
+
namespaces.has(callee.object.name) &&
|
|
41
|
+
callee.property.name === "buttonVariants"
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const unwrap = (node) => {
|
|
47
|
+
if (node.type === "AwaitExpression") return unwrap(node.argument);
|
|
48
|
+
if (node.type === "UnaryExpression" && node.operator === "void") return unwrap(node.argument);
|
|
49
|
+
if (node.type === "ChainExpression") return unwrap(node.expression);
|
|
50
|
+
return node;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const isLocationTarget = (node) => {
|
|
54
|
+
if (node.type !== "MemberExpression" || node.computed) return false;
|
|
55
|
+
const { object, property } = node;
|
|
56
|
+
if (object.type === "Identifier") {
|
|
57
|
+
if (object.name === "window") return property.name === "location";
|
|
58
|
+
return object.name === "location" && property.name === "href";
|
|
59
|
+
}
|
|
60
|
+
return (
|
|
61
|
+
isLocationTarget(object) && object.property.name === "location" && property.name === "href"
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function goesToAnAddress(expression) {
|
|
66
|
+
const node = unwrap(expression);
|
|
67
|
+
if (node.type === "AssignmentExpression") return isLocationTarget(node.left);
|
|
68
|
+
if (node.type !== "CallExpression") return false;
|
|
69
|
+
const { callee } = node;
|
|
70
|
+
if (callee.type === "Identifier") return callee.name === "navigate";
|
|
71
|
+
return (
|
|
72
|
+
callee.type === "MemberExpression" &&
|
|
73
|
+
!callee.computed &&
|
|
74
|
+
callee.object.type === "Identifier" &&
|
|
75
|
+
callee.object.name === "router" &&
|
|
76
|
+
ROUTER_METHODS.has(callee.property.name)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function onlyGoesToAnAddress(fn) {
|
|
81
|
+
if (fn.body.type !== "BlockStatement") return goesToAnAddress(fn.body);
|
|
82
|
+
const { body } = fn.body;
|
|
83
|
+
return (
|
|
84
|
+
body.length > 0 &&
|
|
85
|
+
body.every(
|
|
86
|
+
(statement) =>
|
|
87
|
+
statement.type === "ExpressionStatement" && goesToAnAddress(statement.expression),
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const attributeValue = (openingElement, attributeName) => {
|
|
93
|
+
const attribute = openingElement.attributes.find(
|
|
94
|
+
(candidate) => candidate.type === "JSXAttribute" && candidate.name.name === attributeName,
|
|
95
|
+
);
|
|
96
|
+
return attribute?.value?.type === "JSXExpressionContainer" ? attribute.value.expression : null;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const isLinkElement = (node) =>
|
|
100
|
+
node?.type === "JSXElement" && LINK_ELEMENTS.has(writtenName(node.openingElement.name) ?? "");
|
|
101
|
+
|
|
102
|
+
function enclosingAttribute(node) {
|
|
103
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
104
|
+
if (current.type === "JSXAttribute") return current;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const buttonLinkForNavigation = {
|
|
110
|
+
meta: {
|
|
111
|
+
type: "problem",
|
|
112
|
+
docs: {
|
|
113
|
+
description: "A control that goes to an address is a ButtonLink; a Button runs a command.",
|
|
114
|
+
},
|
|
115
|
+
schema: [],
|
|
116
|
+
messages: {
|
|
117
|
+
"navigating-button":
|
|
118
|
+
"A control that goes to an address is a ButtonLink; a Button runs a command.",
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
create(context) {
|
|
122
|
+
const importOf = renderedImports(context);
|
|
123
|
+
const isButtonVariants = variantsBindings(context);
|
|
124
|
+
return {
|
|
125
|
+
CallExpression(node) {
|
|
126
|
+
if (!isButtonVariants(node.callee)) return;
|
|
127
|
+
const attribute = enclosingAttribute(node);
|
|
128
|
+
if (attribute?.name.name !== "className") return;
|
|
129
|
+
if (isLinkElement(attribute.parent.parent)) {
|
|
130
|
+
context.report({ node, messageId: "navigating-button" });
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
JSXOpeningElement(node) {
|
|
134
|
+
if (partOf(importOf(node.name), BUTTON, context.filename) !== "Button") return;
|
|
135
|
+
const onClick = attributeValue(node, "onClick");
|
|
136
|
+
if (onClick && FUNCTIONS.has(onClick.type) && onlyGoesToAnAddress(onClick)) {
|
|
137
|
+
context.report({ node, messageId: "navigating-button" });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (isLinkElement(attributeValue(node, "render"))) {
|
|
141
|
+
context.report({ node, messageId: "navigating-button" });
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: a `no-restricted-syntax` selector can match
|
|
3
|
+
* `JSXElement[openingElement.name.name='Button']:has(JSXAttribute[name.name='aria-label'])`,
|
|
4
|
+
* but not that the element is the package's Button rather than a local one with
|
|
5
|
+
* the same name, nor that a child is text rather than an icon, nor the `size`
|
|
6
|
+
* that makes a non-literal child an icon-only label. Those need the import
|
|
7
|
+
* binding and the children read together.
|
|
8
|
+
*/
|
|
9
|
+
import { PACKAGE_BARREL, partOf, renderedImports, staticAttribute } from "./rendered-import.js";
|
|
10
|
+
|
|
11
|
+
const BUTTONS = new Map(
|
|
12
|
+
["Button", "ButtonLink"].map((name) => [
|
|
13
|
+
name,
|
|
14
|
+
["@nadicodeai/ui/components/button", PACKAGE_BARREL].map((from) => ({ from, part: name })),
|
|
15
|
+
]),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const NAMING_ATTRIBUTES = new Set(["aria-label", "aria-labelledby"]);
|
|
19
|
+
|
|
20
|
+
const namesItself = (openingElement) =>
|
|
21
|
+
openingElement.attributes.some(
|
|
22
|
+
(attribute) => attribute.type === "JSXAttribute" && NAMING_ATTRIBUTES.has(attribute.name.name),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const isIconSize = (openingElement) =>
|
|
26
|
+
String(staticAttribute(openingElement, "size") ?? "").startsWith("icon");
|
|
27
|
+
|
|
28
|
+
function staticText(expression) {
|
|
29
|
+
if (expression.type === "Literal") {
|
|
30
|
+
// oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
|
|
31
|
+
return typeof expression.value === "string" ? expression.value : null;
|
|
32
|
+
}
|
|
33
|
+
if (expression.type === "TemplateLiteral" && expression.expressions.length === 0) {
|
|
34
|
+
return expression.quasis[0].value.cooked;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function childText(child) {
|
|
40
|
+
if (child.type === "JSXText") return child.value.trim() === "" ? "none" : "visible";
|
|
41
|
+
if (child.type !== "JSXExpressionContainer") return "none";
|
|
42
|
+
const { expression } = child;
|
|
43
|
+
if (expression.type === "JSXEmptyExpression") return "none";
|
|
44
|
+
const text = staticText(expression);
|
|
45
|
+
if (text !== null) return text.trim() === "" ? "none" : "visible";
|
|
46
|
+
return expression.type === "Literal" ? "none" : "computed";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const buttonNoAriaLabelWithText = {
|
|
50
|
+
meta: {
|
|
51
|
+
type: "problem",
|
|
52
|
+
docs: {
|
|
53
|
+
description: "A Button with visible text is named by its text, never by an aria-label.",
|
|
54
|
+
},
|
|
55
|
+
schema: [],
|
|
56
|
+
messages: {
|
|
57
|
+
"named-by-its-text":
|
|
58
|
+
"A Button with visible text is named by its text; remove aria-label. When the text alone does not name the target, use `accessibleName` on a PageCommand.",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
create(context) {
|
|
62
|
+
const importOf = renderedImports(context);
|
|
63
|
+
return {
|
|
64
|
+
JSXElement(node) {
|
|
65
|
+
const { openingElement } = node;
|
|
66
|
+
if (!partOf(importOf(openingElement.name), BUTTONS, context.filename)) return;
|
|
67
|
+
if (!namesItself(openingElement)) return;
|
|
68
|
+
const kinds = node.children.map(childText);
|
|
69
|
+
const showsText =
|
|
70
|
+
kinds.includes("visible") || (kinds.includes("computed") && !isIconSize(openingElement));
|
|
71
|
+
if (showsText) context.report({ node: openingElement, messageId: "named-by-its-text" });
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: `no-restricted-syntax` can match a class token
|
|
3
|
+
* in a `className` literal (strict-design-system.js does), but not which file
|
|
4
|
+
* the token is in, so it cannot allow the files that render a button and refuse
|
|
5
|
+
* the rest, nor tell the small role's two files from the other four.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const BUTTON_ROLE = /(?:^|:)!?(text-button(?:-lg|-xs)?|nc-type-button(?:-[a-z0-9]+)*)!?$/;
|
|
9
|
+
const SMALL_ROLE = /-xs$/;
|
|
10
|
+
const CLASS_FUNCTIONS = new Set(["cva", "cn"]);
|
|
11
|
+
|
|
12
|
+
const inFiles = (filename, files) => {
|
|
13
|
+
const path = filename.replaceAll("\\", "/");
|
|
14
|
+
return files.some((file) => path === file || path.endsWith(`/${file}`));
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const classSite = (node) => {
|
|
18
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
19
|
+
if (current.type === "JSXAttribute") return current.name.name === "className";
|
|
20
|
+
if (
|
|
21
|
+
current.type === "CallExpression" &&
|
|
22
|
+
current.callee.type === "Identifier" &&
|
|
23
|
+
CLASS_FUNCTIONS.has(current.callee.name)
|
|
24
|
+
) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const isVariantKey = (node) => node.parent.type === "Property" && node.parent.key === node;
|
|
32
|
+
|
|
33
|
+
export const buttonTypeRolesOnlyInButtons = {
|
|
34
|
+
meta: {
|
|
35
|
+
type: "problem",
|
|
36
|
+
docs: {
|
|
37
|
+
description:
|
|
38
|
+
"The button type roles label a component that renders a button, in the files that render one.",
|
|
39
|
+
},
|
|
40
|
+
schema: [
|
|
41
|
+
{
|
|
42
|
+
type: "object",
|
|
43
|
+
properties: {
|
|
44
|
+
allow: { type: "array", items: { type: "string" }, uniqueItems: true },
|
|
45
|
+
allowSmall: { type: "array", items: { type: "string" }, uniqueItems: true },
|
|
46
|
+
},
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
defaultOptions: [{ allow: [], allowSmall: [] }],
|
|
51
|
+
messages: {
|
|
52
|
+
"button-role":
|
|
53
|
+
"Button type roles label a component that renders a button. Use a body-* or label-* role for other text; text-button-xs is only for the small button inside a field.",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
create(context) {
|
|
57
|
+
const [{ allow, allowSmall }] = context.options;
|
|
58
|
+
const inAllowed = inFiles(context.filename, allow);
|
|
59
|
+
const inSmallAllowed = inFiles(context.filename, allowSmall);
|
|
60
|
+
|
|
61
|
+
const refused = (value) =>
|
|
62
|
+
value
|
|
63
|
+
.split(/\s+/)
|
|
64
|
+
.map((utility) => BUTTON_ROLE.exec(utility)?.[1])
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.some((role) => (SMALL_ROLE.test(role) ? !inSmallAllowed : !inAllowed));
|
|
67
|
+
|
|
68
|
+
function check(node, value) {
|
|
69
|
+
if (isVariantKey(node) || !classSite(node)) return;
|
|
70
|
+
if (refused(value)) context.report({ node, messageId: "button-role" });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
Literal(node) {
|
|
75
|
+
// oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
|
|
76
|
+
if (typeof node.value === "string") check(node, node.value);
|
|
77
|
+
},
|
|
78
|
+
TemplateElement(node) {
|
|
79
|
+
check(node, node.value.cooked ?? node.value.raw);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: `jsx-a11y/label-has-associated-control` checks a
|
|
3
|
+
* Label for a control, not a control for a name, and a `no-restricted-syntax`
|
|
4
|
+
* selector cannot tell that the package's Checkbox is meant rather than a local
|
|
5
|
+
* one, that a Label elsewhere in the file points at its id through `htmlFor`,
|
|
6
|
+
* or that a labelling ancestor wraps it. Those need the import bindings, the
|
|
7
|
+
* ancestors and the file's labels read together.
|
|
8
|
+
*/
|
|
9
|
+
import { packageModules, partOf, renderedImports, staticAttribute } from "./rendered-import.js";
|
|
10
|
+
|
|
11
|
+
const partsIn = (module, names) =>
|
|
12
|
+
names.map((name) => [
|
|
13
|
+
name,
|
|
14
|
+
[...packageModules(module), `@/components/${module}`].map((from) => ({ from, part: name })),
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
const CONTROLS = new Map([
|
|
18
|
+
...partsIn("checkbox", ["Checkbox"]),
|
|
19
|
+
...partsIn("switch", ["Switch"]),
|
|
20
|
+
...partsIn("radio-group", ["RadioGroupItem"]),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const LABELS = new Map([...partsIn("label", ["Label"]), ...partsIn("field", ["FieldLabel"])]);
|
|
24
|
+
|
|
25
|
+
const NAMING_ANCESTORS = new Map([...LABELS, ...partsIn("settings-section", ["SettingsField"])]);
|
|
26
|
+
|
|
27
|
+
const NAMING_ATTRIBUTES = new Set(["aria-label", "aria-labelledby"]);
|
|
28
|
+
|
|
29
|
+
const findAttribute = (openingElement, name) =>
|
|
30
|
+
openingElement.attributes.find(
|
|
31
|
+
(attribute) => attribute.type === "JSXAttribute" && attribute.name.name === name,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const hasSpread = (openingElement) =>
|
|
35
|
+
openingElement.attributes.some((attribute) => attribute.type === "JSXSpreadAttribute");
|
|
36
|
+
|
|
37
|
+
export const choiceControlHasAName = {
|
|
38
|
+
meta: {
|
|
39
|
+
type: "problem",
|
|
40
|
+
docs: {
|
|
41
|
+
description:
|
|
42
|
+
"A Checkbox, Switch or RadioGroupItem is named by an aria-label, an aria-labelledby, a Label with htmlFor, or a labelling ancestor.",
|
|
43
|
+
},
|
|
44
|
+
schema: [],
|
|
45
|
+
messages: {
|
|
46
|
+
unnamed:
|
|
47
|
+
"Name this control: give it an aria-label (a row's checkbox: Select <row name>), an aria-labelledby, or a Label with htmlFor.",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
create(context) {
|
|
51
|
+
const importOf = renderedImports(context);
|
|
52
|
+
const isPart = (parts) => (openingElement) =>
|
|
53
|
+
Boolean(partOf(importOf(openingElement.name), parts, context.filename));
|
|
54
|
+
const isControl = isPart(CONTROLS);
|
|
55
|
+
const isLabel = isPart(LABELS);
|
|
56
|
+
const isNamingAncestor = isPart(NAMING_ANCESTORS);
|
|
57
|
+
|
|
58
|
+
const reference = (openingElement, attributeName) => {
|
|
59
|
+
const attribute = findAttribute(openingElement, attributeName);
|
|
60
|
+
if (!attribute) return null;
|
|
61
|
+
const stated = staticAttribute(openingElement, attributeName);
|
|
62
|
+
if (stated !== undefined) return String(stated);
|
|
63
|
+
return attribute.value ? context.sourceCode.getText(attribute.value) : null;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const controls = [];
|
|
67
|
+
const pointedAt = new Set();
|
|
68
|
+
|
|
69
|
+
const hasNamingAncestor = (element) => {
|
|
70
|
+
for (let current = element.parent; current; current = current.parent) {
|
|
71
|
+
if (current.type === "JSXElement" && isNamingAncestor(current.openingElement)) return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
JSXElement(node) {
|
|
78
|
+
const { openingElement } = node;
|
|
79
|
+
if (isLabel(openingElement)) {
|
|
80
|
+
const target = reference(openingElement, "htmlFor");
|
|
81
|
+
if (target !== null) pointedAt.add(target);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!isControl(openingElement)) return;
|
|
85
|
+
if (hasSpread(openingElement)) return;
|
|
86
|
+
if (openingElement.attributes.some((a) => NAMING_ATTRIBUTES.has(a.name?.name))) return;
|
|
87
|
+
if (hasNamingAncestor(node)) return;
|
|
88
|
+
controls.push({ node: openingElement, id: reference(openingElement, "id") });
|
|
89
|
+
},
|
|
90
|
+
"Program:exit"() {
|
|
91
|
+
for (const { node, id } of controls) {
|
|
92
|
+
if (id !== null && pointedAt.has(id)) continue;
|
|
93
|
+
context.report({ node, messageId: "unnamed" });
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: a `no-restricted-syntax` selector can match a
|
|
3
|
+
* `RadioGroup` without a `FieldSet` ancestor, but not that the `FieldSet`
|
|
4
|
+
* holds a `FieldLegend`, nor that two `Checkbox` elements share a parent once
|
|
5
|
+
* each sits in its own `Field`, so it would miss the checkbox group.
|
|
6
|
+
*/
|
|
7
|
+
import { findBelow, renderedImports } from "./rendered-import.js";
|
|
8
|
+
|
|
9
|
+
const FROM_DESIGN_SYSTEM = /^(@nadicodeai\/ui(\/|$)|@\/|\.)/;
|
|
10
|
+
const ROW_WRAPPERS = new Set(["Field", "FieldContent", "FieldLabel", "Label"]);
|
|
11
|
+
|
|
12
|
+
const isRepeater = (node) =>
|
|
13
|
+
(node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") &&
|
|
14
|
+
node.parent.type === "CallExpression" &&
|
|
15
|
+
node.parent.callee.type === "MemberExpression" &&
|
|
16
|
+
node.parent.callee.property.name === "map";
|
|
17
|
+
|
|
18
|
+
export const choiceGroupInFieldset = {
|
|
19
|
+
meta: {
|
|
20
|
+
type: "problem",
|
|
21
|
+
docs: {
|
|
22
|
+
description: "A group of radios or checkboxes sits in a FieldSet with a FieldLegend.",
|
|
23
|
+
},
|
|
24
|
+
schema: [],
|
|
25
|
+
messages: {
|
|
26
|
+
"choice-group":
|
|
27
|
+
"A group of radios or checkboxes sits in a FieldSet with a FieldLegend, so the group is announced by its legend.",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
create(context) {
|
|
31
|
+
const importOf = renderedImports(context);
|
|
32
|
+
const exportOf = (element) => {
|
|
33
|
+
const imported = importOf(element.openingElement.name);
|
|
34
|
+
return imported && FROM_DESIGN_SYSTEM.test(imported.source) ? imported.name : null;
|
|
35
|
+
};
|
|
36
|
+
const checkboxGroups = new Map();
|
|
37
|
+
|
|
38
|
+
function hasLegendedFieldSet(node) {
|
|
39
|
+
for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
|
|
40
|
+
if (ancestor.type !== "JSXElement" || exportOf(ancestor) !== "FieldSet") continue;
|
|
41
|
+
const legend = findBelow(
|
|
42
|
+
ancestor,
|
|
43
|
+
context.sourceCode.visitorKeys,
|
|
44
|
+
(child) => child.type === "JSXElement" && exportOf(child) === "FieldLegend",
|
|
45
|
+
);
|
|
46
|
+
if (legend) return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function reportUnlessGrouped(element) {
|
|
52
|
+
if (!hasLegendedFieldSet(element)) {
|
|
53
|
+
context.report({ node: element.openingElement, messageId: "choice-group" });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function recordCheckbox(element) {
|
|
58
|
+
let repeated = false;
|
|
59
|
+
let parent = element.parent;
|
|
60
|
+
while (parent) {
|
|
61
|
+
if (isRepeater(parent)) repeated = true;
|
|
62
|
+
const isRow = parent.type === "JSXElement" && ROW_WRAPPERS.has(exportOf(parent));
|
|
63
|
+
const isGroup = (parent.type === "JSXElement" || parent.type === "JSXFragment") && !isRow;
|
|
64
|
+
if (isGroup) break;
|
|
65
|
+
parent = parent.parent;
|
|
66
|
+
}
|
|
67
|
+
if (!parent) return;
|
|
68
|
+
const group = checkboxGroups.get(parent) ?? { first: element, count: 0 };
|
|
69
|
+
group.count += repeated ? 2 : 1;
|
|
70
|
+
checkboxGroups.set(parent, group);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
JSXElement(node) {
|
|
75
|
+
const name = exportOf(node);
|
|
76
|
+
if (name === "RadioGroup") reportUnlessGrouped(node);
|
|
77
|
+
else if (name === "Checkbox") recordCheckbox(node);
|
|
78
|
+
},
|
|
79
|
+
"Program:exit"() {
|
|
80
|
+
for (const { first, count } of checkboxGroups.values()) {
|
|
81
|
+
if (count >= 2) reportUnlessGrouped(first);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: a `no-restricted-syntax` selector can match the
|
|
3
|
+
* string `"mod+k"`, but not a handler that reads `key` and `metaKey` as two
|
|
4
|
+
* separate checks (`(e.metaKey || e.ctrlKey) && e.key === "k"`), because the
|
|
5
|
+
* two reads only mean a shortcut together, in the same function; and it cannot
|
|
6
|
+
* allow the files that own Find.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const SHORTCUT_STRING = /^(?:mod|meta|cmd|ctrl)\+k$/i;
|
|
10
|
+
const MODIFIER_PROPERTIES = new Set(["metaKey", "ctrlKey"]);
|
|
11
|
+
const CASE_METHODS = new Set(["toLowerCase", "toUpperCase"]);
|
|
12
|
+
const KEY_VALUES = { key: new Set(["k", "K"]), code: new Set(["KeyK"]) };
|
|
13
|
+
|
|
14
|
+
const inFiles = (filename, files) => {
|
|
15
|
+
const path = filename.replaceAll("\\", "/");
|
|
16
|
+
return files.some((file) => path === file || path.endsWith(`/${file}`));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const unwrapChain = (node) => (node.type === "ChainExpression" ? node.expression : node);
|
|
20
|
+
|
|
21
|
+
const propertyName = (member) =>
|
|
22
|
+
member.type === "MemberExpression" && !member.computed ? member.property.name : null;
|
|
23
|
+
|
|
24
|
+
function keyRead(node) {
|
|
25
|
+
const read = unwrapChain(node);
|
|
26
|
+
if (read.type === "CallExpression") {
|
|
27
|
+
const callee = unwrapChain(read.callee);
|
|
28
|
+
if (propertyName(callee) && CASE_METHODS.has(propertyName(callee))) {
|
|
29
|
+
return keyRead(callee.object);
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const name = propertyName(read);
|
|
34
|
+
return name && name in KEY_VALUES ? name : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const isLetterK = (read, literal) =>
|
|
38
|
+
literal.type === "Literal" &&
|
|
39
|
+
// oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
|
|
40
|
+
typeof literal.value === "string" &&
|
|
41
|
+
KEY_VALUES[read].has(literal.value);
|
|
42
|
+
|
|
43
|
+
const COMPARISONS = new Set(["===", "==", "!==", "!="]);
|
|
44
|
+
|
|
45
|
+
export const commandShortcutSingleOwner = {
|
|
46
|
+
meta: {
|
|
47
|
+
type: "problem",
|
|
48
|
+
docs: {
|
|
49
|
+
description: "Only the files that own Find bind the command-and-K shortcut.",
|
|
50
|
+
},
|
|
51
|
+
schema: [
|
|
52
|
+
{
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
owners: { type: "array", items: { type: "string" }, uniqueItems: true },
|
|
56
|
+
},
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
defaultOptions: [{ owners: [] }],
|
|
61
|
+
messages: {
|
|
62
|
+
"find-shortcut": "⌘K and Ctrl+K belong to Find. Bind an in-page field to another key.",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
create(context) {
|
|
66
|
+
if (inFiles(context.filename, context.options[0].owners)) return {};
|
|
67
|
+
|
|
68
|
+
const frames = [{ letterKs: [], readsModifier: false }];
|
|
69
|
+
const frame = () => frames.at(-1);
|
|
70
|
+
|
|
71
|
+
const enter = () => frames.push({ letterKs: [], readsModifier: false });
|
|
72
|
+
const leave = () => {
|
|
73
|
+
const { letterKs, readsModifier } = frames.pop();
|
|
74
|
+
if (!readsModifier) return;
|
|
75
|
+
for (const node of letterKs) context.report({ node, messageId: "find-shortcut" });
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const checkString = (node, value) => {
|
|
79
|
+
if (SHORTCUT_STRING.test(value.trim())) context.report({ node, messageId: "find-shortcut" });
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const noteLetterK = (node, read, other) => {
|
|
83
|
+
const name = keyRead(read);
|
|
84
|
+
if (name && isLetterK(name, other)) frame().letterKs.push(node);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
FunctionDeclaration: enter,
|
|
89
|
+
FunctionExpression: enter,
|
|
90
|
+
ArrowFunctionExpression: enter,
|
|
91
|
+
"FunctionDeclaration:exit": leave,
|
|
92
|
+
"FunctionExpression:exit": leave,
|
|
93
|
+
"ArrowFunctionExpression:exit": leave,
|
|
94
|
+
"Program:exit"() {
|
|
95
|
+
leave();
|
|
96
|
+
},
|
|
97
|
+
MemberExpression(node) {
|
|
98
|
+
if (MODIFIER_PROPERTIES.has(propertyName(node))) frame().readsModifier = true;
|
|
99
|
+
},
|
|
100
|
+
BinaryExpression(node) {
|
|
101
|
+
if (!COMPARISONS.has(node.operator)) return;
|
|
102
|
+
noteLetterK(node, node.left, node.right);
|
|
103
|
+
noteLetterK(node, node.right, node.left);
|
|
104
|
+
},
|
|
105
|
+
SwitchCase(node) {
|
|
106
|
+
if (node.test) noteLetterK(node, node.parent.discriminant, node.test);
|
|
107
|
+
},
|
|
108
|
+
Literal(node) {
|
|
109
|
+
// oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
|
|
110
|
+
if (typeof node.value === "string") checkString(node, node.value);
|
|
111
|
+
},
|
|
112
|
+
TemplateElement(node) {
|
|
113
|
+
if (node.parent.type === "TemplateLiteral" && node.parent.expressions.length === 0)
|
|
114
|
+
checkString(node, node.value.cooked ?? "");
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: `no-restricted-syntax` reads one attribute at a
|
|
3
|
+
* time, and this check compares two of them on the same element (the title's
|
|
4
|
+
* verb with the confirm label's), and must know the element is the package's
|
|
5
|
+
* `ConfirmDialog` or the Portal's wrapper of it, not a local component.
|
|
6
|
+
*/
|
|
7
|
+
import { stringAttribute } from "./literal-text.js";
|
|
8
|
+
import { renderedImports, writtenName } from "./rendered-import.js";
|
|
9
|
+
|
|
10
|
+
const DIALOGS = new Set(["ConfirmDialog", "ConfirmedActionForm"]);
|
|
11
|
+
const QUESTION_OPENING = /^(?:are you sure|do you (?:really )?want|would you like|should)\b/i;
|
|
12
|
+
|
|
13
|
+
const words = (text) => text.trim().split(/\s+/).filter(Boolean);
|
|
14
|
+
|
|
15
|
+
export const confirmDialogWords = {
|
|
16
|
+
meta: {
|
|
17
|
+
type: "problem",
|
|
18
|
+
docs: {
|
|
19
|
+
description:
|
|
20
|
+
"A ConfirmDialog's title is a statement, and its confirm label repeats the title's verb with the noun.",
|
|
21
|
+
},
|
|
22
|
+
schema: [],
|
|
23
|
+
messages: {
|
|
24
|
+
"title-is-a-question":
|
|
25
|
+
'The title of a confirmation is a statement that names the record, "Remove pec@acme.it", never a question.',
|
|
26
|
+
"confirm-label-bare":
|
|
27
|
+
'The confirm label repeats the verb and the noun, "Remove mailbox"; "{{label}}" alone does not say what is confirmed.',
|
|
28
|
+
"confirm-verb-differs":
|
|
29
|
+
'The confirm label repeats the title\'s verb: the title says "{{title}}", so the label starts with "{{verb}}", not "{{label}}".',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
create(context) {
|
|
33
|
+
const importOf = renderedImports(context);
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
JSXOpeningElement(node) {
|
|
37
|
+
if (!importOf(node.name) || !DIALOGS.has(writtenName(node.name))) return;
|
|
38
|
+
const titleText = stringAttribute(node, "title");
|
|
39
|
+
const labelText = stringAttribute(node, "confirmLabel");
|
|
40
|
+
if (
|
|
41
|
+
titleText !== null &&
|
|
42
|
+
(titleText.trim().endsWith("?") || QUESTION_OPENING.test(titleText.trim()))
|
|
43
|
+
) {
|
|
44
|
+
context.report({ node, messageId: "title-is-a-question" });
|
|
45
|
+
}
|
|
46
|
+
if (labelText === null) return;
|
|
47
|
+
const labelWords = words(labelText);
|
|
48
|
+
if (labelWords.length < 2) {
|
|
49
|
+
context.report({ node, messageId: "confirm-label-bare", data: { label: labelText } });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const verb = titleText === null ? undefined : words(titleText)[0];
|
|
53
|
+
if (verb !== undefined && verb.toLowerCase() !== labelWords[0].toLowerCase()) {
|
|
54
|
+
context.report({
|
|
55
|
+
node,
|
|
56
|
+
messageId: "confirm-verb-differs",
|
|
57
|
+
data: { title: titleText, verb, label: labelText },
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
};
|