@nadicodeai/ui 12.1.102 → 12.1.104
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 +19 -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,167 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: a `no-restricted-syntax` selector can count the
|
|
3
|
+
* children of one element, but not follow a renamed import to the part, count
|
|
4
|
+
* the items below wrappers, or tell a literal list from one that a `map`,
|
|
5
|
+
* a spread or a condition makes.
|
|
6
|
+
*/
|
|
7
|
+
import { findBelow, renderedImports, walk } from "./rendered-import.js";
|
|
8
|
+
|
|
9
|
+
const GROUPS = new Map([
|
|
10
|
+
["RadioGroup", { item: "RadioGroupItem", min: 2, max: 6 }],
|
|
11
|
+
["ChoiceboxGroup", { item: "ChoiceboxGroupItem", max: 6 }],
|
|
12
|
+
["MultiSelect", { item: "MultiSelectRow", min: 2 }],
|
|
13
|
+
["MultiSelectContent", { item: "MultiSelectRow", min: 2 }],
|
|
14
|
+
]);
|
|
15
|
+
const COMMAND_PROPS = new Map([
|
|
16
|
+
["ActionMenu", "commands"],
|
|
17
|
+
["ActionContextMenu", "commands"],
|
|
18
|
+
["PageActions", "menu"],
|
|
19
|
+
["PageActionsRow", "menu"],
|
|
20
|
+
]);
|
|
21
|
+
const MENU_TYPES = new Set(["RecordMenu", "PageMenu"]);
|
|
22
|
+
const MAX_COMMANDS = 10;
|
|
23
|
+
const LIST_CALLBACKS = new Set(["map", "flatMap"]);
|
|
24
|
+
|
|
25
|
+
const isChildPosition = (node) =>
|
|
26
|
+
node.parent.type === "JSXElement" || node.parent.type === "JSXFragment";
|
|
27
|
+
|
|
28
|
+
const withoutTypeAssertion = (node) =>
|
|
29
|
+
node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" ? node.expression : node;
|
|
30
|
+
|
|
31
|
+
const keyName = (property) =>
|
|
32
|
+
property.key.type === "Identifier" ? property.key.name : property.key.value;
|
|
33
|
+
|
|
34
|
+
function literalCommands(menu) {
|
|
35
|
+
const object = withoutTypeAssertion(menu);
|
|
36
|
+
if (object.type !== "ObjectExpression") return null;
|
|
37
|
+
const property = object.properties.find(
|
|
38
|
+
(candidate) =>
|
|
39
|
+
candidate.type === "Property" && !candidate.computed && keyName(candidate) === "commands",
|
|
40
|
+
);
|
|
41
|
+
return property ? literalArray(property.value) : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function literalArray(value) {
|
|
45
|
+
const array = withoutTypeAssertion(value);
|
|
46
|
+
if (array.type !== "ArrayExpression") return null;
|
|
47
|
+
return array.elements.every((element) => element && element.type !== "SpreadElement")
|
|
48
|
+
? array
|
|
49
|
+
: null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const optionCount = {
|
|
53
|
+
meta: {
|
|
54
|
+
type: "problem",
|
|
55
|
+
docs: {
|
|
56
|
+
description:
|
|
57
|
+
"A RadioGroup holds 2 to 6 options, a Choicebox at most 6 tiles, a MultiSelect at least 2 rows, a menu about 10 commands.",
|
|
58
|
+
},
|
|
59
|
+
schema: [],
|
|
60
|
+
messages: {
|
|
61
|
+
"option-count":
|
|
62
|
+
"Keep the choice readable: a RadioGroup holds 2 to 6 options (7 or more is a Select or Combobox), a Choicebox at most 6 tiles, a MultiSelect at least 2 rows (one is a Checkbox or Switch), a menu about 10 commands (group the rest by job into a second menu or a page).",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
create(context) {
|
|
66
|
+
const importOf = renderedImports(context);
|
|
67
|
+
const { visitorKeys } = context.sourceCode;
|
|
68
|
+
|
|
69
|
+
const importedName = (element) => importOf(element.openingElement.name)?.name ?? null;
|
|
70
|
+
|
|
71
|
+
const hidesItems = (container) =>
|
|
72
|
+
findBelow(
|
|
73
|
+
container,
|
|
74
|
+
visitorKeys,
|
|
75
|
+
(node) =>
|
|
76
|
+
node.type === "JSXElement" ||
|
|
77
|
+
node.type === "JSXFragment" ||
|
|
78
|
+
(node.type === "CallExpression" &&
|
|
79
|
+
node.callee.type === "MemberExpression" &&
|
|
80
|
+
LIST_CALLBACKS.has(node.callee.property.name)),
|
|
81
|
+
) !== null;
|
|
82
|
+
|
|
83
|
+
function countLiteralItems(group, itemName) {
|
|
84
|
+
let count = 0;
|
|
85
|
+
let literal = true;
|
|
86
|
+
const tally = (node) => {
|
|
87
|
+
if (node.type === "JSXElement" && importedName(node) === itemName) count += 1;
|
|
88
|
+
if (
|
|
89
|
+
(node.type === "JSXExpressionContainer" || node.type === "JSXSpreadChild") &&
|
|
90
|
+
isChildPosition(node) &&
|
|
91
|
+
hidesItems(node)
|
|
92
|
+
) {
|
|
93
|
+
literal = false;
|
|
94
|
+
return "stop";
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
};
|
|
98
|
+
for (const child of group.children) {
|
|
99
|
+
if (child.type === "JSXText" && child.value.trim() === "") continue;
|
|
100
|
+
if (child.type !== "JSXElement") return null;
|
|
101
|
+
tally(child);
|
|
102
|
+
if (walk(child, visitorKeys, tally) === "stop" || !literal) return null;
|
|
103
|
+
}
|
|
104
|
+
return count;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const ownsContent = (element) =>
|
|
108
|
+
findBelow(
|
|
109
|
+
element,
|
|
110
|
+
visitorKeys,
|
|
111
|
+
(node) => node.type === "JSXElement" && importedName(node) === "MultiSelectContent",
|
|
112
|
+
) !== null;
|
|
113
|
+
|
|
114
|
+
function checkGroup(element, name) {
|
|
115
|
+
const { item, min = 0, max = Infinity } = GROUPS.get(name);
|
|
116
|
+
if (name === "MultiSelect" && ownsContent(element)) return;
|
|
117
|
+
const count = countLiteralItems(element, item);
|
|
118
|
+
if (!count) return;
|
|
119
|
+
if (count < min || count > max) {
|
|
120
|
+
context.report({ node: element.openingElement, messageId: "option-count" });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function checkCommands(array) {
|
|
125
|
+
if (array && array.elements.length > MAX_COMMANDS) {
|
|
126
|
+
context.report({ node: array, messageId: "option-count" });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function checkMenuAttribute(openingElement, name) {
|
|
131
|
+
const prop = COMMAND_PROPS.get(name);
|
|
132
|
+
const attribute = openingElement.attributes.find(
|
|
133
|
+
(candidate) => candidate.type === "JSXAttribute" && candidate.name.name === prop,
|
|
134
|
+
);
|
|
135
|
+
if (attribute?.value?.type !== "JSXExpressionContainer") return;
|
|
136
|
+
const { expression } = attribute.value;
|
|
137
|
+
checkCommands(prop === "commands" ? literalArray(expression) : literalCommands(expression));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const isMenuType = (annotation) =>
|
|
141
|
+
annotation?.type === "TSTypeReference" &&
|
|
142
|
+
annotation.typeName.type === "Identifier" &&
|
|
143
|
+
MENU_TYPES.has(annotation.typeName.name);
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
JSXElement(node) {
|
|
147
|
+
const name = importedName(node);
|
|
148
|
+
if (GROUPS.has(name)) checkGroup(node, name);
|
|
149
|
+
},
|
|
150
|
+
JSXOpeningElement(node) {
|
|
151
|
+
const name = importOf(node.name)?.name;
|
|
152
|
+
if (COMMAND_PROPS.has(name)) checkMenuAttribute(node, name);
|
|
153
|
+
},
|
|
154
|
+
VariableDeclarator(node) {
|
|
155
|
+
if (
|
|
156
|
+
node.init?.type === "ObjectExpression" &&
|
|
157
|
+
isMenuType(node.id.typeAnnotation?.typeAnnotation)
|
|
158
|
+
) {
|
|
159
|
+
checkCommands(literalCommands(node.init));
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"TSAsExpression, TSSatisfiesExpression"(node) {
|
|
163
|
+
if (isMenuType(node.typeAnnotation)) checkCommands(literalCommands(node.expression));
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: a `no-restricted-syntax` selector can match a
|
|
3
|
+
* `render` property holding a `<Link>`, but not that the same object literal
|
|
4
|
+
* also has a `label`, which is what tells a command (PageCommand, MenuCommand)
|
|
5
|
+
* from any other object with a `render` field.
|
|
6
|
+
*/
|
|
7
|
+
import { writtenName } from "./rendered-import.js";
|
|
8
|
+
|
|
9
|
+
const ADDRESS_ELEMENTS = new Set(["a", "Link", "NextLink"]);
|
|
10
|
+
|
|
11
|
+
const keyName = (property) => {
|
|
12
|
+
if (property.type !== "Property" || property.computed) return null;
|
|
13
|
+
return property.key.type === "Identifier" ? property.key.name : property.key.value;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const goesToAnAddress = (value) =>
|
|
17
|
+
value.type === "JSXElement" && ADDRESS_ELEMENTS.has(writtenName(value.openingElement.name));
|
|
18
|
+
|
|
19
|
+
export const pageCommandLinkNotRender = {
|
|
20
|
+
meta: {
|
|
21
|
+
type: "problem",
|
|
22
|
+
docs: {
|
|
23
|
+
description: "A command that goes to an address passes it as `link`, not as `render`.",
|
|
24
|
+
},
|
|
25
|
+
schema: [],
|
|
26
|
+
messages: {
|
|
27
|
+
"link-in-render":
|
|
28
|
+
'A command that goes to an address passes it as `link: <Link href="..." />`; `render` is only for an element that is itself a command, such as a component that renders a Button.',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
create(context) {
|
|
32
|
+
return {
|
|
33
|
+
ObjectExpression(node) {
|
|
34
|
+
if (!node.properties.some((property) => keyName(property) === "label")) return;
|
|
35
|
+
for (const property of node.properties) {
|
|
36
|
+
if (keyName(property) === "render" && goesToAnAddress(property.value)) {
|
|
37
|
+
context.report({ node: property.value, messageId: "link-in-render" });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: `jsx-a11y` has no rule for a progressbar's name,
|
|
3
|
+
* and a `no-restricted-syntax` selector cannot tell that the package's Progress
|
|
4
|
+
* is meant rather than a local one, nor look for a ProgressLabel anywhere in
|
|
5
|
+
* its children. Those need the import bindings and the element's subtree read
|
|
6
|
+
* together.
|
|
7
|
+
*/
|
|
8
|
+
import { findBelow, packageModules, partOf, renderedImports } from "./rendered-import.js";
|
|
9
|
+
|
|
10
|
+
const partsIn = (module, names) =>
|
|
11
|
+
names.map((name) => [
|
|
12
|
+
name,
|
|
13
|
+
[...packageModules(module), `@/components/${module}`].map((from) => ({ from, part: name })),
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const PROGRESS = new Map(partsIn("progress", ["Progress"]));
|
|
17
|
+
const PROGRESS_LABEL = new Map(partsIn("progress", ["ProgressLabel"]));
|
|
18
|
+
|
|
19
|
+
const NAMING_ATTRIBUTES = new Set(["aria-label", "aria-labelledby"]);
|
|
20
|
+
|
|
21
|
+
export const progressNeedsName = {
|
|
22
|
+
meta: {
|
|
23
|
+
type: "problem",
|
|
24
|
+
docs: {
|
|
25
|
+
description:
|
|
26
|
+
"A Progress is named by an aria-label, an aria-labelledby or a ProgressLabel among its children.",
|
|
27
|
+
},
|
|
28
|
+
schema: [],
|
|
29
|
+
messages: {
|
|
30
|
+
unnamed:
|
|
31
|
+
"Name the work: add <ProgressLabel> (Uploading files) or aria-label, and print the count in ProgressValue.",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
create(context) {
|
|
35
|
+
const importOf = renderedImports(context);
|
|
36
|
+
const isPart = (parts, openingElement) =>
|
|
37
|
+
Boolean(partOf(importOf(openingElement.name), parts, context.filename));
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
JSXElement(node) {
|
|
41
|
+
const { openingElement } = node;
|
|
42
|
+
if (!isPart(PROGRESS, openingElement)) return;
|
|
43
|
+
const namedByAttribute = openingElement.attributes.some(
|
|
44
|
+
(attribute) =>
|
|
45
|
+
attribute.type === "JSXSpreadAttribute" || NAMING_ATTRIBUTES.has(attribute.name.name),
|
|
46
|
+
);
|
|
47
|
+
if (namedByAttribute) return;
|
|
48
|
+
const label = findBelow(
|
|
49
|
+
node,
|
|
50
|
+
context.sourceCode.visitorKeys,
|
|
51
|
+
(child) => child.type === "JSXElement" && isPart(PROGRESS_LABEL, child.openingElement),
|
|
52
|
+
);
|
|
53
|
+
if (!label) context.report({ node: openingElement, messageId: "unnamed" });
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: a `no-restricted-syntax` selector can find a
|
|
3
|
+
* `maxLength` inside a `SettingsSection`, but not follow a renamed import to the
|
|
4
|
+
* component, nor read whether the section's `save` or `apply` object names a hint.
|
|
5
|
+
*/
|
|
6
|
+
import { packageModules, partOf, renderedImports } from "./rendered-import.js";
|
|
7
|
+
|
|
8
|
+
const SECTION = new Map([
|
|
9
|
+
[
|
|
10
|
+
"SettingsSection",
|
|
11
|
+
packageModules("settings-section").map((from) => ({ from, part: "SettingsSection" })),
|
|
12
|
+
],
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const attributeNamed = (openingElement, name) =>
|
|
16
|
+
openingElement.attributes.find(
|
|
17
|
+
(attribute) => attribute.type === "JSXAttribute" && attribute.name.name === name,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
/** true: the section shows a footer line; false: it shows none; null: the source does not say. */
|
|
21
|
+
function footerLine(openingElement) {
|
|
22
|
+
if (openingElement.attributes.some((attribute) => attribute.type === "JSXSpreadAttribute"))
|
|
23
|
+
return null;
|
|
24
|
+
if (
|
|
25
|
+
attributeNamed(openingElement, "footerHint") ||
|
|
26
|
+
attributeNamed(openingElement, "lockedReason")
|
|
27
|
+
)
|
|
28
|
+
return true;
|
|
29
|
+
const commit = attributeNamed(openingElement, "save") ?? attributeNamed(openingElement, "apply");
|
|
30
|
+
if (!commit) return false;
|
|
31
|
+
const value = commit.value?.type === "JSXExpressionContainer" ? commit.value.expression : null;
|
|
32
|
+
if (value?.type !== "ObjectExpression") return null;
|
|
33
|
+
if (value.properties.some((property) => property.type === "SpreadElement")) return null;
|
|
34
|
+
return value.properties.some(
|
|
35
|
+
(property) =>
|
|
36
|
+
property.type === "Property" && (property.key.name ?? property.key.value) === "hint",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function limitsIn(element, isSection) {
|
|
41
|
+
const found = [];
|
|
42
|
+
const visit = (node) => {
|
|
43
|
+
if (node.type === "JSXElement") {
|
|
44
|
+
if (node !== element && isSection(node.openingElement)) return;
|
|
45
|
+
const limit = attributeNamed(node.openingElement, "maxLength");
|
|
46
|
+
if (limit) found.push(limit);
|
|
47
|
+
node.children.forEach(visit);
|
|
48
|
+
} else if (node.type === "JSXFragment") {
|
|
49
|
+
node.children.forEach(visit);
|
|
50
|
+
} else if (node.type === "JSXExpressionContainer") {
|
|
51
|
+
visitExpression(node.expression);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const visitExpression = (expression) => {
|
|
55
|
+
if (!expression) return;
|
|
56
|
+
if (expression.type === "JSXElement" || expression.type === "JSXFragment") visit(expression);
|
|
57
|
+
else if (expression.type === "LogicalExpression") visitExpression(expression.right);
|
|
58
|
+
else if (expression.type === "ConditionalExpression") {
|
|
59
|
+
visitExpression(expression.consequent);
|
|
60
|
+
visitExpression(expression.alternate);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
element.children.forEach(visit);
|
|
64
|
+
return found;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const settingsLimitInFooter = {
|
|
68
|
+
meta: {
|
|
69
|
+
type: "problem",
|
|
70
|
+
docs: {
|
|
71
|
+
description:
|
|
72
|
+
"A SettingsSection whose field takes a maxLength says the limit in its footer line, as Vercel's settings cards do.",
|
|
73
|
+
},
|
|
74
|
+
schema: [],
|
|
75
|
+
messages: {
|
|
76
|
+
"limit-not-said":
|
|
77
|
+
"Say this limit in the card's footer line (`save.hint`, `apply.hint` or `footerHint`), worded by the product's catalogue, such as \"Please use 48 characters at maximum.\"",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
create(context) {
|
|
81
|
+
const importOf = renderedImports(context);
|
|
82
|
+
const isSection = (openingElement) =>
|
|
83
|
+
partOf(importOf(openingElement.name), SECTION, context.filename) === "SettingsSection";
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
JSXElement(node) {
|
|
87
|
+
if (!isSection(node.openingElement)) return;
|
|
88
|
+
if (footerLine(node.openingElement) !== false) return;
|
|
89
|
+
for (const limit of limitsIn(node, isSection))
|
|
90
|
+
context.report({ node: limit, messageId: "limit-not-said" });
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: `no-restricted-syntax` selects a JSX element by
|
|
3
|
+
* name and ancestors, but not the one the import renders under another name,
|
|
4
|
+
* nor "this file or this function is a loading view", which depends on the
|
|
5
|
+
* file name and the enclosing function's name together.
|
|
6
|
+
*/
|
|
7
|
+
import { renderedImports } from "./rendered-import.js";
|
|
8
|
+
|
|
9
|
+
const SKELETON = "Skeleton";
|
|
10
|
+
const REGION = "SkeletonRegion";
|
|
11
|
+
const PLACEHOLDER_FILE = /(?:^|\/)(?:loading|[^/]+-skeleton)\.[jt]sx$/;
|
|
12
|
+
const PLACEHOLDER_FUNCTION = /Skeleton$/;
|
|
13
|
+
const FUNCTIONS = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
|
|
14
|
+
|
|
15
|
+
const hasContent = (child) => {
|
|
16
|
+
if (child.type === "JSXText") return child.value.trim() !== "";
|
|
17
|
+
if (child.type === "JSXExpressionContainer")
|
|
18
|
+
return child.expression.type !== "JSXEmptyExpression";
|
|
19
|
+
return true;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const marksBusy = (openingElement) =>
|
|
23
|
+
openingElement.attributes.some(
|
|
24
|
+
(attribute) => attribute.type === "JSXAttribute" && attribute.name.name === "aria-busy",
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
function functionName(fn) {
|
|
28
|
+
if (fn.id) return fn.id.name;
|
|
29
|
+
const owner = fn.parent;
|
|
30
|
+
return owner.type === "VariableDeclarator" && owner.id.type === "Identifier"
|
|
31
|
+
? owner.id.name
|
|
32
|
+
: null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const skeletonInRegion = {
|
|
36
|
+
meta: {
|
|
37
|
+
type: "problem",
|
|
38
|
+
docs: {
|
|
39
|
+
description:
|
|
40
|
+
"Placeholders of a loading view sit in a SkeletonRegion, or under an element marked aria-busy.",
|
|
41
|
+
},
|
|
42
|
+
schema: [],
|
|
43
|
+
messages: {
|
|
44
|
+
"placeholder-outside-region":
|
|
45
|
+
"Wrap these placeholders in <SkeletonRegion busy> from @nadicodeai/ui/components/skeleton so the loading view is aria-busy; a component that renders both the placeholders and the data passes busy={isPending}.",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
create(context) {
|
|
49
|
+
const filename = context.filename.replaceAll("\\", "/");
|
|
50
|
+
const loadingFile = PLACEHOLDER_FILE.test(filename);
|
|
51
|
+
const importOf = renderedImports(context);
|
|
52
|
+
|
|
53
|
+
function loadingView(element) {
|
|
54
|
+
if (loadingFile) return true;
|
|
55
|
+
for (let current = element.parent; current; current = current.parent) {
|
|
56
|
+
if (!FUNCTIONS.has(current.type)) continue;
|
|
57
|
+
if (PLACEHOLDER_FUNCTION.test(functionName(current) ?? "")) return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function inABusyRegion(element) {
|
|
63
|
+
for (let current = element.parent; current; current = current.parent) {
|
|
64
|
+
if (current.type !== "JSXElement") continue;
|
|
65
|
+
if (importOf(current.openingElement.name)?.name === REGION) return true;
|
|
66
|
+
if (marksBusy(current.openingElement)) return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
JSXElement(node) {
|
|
73
|
+
if (importOf(node.openingElement.name)?.name !== SKELETON) return;
|
|
74
|
+
if (node.children.some(hasContent)) return;
|
|
75
|
+
if (!loadingView(node) || inABusyRegion(node)) return;
|
|
76
|
+
context.report({ node: node.openingElement, messageId: "placeholder-outside-region" });
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: a `no-restricted-syntax` selector cannot follow
|
|
3
|
+
* a renamed `TableHead` or `Button` import, nor tell a column definition's
|
|
4
|
+
* `header` from any other property of that name.
|
|
5
|
+
*/
|
|
6
|
+
import { packageModules, partOf, renderedImports, walk } from "./rendered-import.js";
|
|
7
|
+
|
|
8
|
+
const exporters = (module, part) =>
|
|
9
|
+
new Map([
|
|
10
|
+
[part, [...packageModules(module), `@/components/${module}`].map((from) => ({ from, part }))],
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
const TABLE_HEAD = exporters("table", "TableHead");
|
|
14
|
+
const BUTTON = exporters("button", "Button");
|
|
15
|
+
|
|
16
|
+
const FUNCTIONS = new Set(["FunctionExpression", "ArrowFunctionExpression"]);
|
|
17
|
+
const COLUMN_KEYS = new Set(["accessorKey", "accessorFn", "id", "cell"]);
|
|
18
|
+
|
|
19
|
+
const keyName = (property) => {
|
|
20
|
+
if (property.computed) return null;
|
|
21
|
+
return property.key.name ?? property.key.value ?? null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const isColumnHeader = (property) =>
|
|
25
|
+
property.type === "Property" &&
|
|
26
|
+
keyName(property) === "header" &&
|
|
27
|
+
FUNCTIONS.has(property.value.type) &&
|
|
28
|
+
property.parent.properties.some(
|
|
29
|
+
(sibling) => sibling.type === "Property" && COLUMN_KEYS.has(keyName(sibling)),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const sortableHeadingUsesSort = {
|
|
33
|
+
meta: {
|
|
34
|
+
type: "problem",
|
|
35
|
+
docs: {
|
|
36
|
+
description: "A sortable heading is TableHead's sort prop, not a button drawn inside it.",
|
|
37
|
+
},
|
|
38
|
+
schema: [],
|
|
39
|
+
messages: {
|
|
40
|
+
"drawn-sort-button":
|
|
41
|
+
"A sortable heading is TableHead's sort prop (order, next, onSort), not a button you draw. The sort prop gives the heading aria-sort and names the next sort.",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
create(context) {
|
|
45
|
+
const importOf = renderedImports(context);
|
|
46
|
+
const { visitorKeys } = context.sourceCode;
|
|
47
|
+
|
|
48
|
+
const drawsAControl = (opening) =>
|
|
49
|
+
opening.attributes.some(
|
|
50
|
+
(attribute) => attribute.type === "JSXAttribute" && attribute.name.name === "onClick",
|
|
51
|
+
) || partOf(importOf(opening.name), BUTTON, context.filename) === "Button";
|
|
52
|
+
|
|
53
|
+
const reported = new Set();
|
|
54
|
+
|
|
55
|
+
function reportControlsBelow(root, skipped) {
|
|
56
|
+
walk(root, visitorKeys, (child) => {
|
|
57
|
+
if (child === skipped) return false;
|
|
58
|
+
if (child.type === "JSXOpeningElement" && !reported.has(child) && drawsAControl(child)) {
|
|
59
|
+
reported.add(child);
|
|
60
|
+
context.report({ node: child, messageId: "drawn-sort-button" });
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
JSXElement(node) {
|
|
68
|
+
const { openingElement } = node;
|
|
69
|
+
if (partOf(importOf(openingElement.name), TABLE_HEAD, context.filename) !== "TableHead") {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
reportControlsBelow(node, openingElement);
|
|
73
|
+
},
|
|
74
|
+
Property(node) {
|
|
75
|
+
if (isColumnHeader(node)) reportControlsBelow(node.value, null);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: a `no-restricted-syntax` selector can match a
|
|
3
|
+
* padding class written as a literal on `<TableCell>`, but not one composed
|
|
4
|
+
* through `cn(...)` or a conditional, not which JSX name is the table's part
|
|
5
|
+
* once the import is renamed, and not which object literal is a column
|
|
6
|
+
* definition rather than any other object with a `className`.
|
|
7
|
+
*/
|
|
8
|
+
import { PACKAGE_BARREL, partOf, renderedImports, walk } from "./rendered-import.js";
|
|
9
|
+
|
|
10
|
+
const CELL_PARTS = new Set(["TableHead", "TableCell"]);
|
|
11
|
+
|
|
12
|
+
const TABLE_PARTS = new Map(
|
|
13
|
+
[...CELL_PARTS].map((name) => [
|
|
14
|
+
name,
|
|
15
|
+
["@nadicodeai/ui/components/table", PACKAGE_BARREL, "@/components/table"].map((from) => ({
|
|
16
|
+
from,
|
|
17
|
+
part: name,
|
|
18
|
+
})),
|
|
19
|
+
]),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const COLUMN_KEYS = new Set(["accessorKey", "accessorFn", "header", "cell"]);
|
|
23
|
+
const SIDEWAYS_PADDING = /^-?(?:px|ps|pe|pl|pr)-(?<step>.+?)!?$/;
|
|
24
|
+
|
|
25
|
+
const keyName = (property) => {
|
|
26
|
+
if (property.computed) return null;
|
|
27
|
+
return property.key.type === "Identifier" ? property.key.name : property.key.value;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const propertyNamed = (objectExpression, name) =>
|
|
31
|
+
objectExpression.properties.find(
|
|
32
|
+
(property) => property.type === "Property" && keyName(property) === name,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
function withoutVariants(utility) {
|
|
36
|
+
let depth = 0;
|
|
37
|
+
let start = 0;
|
|
38
|
+
for (let index = 0; index < utility.length; index += 1) {
|
|
39
|
+
const character = utility[index];
|
|
40
|
+
if (character === "[") depth += 1;
|
|
41
|
+
else if (character === "]") depth -= 1;
|
|
42
|
+
else if (character === ":" && depth === 0) start = index + 1;
|
|
43
|
+
}
|
|
44
|
+
return utility.slice(start).replace(/^!/, "");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const insetsSideways = (utility) => {
|
|
48
|
+
const step = SIDEWAYS_PADDING.exec(withoutVariants(utility))?.groups.step;
|
|
49
|
+
return step !== undefined && step !== "0";
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const tableCellInsetOwner = {
|
|
53
|
+
meta: {
|
|
54
|
+
type: "problem",
|
|
55
|
+
docs: {
|
|
56
|
+
description: "The table insets its cells sideways; a cell or a column never sets it.",
|
|
57
|
+
},
|
|
58
|
+
schema: [],
|
|
59
|
+
messages: {
|
|
60
|
+
"cell-inset":
|
|
61
|
+
"Table cells are inset 8 px sideways at every width by the table. Remove the class; set the width with the column's `size`.",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
create(context) {
|
|
65
|
+
const importOf = renderedImports(context);
|
|
66
|
+
const { visitorKeys } = context.sourceCode;
|
|
67
|
+
|
|
68
|
+
function checkClasses(value) {
|
|
69
|
+
const fragments = [];
|
|
70
|
+
const read = (node) => {
|
|
71
|
+
// 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.
|
|
72
|
+
if (node.type === "Literal" && typeof node.value === "string") fragments.push(node.value);
|
|
73
|
+
if (node.type === "TemplateElement") fragments.push(node.value.cooked ?? node.value.raw);
|
|
74
|
+
};
|
|
75
|
+
read(value);
|
|
76
|
+
walk(value, visitorKeys, read);
|
|
77
|
+
const refused = fragments
|
|
78
|
+
.flatMap((fragment) => fragment.split(/\s+/).filter(Boolean))
|
|
79
|
+
.some(insetsSideways);
|
|
80
|
+
if (refused) context.report({ node: value, messageId: "cell-inset" });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
JSXOpeningElement(node) {
|
|
85
|
+
if (!CELL_PARTS.has(partOf(importOf(node.name), TABLE_PARTS, context.filename) ?? "")) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
for (const attribute of node.attributes) {
|
|
89
|
+
if (attribute.type === "JSXAttribute" && attribute.name.name === "className") {
|
|
90
|
+
if (attribute.value) checkClasses(attribute.value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
ObjectExpression(node) {
|
|
95
|
+
const isColumn = node.properties.some(
|
|
96
|
+
(property) => property.type === "Property" && COLUMN_KEYS.has(keyName(property)),
|
|
97
|
+
);
|
|
98
|
+
if (!isColumn) return;
|
|
99
|
+
const own = propertyNamed(node, "className");
|
|
100
|
+
if (own) checkClasses(own.value);
|
|
101
|
+
const meta = propertyNamed(node, "meta")?.value;
|
|
102
|
+
const metaOwn = meta?.type === "ObjectExpression" && propertyNamed(meta, "className");
|
|
103
|
+
if (metaOwn) checkClasses(metaOwn.value);
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard tool checked first: a `no-restricted-syntax` selector can match a
|
|
3
|
+
* `JSXAttribute[name.name='defaultValue']`, but not that the element is the
|
|
4
|
+
* package's Tabs rather than TabsNav or a local component, nor that a missing
|
|
5
|
+
* `value` is covered by a spread. Those need the import binding and the
|
|
6
|
+
* attribute list read together.
|
|
7
|
+
*/
|
|
8
|
+
import { packageModules, partOf, renderedImports } from "./rendered-import.js";
|
|
9
|
+
|
|
10
|
+
const TABS = new Map([["Tabs", packageModules("tabs").map((from) => ({ from, part: "Tabs" }))]]);
|
|
11
|
+
|
|
12
|
+
const hasAttribute = (openingElement, attributeName) =>
|
|
13
|
+
openingElement.attributes.some(
|
|
14
|
+
(attribute) => attribute.type === "JSXAttribute" && attribute.name.name === attributeName,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const hasSpread = (openingElement) =>
|
|
18
|
+
openingElement.attributes.some((attribute) => attribute.type === "JSXSpreadAttribute");
|
|
19
|
+
|
|
20
|
+
export const tabsSelectionIsControlled = {
|
|
21
|
+
meta: {
|
|
22
|
+
type: "problem",
|
|
23
|
+
docs: {
|
|
24
|
+
description: "Tabs is controlled by a `value` read from the address, never `defaultValue`.",
|
|
25
|
+
},
|
|
26
|
+
schema: [],
|
|
27
|
+
messages: {
|
|
28
|
+
"chosen-tab-in-address":
|
|
29
|
+
"The chosen tab lives in the address: control Tabs with `value` read from the route or ?tab=, so a reload, Back and a shared link restore it.",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
create(context) {
|
|
33
|
+
const importOf = renderedImports(context);
|
|
34
|
+
return {
|
|
35
|
+
JSXElement(node) {
|
|
36
|
+
const { openingElement } = node;
|
|
37
|
+
if (!partOf(importOf(openingElement.name), TABS, context.filename)) return;
|
|
38
|
+
const uncontrolled =
|
|
39
|
+
hasAttribute(openingElement, "defaultValue") ||
|
|
40
|
+
(!hasAttribute(openingElement, "value") && !hasSpread(openingElement));
|
|
41
|
+
if (uncontrolled) {
|
|
42
|
+
context.report({ node: openingElement, messageId: "chosen-tab-in-address" });
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
};
|