@scalewing/react 1.46.0 → 1.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +228 -4
  3. package/dist/app-shell.test-support.d.ts +5 -0
  4. package/dist/app-shell.test-support.js +45 -0
  5. package/dist/command-palette.test-support.d.ts +21 -0
  6. package/dist/command-palette.test-support.js +55 -0
  7. package/dist/components/AppHeader.d.ts +3 -0
  8. package/dist/components/AppShell.d.ts +13 -0
  9. package/dist/components/AppShell.js +65 -0
  10. package/dist/components/Band.d.ts +30 -0
  11. package/dist/components/Band.js +23 -0
  12. package/dist/components/ButtonLink.d.ts +33 -0
  13. package/dist/components/ButtonLink.js +27 -0
  14. package/dist/components/CommandPalette.d.ts +12 -0
  15. package/dist/components/CommandPalette.js +143 -0
  16. package/dist/components/CommandPaletteTrigger.d.ts +9 -0
  17. package/dist/components/CommandPaletteTrigger.js +16 -0
  18. package/dist/components/ContextSwitcher.d.ts +12 -0
  19. package/dist/components/ContextSwitcher.js +62 -0
  20. package/dist/components/FixtureGroup.d.ts +9 -0
  21. package/dist/components/FixtureGroup.js +16 -0
  22. package/dist/components/FixtureRow.d.ts +10 -0
  23. package/dist/components/FixtureRow.js +46 -0
  24. package/dist/components/GlyphTile.d.ts +9 -3
  25. package/dist/components/GlyphTile.js +15 -4
  26. package/dist/components/NavCount.d.ts +12 -5
  27. package/dist/components/NavCount.js +9 -4
  28. package/dist/components/SectionNav.d.ts +2 -1
  29. package/dist/components/SectionNav.js +3 -19
  30. package/dist/components/SiteFooter.d.ts +12 -0
  31. package/dist/components/SiteFooter.js +23 -0
  32. package/dist/components/SiteHeader.d.ts +14 -0
  33. package/dist/components/SiteHeader.js +24 -0
  34. package/dist/components/Skeleton.d.ts +22 -0
  35. package/dist/components/Skeleton.js +30 -0
  36. package/dist/components/app-shell/AppShellBar.d.ts +23 -0
  37. package/dist/components/app-shell/AppShellBar.js +25 -0
  38. package/dist/components/app-shell/AppShellDrawer.d.ts +24 -0
  39. package/dist/components/app-shell/AppShellDrawer.js +17 -0
  40. package/dist/components/app-shell/AppShellLink.d.ts +14 -0
  41. package/dist/components/app-shell/AppShellLink.js +27 -0
  42. package/dist/components/app-shell/AppShellNav.d.ts +15 -0
  43. package/dist/components/app-shell/AppShellNav.js +29 -0
  44. package/dist/components/app-shell/AppShellSidebar.d.ts +18 -0
  45. package/dist/components/app-shell/AppShellSidebar.js +18 -0
  46. package/dist/components/app-shell/types.d.ts +122 -0
  47. package/dist/components/app-shell/types.js +1 -0
  48. package/dist/components/app-shell/validate.d.ts +7 -0
  49. package/dist/components/app-shell/validate.js +70 -0
  50. package/dist/components/command-palette/PaletteList.d.ts +20 -0
  51. package/dist/components/command-palette/PaletteList.js +21 -0
  52. package/dist/components/command-palette/shortcut.d.ts +19 -0
  53. package/dist/components/command-palette/shortcut.js +75 -0
  54. package/dist/components/command-palette/types.d.ts +72 -0
  55. package/dist/components/command-palette/types.js +2 -0
  56. package/dist/components/command-palette/validate.d.ts +10 -0
  57. package/dist/components/command-palette/validate.js +50 -0
  58. package/dist/components/context-switcher/SwitcherOption.d.ts +17 -0
  59. package/dist/components/context-switcher/SwitcherOption.js +20 -0
  60. package/dist/components/context-switcher/SwitcherPopup.d.ts +31 -0
  61. package/dist/components/context-switcher/SwitcherPopup.js +111 -0
  62. package/dist/components/context-switcher/options.d.ts +33 -0
  63. package/dist/components/context-switcher/options.js +60 -0
  64. package/dist/components/context-switcher/types.d.ts +96 -0
  65. package/dist/components/context-switcher/types.js +1 -0
  66. package/dist/components/context-switcher/use-switcher-list.d.ts +29 -0
  67. package/dist/components/context-switcher/use-switcher-list.js +93 -0
  68. package/dist/components/context-switcher/validate.d.ts +8 -0
  69. package/dist/components/context-switcher/validate.js +94 -0
  70. package/dist/components/fixture/fixture-list-context.d.ts +4 -0
  71. package/dist/components/fixture/fixture-list-context.js +9 -0
  72. package/dist/components/fixture/types.d.ts +50 -0
  73. package/dist/components/fixture/types.js +1 -0
  74. package/dist/components/fixture/validate.d.ts +8 -0
  75. package/dist/components/fixture/validate.js +36 -0
  76. package/dist/components/nav-items.d.ts +21 -0
  77. package/dist/components/nav-items.js +26 -0
  78. package/dist/components/site-footer/types.d.ts +31 -0
  79. package/dist/components/site-footer/types.js +1 -0
  80. package/dist/components/site-footer/validate.d.ts +7 -0
  81. package/dist/components/site-footer/validate.js +22 -0
  82. package/dist/components/site-header/SiteHeaderLinks.d.ts +7 -0
  83. package/dist/components/site-header/SiteHeaderLinks.js +7 -0
  84. package/dist/components/site-header/types.d.ts +38 -0
  85. package/dist/components/site-header/types.js +1 -0
  86. package/dist/components/site-header/use-menu-dismiss.d.ts +8 -0
  87. package/dist/components/site-header/use-menu-dismiss.js +62 -0
  88. package/dist/components/site-header/validate.d.ts +7 -0
  89. package/dist/components/site-header/validate.js +17 -0
  90. package/dist/components/use-element-height.d.ts +10 -0
  91. package/dist/components/use-element-height.js +35 -0
  92. package/dist/context-switcher.test-support.d.ts +9 -0
  93. package/dist/context-switcher.test-support.js +41 -0
  94. package/dist/css/css-app-shell-drawer.d.ts +2 -0
  95. package/dist/css/css-app-shell-drawer.js +91 -0
  96. package/dist/css/css-app-shell-nav.d.ts +3 -0
  97. package/dist/css/css-app-shell-nav.js +167 -0
  98. package/dist/css/css-app-shell-page-bar.d.ts +3 -0
  99. package/dist/css/css-app-shell-page-bar.js +81 -0
  100. package/dist/css/css-app-shell.d.ts +4 -0
  101. package/dist/css/css-app-shell.js +155 -0
  102. package/dist/css/css-band.d.ts +10 -0
  103. package/dist/css/css-band.js +101 -0
  104. package/dist/css/css-button-link.d.ts +3 -0
  105. package/dist/css/css-button-link.js +48 -0
  106. package/dist/css/css-command-palette-key.d.ts +1 -0
  107. package/dist/css/css-command-palette-key.js +33 -0
  108. package/dist/css/css-command-palette-trigger.d.ts +2 -0
  109. package/dist/css/css-command-palette-trigger.js +88 -0
  110. package/dist/css/css-command-palette.d.ts +2 -0
  111. package/dist/css/css-command-palette.js +209 -0
  112. package/dist/css/css-components.js +27 -0
  113. package/dist/css/css-context-switcher-trigger.d.ts +15 -0
  114. package/dist/css/css-context-switcher-trigger.js +175 -0
  115. package/dist/css/css-context-switcher.d.ts +4 -0
  116. package/dist/css/css-context-switcher.js +219 -0
  117. package/dist/css/css-count-pill.d.ts +7 -0
  118. package/dist/css/css-count-pill.js +50 -12
  119. package/dist/css/css-fixture.d.ts +5 -0
  120. package/dist/css/css-fixture.js +260 -0
  121. package/dist/css/css-glyph-tile.d.ts +2 -0
  122. package/dist/css/css-glyph-tile.js +29 -1
  123. package/dist/css/css-site-footer.d.ts +2 -0
  124. package/dist/css/css-site-footer.js +152 -0
  125. package/dist/css/css-site-header.d.ts +2 -0
  126. package/dist/css/css-site-header.js +262 -0
  127. package/dist/css/css-skeleton.d.ts +10 -0
  128. package/dist/css/css-skeleton.js +110 -0
  129. package/dist/css/stacking.js +7 -4
  130. package/dist/css/stylesheet.js +18 -0
  131. package/dist/index.d.ts +14 -0
  132. package/dist/index.js +11 -0
  133. package/dist/styles.css +2122 -512
  134. package/package.json +2 -2
@@ -0,0 +1,155 @@
1
+ import { breakpointQuery } from './breakpoints.js';
2
+ import { appShellDrawerClassCatalog, cssAppShellDrawerClasses, } from './css-app-shell-drawer.js';
3
+ import { appShellNavClassCatalog, cssAppShellNavClasses, cssAppShellRailNavRules, } from './css-app-shell-nav.js';
4
+ import { appShellPageBarClassCatalog, cssAppShellPageBarClasses, } from './css-app-shell-page-bar.js';
5
+ import { iconButtonRules } from './css-icon-button.js';
6
+ /*
7
+ * The width from which AppShell's sidebar is full rather than a rail: an
8
+ * internal media query of the component, not a public breakpoint (the
9
+ * `md` and `lg` tokens stay the only ones products use).
10
+ */
11
+ const wideShellWidth = 75;
12
+ /** From this width up AppShell draws its full sidebar; script reads it too. */
13
+ export const appShellWideQuery = `(min-width: ${wideShellWidth}rem)`;
14
+ /* The full sidebar's width, a named geometry of this frame. */
15
+ const sidebarWidth = '15rem';
16
+ /* The rail: the 44 px target with a space-3 gutter each side, and its hairline. */
17
+ const railWidth = 'calc(var(--sw-control-md-min-height) + 2 * var(--sw-space-3) + 1px)';
18
+ const fromLg = breakpointQuery('from', 'lg');
19
+ /*
20
+ * Below `lg`: the page under a sticky top bar (an AppHeader `bar`: the menu
21
+ * button, the title, the end controls), no sidebar. The frame never makes
22
+ * `main` a scroll box: the page scrolls with the document.
23
+ */
24
+ function barRules() {
25
+ return `.sw-app-shell {
26
+ min-height: 100vh;
27
+ min-height: 100dvh;
28
+ }
29
+
30
+ .sw-app-shell-bar {
31
+ align-items: center;
32
+ display: flex;
33
+ gap: var(--sw-space-2);
34
+ }
35
+
36
+ .sw-app-shell-bar-title {
37
+ flex: 1 1 auto;
38
+ min-width: 0;
39
+ }
40
+
41
+ .sw-app-shell-bar-end {
42
+ align-items: center;
43
+ display: flex;
44
+ flex: none;
45
+ gap: var(--sw-space-2);
46
+ }
47
+
48
+ ${iconButtonRules('sw-app-shell-menu')}
49
+
50
+ .sw-app-shell-main {
51
+ display: block;
52
+ min-width: 0;
53
+ }
54
+
55
+ .sw-app-shell-sidebar,
56
+ .sw-app-shell-sidebar .sw-app-shell-rail-slot {
57
+ display: none;
58
+ }`;
59
+ }
60
+ /*
61
+ * From \`lg\`: no bar; the sidebar is a column of the frame's grid, sticky at
62
+ * the top at the viewport's height on the solid surface with a hairline at
63
+ * its inline end. Its header and footer keep their height and only the nav
64
+ * between them scrolls, when it is taller than the window. A rail to 75rem,
65
+ * full from there.
66
+ */
67
+ function sidebarRules() {
68
+ return `@media ${fromLg} {
69
+ .sw-app-shell {
70
+ align-items: start;
71
+ display: grid;
72
+ grid-template-columns: auto minmax(0, 1fr);
73
+ }
74
+
75
+ .sw-app-shell-bar {
76
+ display: none;
77
+ }
78
+
79
+ .sw-app-shell-sidebar {
80
+ background: var(--sw-color-surface);
81
+ border-inline-end: 1px solid var(--sw-color-border);
82
+ box-sizing: border-box;
83
+ display: flex;
84
+ flex-direction: column;
85
+ height: 100vh;
86
+ height: 100dvh;
87
+ position: sticky;
88
+ top: 0;
89
+ width: ${railWidth};
90
+ }
91
+
92
+ @media not all and ${appShellWideQuery} {
93
+ .sw-app-shell-sidebar .sw-app-shell-slot {
94
+ display: none;
95
+ }
96
+
97
+ .sw-app-shell-sidebar .sw-app-shell-rail-slot {
98
+ display: block;
99
+ }
100
+
101
+ ${cssAppShellRailNavRules(' ')}
102
+ }
103
+ }
104
+
105
+ @media ${appShellWideQuery} {
106
+ .sw-app-shell-sidebar {
107
+ width: ${sidebarWidth};
108
+ }
109
+ }`;
110
+ }
111
+ /*
112
+ * AppShell: a workspace frame with a pinned side navigation (see
113
+ * components/AppShell.tsx). The links, groups and the rail's nav are
114
+ * `css-app-shell-nav.ts`; the page bar and the menu's count are
115
+ * `css-app-shell-page-bar.ts`; the menu drawer is `css-app-shell-drawer.ts`.
116
+ */
117
+ export function cssAppShellClasses() {
118
+ return `${barRules()}
119
+
120
+ .sw-app-shell-header,
121
+ .sw-app-shell-footer {
122
+ flex: none;
123
+ padding: var(--sw-space-3);
124
+ }
125
+
126
+ .sw-app-shell-footer {
127
+ border-top: 1px solid var(--sw-color-border);
128
+ }
129
+
130
+ ${cssAppShellNavClasses()}
131
+
132
+ ${cssAppShellPageBarClasses()}
133
+
134
+ ${sidebarRules()}
135
+
136
+ ${cssAppShellDrawerClasses()}`;
137
+ }
138
+ export function appShellClassCatalog() {
139
+ return [
140
+ 'sw-app-shell',
141
+ 'sw-app-shell-bar',
142
+ 'sw-app-shell-menu',
143
+ 'sw-app-shell-bar-title',
144
+ 'sw-app-shell-bar-end',
145
+ 'sw-app-shell-main',
146
+ 'sw-app-shell-sidebar',
147
+ 'sw-app-shell-slot',
148
+ 'sw-app-shell-rail-slot',
149
+ 'sw-app-shell-header',
150
+ 'sw-app-shell-footer',
151
+ ...appShellNavClassCatalog(),
152
+ ...appShellPageBarClassCatalog(),
153
+ ...appShellDrawerClassCatalog(),
154
+ ];
155
+ }
@@ -0,0 +1,10 @@
1
+ /** A band's fill: the page's surface, the quiet subtle fill, or the dark inverse band. */
2
+ export declare const bandTones: readonly ["surface", "subtle", "inverse"];
3
+ export type BandTone = (typeof bandTones)[number];
4
+ /** How wide a band's content column runs: the page container or the reading width. */
5
+ export declare const bandWidths: readonly ["container", "reading"];
6
+ export type BandWidth = (typeof bandWidths)[number];
7
+ export declare function bandToneClass(tone: BandTone): string;
8
+ export declare function bandWidthClass(width: BandWidth): string;
9
+ export declare function cssBandClasses(): string;
10
+ export declare function bandClassCatalog(): string[];
@@ -0,0 +1,101 @@
1
+ import { breakpointQuery } from './breakpoints.js';
2
+ /** A band's fill: the page's surface, the quiet subtle fill, or the dark inverse band. */
3
+ export const bandTones = ['surface', 'subtle', 'inverse'];
4
+ /** How wide a band's content column runs: the page container or the reading width. */
5
+ export const bandWidths = ['container', 'reading'];
6
+ export function bandToneClass(tone) {
7
+ return `sw-band-${tone}`;
8
+ }
9
+ export function bandWidthClass(width) {
10
+ return `sw-band-inner-${width}`;
11
+ }
12
+ /*
13
+ * The inverse band re-scopes the theme's colours for everything inside it:
14
+ * words, muted words, hairlines, the quiet subtle fill, links and the
15
+ * accent (a primary button, the focus ring) read on the dark fill, so
16
+ * Text, links and Buttons need no band-aware variant. Every plain link,
17
+ * a quiet one included, takes the band's words with a half-strength
18
+ * underline, since its colour is the words' own (WCAG 1.4.1). The muted
19
+ * words, hairline and subtle fill are mixes of the two band colours, so a
20
+ * brand overlay of `inverse` moves them too; the muted mix keeps 7:1 on
21
+ * the default bands and on a navy one.
22
+ */
23
+ function inverseScope() {
24
+ return `.sw-band-inverse {
25
+ --sw-color-background: var(--sw-color-inverse);
26
+ --sw-color-surface: var(--sw-color-inverse);
27
+ --sw-color-text: var(--sw-color-onInverse);
28
+ --sw-color-muted: color-mix(in srgb, var(--sw-color-onInverse) 78%, var(--sw-color-inverse));
29
+ --sw-color-border: color-mix(in srgb, var(--sw-color-onInverse) 18%, var(--sw-color-inverse));
30
+ --sw-color-subtle: color-mix(in srgb, var(--sw-color-onInverse) 10%, var(--sw-color-inverse));
31
+ --sw-color-accent: var(--sw-color-onInverse);
32
+ --sw-color-onAccent: var(--sw-color-inverse);
33
+ background: var(--sw-color-inverse);
34
+ color: var(--sw-color-onInverse);
35
+ }
36
+
37
+ [data-theme] .sw-band-inverse a:not(.sw-button) {
38
+ color: var(--sw-color-onInverse);
39
+ text-decoration: underline;
40
+ text-decoration-color: color-mix(in srgb, currentColor 50%, transparent);
41
+ text-underline-offset: 0.2em;
42
+ }
43
+
44
+ [data-theme] .sw-band-inverse a:not(.sw-button):hover {
45
+ text-decoration-color: currentColor;
46
+ }`;
47
+ }
48
+ /*
49
+ * The surface band is the cards' fill (white in light, a step above the
50
+ * black page in dark), so it stands apart from a grouped or dark page; the
51
+ * subtle band is the quiet grey.
52
+ *
53
+ * A band is a full-width stretch of the page in one fill with its content
54
+ * in a centred column, the page gutter at its sides (the 16 px phone
55
+ * gutter, 24 px from md).
56
+ */
57
+ export function cssBandClasses() {
58
+ return `.sw-band {
59
+ box-sizing: border-box;
60
+ width: 100%;
61
+ }
62
+
63
+ .sw-band-surface {
64
+ background: var(--sw-color-surface);
65
+ color: var(--sw-color-text);
66
+ }
67
+
68
+ .sw-band-subtle {
69
+ background: var(--sw-color-subtle);
70
+ color: var(--sw-color-text);
71
+ }
72
+
73
+ ${inverseScope()}
74
+
75
+ .sw-band-inner {
76
+ box-sizing: border-box;
77
+ margin-inline: auto;
78
+ padding-inline: var(--sw-space-4);
79
+ width: 100%;
80
+ }
81
+
82
+ .${bandWidthClass('container')} { max-width: calc(var(--sw-container-max) + 2 * var(--sw-space-5)); }
83
+
84
+ .${bandWidthClass('reading')} { max-width: calc(var(--sw-reading-max) + 2 * var(--sw-space-5)); }
85
+
86
+ @media ${breakpointQuery('from', 'md')} {
87
+ .sw-band-inner { padding-inline: var(--sw-space-5); }
88
+ }
89
+
90
+ @media (forced-colors: active) {
91
+ .sw-band-inverse { border-block: 1px solid CanvasText; }
92
+ }`;
93
+ }
94
+ export function bandClassCatalog() {
95
+ return [
96
+ 'sw-band',
97
+ 'sw-band-inner',
98
+ ...bandTones.map(bandToneClass),
99
+ ...bandWidths.map(bandWidthClass),
100
+ ];
101
+ }
@@ -0,0 +1,3 @@
1
+ export declare const buttonLinkClass = "sw-button-link";
2
+ export declare function cssButtonLinkClasses(): string;
3
+ export declare function buttonLinkClassCatalog(): string[];
@@ -0,0 +1,48 @@
1
+ import { pressedRingGap, pressedRingWidth, webButtonVariants, } from './css-button.js';
2
+ /*
3
+ * A link drawn as a Button (`ButtonLink`) wears the Button's own classes.
4
+ * The canvas paints every link in the accent, with no underline, and in the
5
+ * text colour on hover, from `[data-theme] a`; these rules restore each
6
+ * variant's words over that, hover included, at one class more. A link
7
+ * that is the current page (`aria-current="page"`, such as a site's
8
+ * Contact us on Contact) takes the pressed button's accent ring past a gap
9
+ * in the page background, so the mark keeps the accent's contrast whatever
10
+ * the fill; forced colours drop box shadows, so there the same ring is a
11
+ * Highlight border on an out-of-flow ::after, as a pressed Button's is.
12
+ */
13
+ const variantWords = {
14
+ primary: 'var(--sw-color-onAccent)',
15
+ secondary: 'var(--sw-color-onSecondary)',
16
+ tertiary: 'var(--sw-color-onTertiary)',
17
+ ghost: 'var(--sw-color-accent)',
18
+ danger: 'var(--sw-color-onDanger)',
19
+ neutral: 'var(--sw-color-text)',
20
+ };
21
+ export const buttonLinkClass = 'sw-button-link';
22
+ export function cssButtonLinkClasses() {
23
+ const words = webButtonVariants
24
+ .map((variant) => `[data-theme] a.${buttonLinkClass}.sw-button-${variant},\n[data-theme] a.${buttonLinkClass}.sw-button-${variant}:hover { color: ${variantWords[variant]}; }`)
25
+ .join('\n');
26
+ return `[data-theme] a.${buttonLinkClass} {
27
+ text-decoration: none;
28
+ }
29
+
30
+ ${words}
31
+
32
+ [data-theme] a.${buttonLinkClass}[aria-current='page'] {
33
+ box-shadow: 0 0 0 ${pressedRingGap}px var(--sw-color-background), 0 0 0 ${pressedRingWidth}px var(--sw-color-accent);
34
+ }
35
+
36
+ [data-theme] a.${buttonLinkClass}[aria-current='page']:focus-visible {
37
+ outline-offset: calc(var(--sw-focus-ring-offset) + ${pressedRingWidth}px);
38
+ }
39
+
40
+ @media (forced-colors: active) {
41
+ [data-theme] a.${buttonLinkClass} { color: LinkText; }
42
+ [data-theme] a.${buttonLinkClass}[aria-current='page'] { border-color: Highlight; position: relative; }
43
+ [data-theme] a.${buttonLinkClass}[aria-current='page']::after { border: ${pressedRingWidth - pressedRingGap}px solid Highlight; border-radius: inherit; content: ''; inset: calc(-1px - ${pressedRingWidth}px); pointer-events: none; position: absolute; }
44
+ }`;
45
+ }
46
+ export function buttonLinkClassCatalog() {
47
+ return [buttonLinkClass];
48
+ }
@@ -0,0 +1 @@
1
+ export declare function cssCommandPaletteKeyClasses(): string;
@@ -0,0 +1,33 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ const caption = typographyVariants.caption;
3
+ /*
4
+ * A key cap: a key's name or glyph ("⌘K", "↵") in a small rounded hairline
5
+ * box in the muted colour, caption size at weight 500 with tabular figures.
6
+ * CommandPaletteTrigger's shortcut hint and the palette's Enter hint share
7
+ * it. Forced colours keep its edge.
8
+ */
9
+ export function cssCommandPaletteKeyClasses() {
10
+ return `.sw-command-palette-key {
11
+ align-items: center;
12
+ border: 1px solid var(--sw-color-border);
13
+ border-radius: var(--sw-radius-sm);
14
+ box-sizing: border-box;
15
+ color: var(--sw-color-muted);
16
+ display: inline-flex;
17
+ flex: none;
18
+ font-family: var(--sw-font-sans);
19
+ font-size: ${caption.fontSize}px;
20
+ font-variant-numeric: tabular-nums;
21
+ font-weight: 500;
22
+ justify-content: center;
23
+ letter-spacing: ${caption.letterSpacing}px;
24
+ line-height: ${caption.lineHeight}px;
25
+ min-width: var(--sw-space-5);
26
+ padding-inline: var(--sw-space-1);
27
+ white-space: nowrap;
28
+ }
29
+
30
+ @media (forced-colors: active) {
31
+ .sw-command-palette-key { border-color: CanvasText; color: CanvasText; }
32
+ }`;
33
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssCommandPaletteTriggerClasses(): string;
2
+ export declare function commandPaletteTriggerClassCatalog(): string[];
@@ -0,0 +1,88 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { quietFill } from './quiet-fill.js';
3
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
4
+ const label = typographyVariants.label;
5
+ /* The full trigger's least width, a named geometry of this control. */
6
+ const triggerMinWidth = '12rem';
7
+ const trigger = '.sw-command-palette-trigger';
8
+ const compact = `${trigger}.sw-command-palette-trigger-compact`;
9
+ /*
10
+ * The trigger: a search-looking pill (the surface inside the border
11
+ * hairline, fully rounded) holding the glyph, the placeholder words in the
12
+ * muted colour taking the free space, and the key hint at the end; the sm
13
+ * control height, the 44 px target on a coarse pointer. `compact` is the
14
+ * glyph alone in a round quiet button. Hover takes the quiet fill.
15
+ */
16
+ export function cssCommandPaletteTriggerClasses() {
17
+ return `${trigger} {
18
+ align-items: center;
19
+ appearance: none;
20
+ background: var(--sw-color-surface);
21
+ border: 1px solid var(--sw-color-border);
22
+ border-radius: var(--sw-radius-pill);
23
+ box-sizing: border-box;
24
+ color: var(--sw-color-muted);
25
+ cursor: pointer;
26
+ display: inline-flex;
27
+ font-family: var(--sw-font-sans);
28
+ font-size: ${label.fontSize}px;
29
+ font-weight: 400;
30
+ gap: var(--sw-space-2);
31
+ letter-spacing: ${label.letterSpacing}px;
32
+ line-height: ${label.lineHeight}px;
33
+ margin: 0;
34
+ max-width: 100%;
35
+ min-height: var(--sw-control-sm-min-height);
36
+ min-width: min(${triggerMinWidth}, 100%);
37
+ padding-inline: var(--sw-space-3) var(--sw-space-2);
38
+ text-align: start;
39
+ }
40
+
41
+ .sw-command-palette-trigger-icon {
42
+ display: inline-flex;
43
+ flex: none;
44
+ }
45
+
46
+ .sw-command-palette-trigger-text {
47
+ flex: 1 1 auto;
48
+ min-width: 0;
49
+ overflow: hidden;
50
+ text-overflow: ellipsis;
51
+ white-space: nowrap;
52
+ }
53
+
54
+ @media (hover: hover) {
55
+ ${trigger}:hover { background: ${quietFill}; color: var(--sw-color-text); }
56
+ }
57
+
58
+ ${trigger}:focus-visible {
59
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
60
+ outline-offset: var(--sw-focus-ring-offset);
61
+ }
62
+
63
+ ${compact} {
64
+ background: transparent;
65
+ border-color: transparent;
66
+ justify-content: center;
67
+ min-width: var(--sw-control-sm-min-height);
68
+ padding: 0;
69
+ }
70
+
71
+ @media ${coarsePointerQuery} {
72
+ ${trigger} { min-height: ${touchTarget}; }
73
+ ${compact} { min-width: ${touchTarget}; }
74
+ }
75
+
76
+ @media (forced-colors: active) {
77
+ ${trigger} { color: ButtonText; }
78
+ }`;
79
+ }
80
+ export function commandPaletteTriggerClassCatalog() {
81
+ return [
82
+ 'sw-command-palette-trigger',
83
+ 'sw-command-palette-trigger-compact',
84
+ 'sw-command-palette-trigger-icon',
85
+ 'sw-command-palette-trigger-text',
86
+ 'sw-command-palette-key',
87
+ ];
88
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssCommandPaletteClasses(): string;
2
+ export declare function commandPaletteClassCatalog(): string[];
@@ -0,0 +1,209 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { breakpointQuery } from './breakpoints.js';
3
+ import { cssCommandPaletteKeyClasses } from './css-command-palette-key.js';
4
+ import { commandPaletteTriggerClassCatalog, cssCommandPaletteTriggerClasses, } from './css-command-palette-trigger.js';
5
+ import { quietFill } from './quiet-fill.js';
6
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
7
+ const body = typographyVariants.body;
8
+ const caption = typographyVariants.caption;
9
+ const label = typographyVariants.label;
10
+ /* The palette's width, a named geometry of this control. */
11
+ const paletteWidth = '40rem';
12
+ /* Its distance from the window's top: a tenth of the height, at least space-2. */
13
+ const paletteTop = 'max(var(--sw-space-2), 10vh)';
14
+ const fromMd = breakpointQuery('from', 'md');
15
+ /*
16
+ * The palette: a modal dialog on the top layer near the window's top (not
17
+ * centred, so results grow downward), glass like Dialog, 40rem wide (the
18
+ * window less space-2 each side on a phone). Its body is a column: the
19
+ * field, the listbox (the only part that scrolls), the message, the hint.
20
+ */
21
+ function frameRules() {
22
+ return `.sw-command-palette {
23
+ background: var(--sw-glass-fill);
24
+ border: 1px solid var(--sw-glass-border);
25
+ border-radius: var(--sw-radius-lg);
26
+ box-shadow: var(--sw-elevation-sm), inset 0 1px 0 var(--sw-glass-specular);
27
+ backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
28
+ -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
29
+ box-sizing: border-box;
30
+ color: var(--sw-color-text);
31
+ margin: var(--sw-space-2) auto auto;
32
+ max-height: calc(100dvh - 2 * var(--sw-space-2));
33
+ max-width: calc(100% - 2 * var(--sw-space-2));
34
+ overflow: hidden;
35
+ padding: 0;
36
+ width: ${paletteWidth};
37
+ }
38
+
39
+ @media ${fromMd} {
40
+ .sw-command-palette {
41
+ margin-top: ${paletteTop};
42
+ max-height: calc(100dvh - ${paletteTop} - var(--sw-space-8));
43
+ }
44
+ }
45
+
46
+ .sw-command-palette::backdrop {
47
+ background: color-mix(in srgb, var(--sw-color-text) 28%, transparent);
48
+ }
49
+
50
+ .sw-command-palette-body {
51
+ display: flex;
52
+ flex-direction: column;
53
+ max-height: inherit;
54
+ }
55
+
56
+ [data-theme] input.sw-command-palette-field {
57
+ background: transparent;
58
+ border: 0;
59
+ border-bottom: 1px solid var(--sw-color-border);
60
+ border-radius: 0;
61
+ box-sizing: border-box;
62
+ flex: none;
63
+ font-size: ${body.fontSize}px;
64
+ letter-spacing: ${body.letterSpacing}px;
65
+ line-height: ${body.lineHeight}px;
66
+ min-height: ${touchTarget};
67
+ padding: var(--sw-space-3) var(--sw-space-4);
68
+ width: 100%;
69
+ }
70
+
71
+ [data-theme] input.sw-command-palette-field:focus,
72
+ [data-theme] input.sw-command-palette-field:focus-visible {
73
+ border-bottom-color: var(--sw-color-accent);
74
+ box-shadow: inset 0 -1px 0 var(--sw-color-accent);
75
+ outline: none;
76
+ }
77
+
78
+ .sw-command-palette-list {
79
+ flex: 1 1 auto;
80
+ min-height: 0;
81
+ overflow-y: auto;
82
+ overscroll-behavior: contain;
83
+ padding: var(--sw-space-1) var(--sw-space-2);
84
+ }
85
+
86
+ .sw-command-palette-heading {
87
+ color: var(--sw-color-muted);
88
+ font-family: var(--sw-font-sans);
89
+ font-size: ${caption.fontSize}px;
90
+ font-weight: 600;
91
+ letter-spacing: ${caption.letterSpacing}px;
92
+ line-height: ${caption.lineHeight}px;
93
+ padding: var(--sw-space-2) var(--sw-space-2) var(--sw-space-1);
94
+ }
95
+
96
+ .sw-command-palette-message,
97
+ .sw-command-palette-hint {
98
+ color: var(--sw-color-muted);
99
+ font-family: var(--sw-font-sans);
100
+ font-size: ${caption.fontSize}px;
101
+ letter-spacing: ${caption.letterSpacing}px;
102
+ line-height: ${caption.lineHeight}px;
103
+ margin: 0;
104
+ padding: var(--sw-space-3) var(--sw-space-4);
105
+ }
106
+
107
+ .sw-command-palette-hint {
108
+ border-top: 1px solid var(--sw-color-border);
109
+ flex: none;
110
+ }`;
111
+ }
112
+ /*
113
+ * A result: its mark, its label over its muted detail, and on the active
114
+ * row (`aria-selected`, the arrow keys' or the pointer's) the quiet fill
115
+ * and the Enter key cap at the end. The sm control height, 44 px on a
116
+ * coarse pointer. On the fill the detail moves toward the text colour to
117
+ * keep 4.5:1. Forced colours draw the active row in Highlight.
118
+ */
119
+ function itemRules() {
120
+ return `.sw-command-palette-item {
121
+ align-items: center;
122
+ border-radius: var(--sw-radius-sm);
123
+ box-sizing: border-box;
124
+ cursor: pointer;
125
+ display: flex;
126
+ font-family: var(--sw-font-sans);
127
+ font-size: ${label.fontSize}px;
128
+ gap: var(--sw-space-3);
129
+ letter-spacing: ${label.letterSpacing}px;
130
+ line-height: ${label.lineHeight}px;
131
+ min-height: var(--sw-control-sm-min-height);
132
+ overflow-wrap: anywhere;
133
+ padding: var(--sw-space-1) var(--sw-space-2);
134
+ }
135
+
136
+ .sw-command-palette-item[aria-selected='true'] {
137
+ background: ${quietFill};
138
+ }
139
+
140
+ .sw-command-palette-icon {
141
+ display: inline-flex;
142
+ flex: none;
143
+ }
144
+
145
+ .sw-command-palette-words {
146
+ display: flex;
147
+ flex: 1 1 auto;
148
+ flex-direction: column;
149
+ min-width: 0;
150
+ }
151
+
152
+ .sw-command-palette-label {
153
+ font-weight: 500;
154
+ }
155
+
156
+ .sw-command-palette-detail {
157
+ color: var(--sw-color-muted);
158
+ font-size: ${caption.fontSize}px;
159
+ letter-spacing: ${caption.letterSpacing}px;
160
+ line-height: ${caption.lineHeight}px;
161
+ }
162
+
163
+ .sw-command-palette-item[aria-selected='true'] .sw-command-palette-detail {
164
+ color: color-mix(in srgb, var(--sw-color-muted) 70%, var(--sw-color-text));
165
+ }
166
+
167
+ @media ${coarsePointerQuery} {
168
+ .sw-command-palette-item { min-height: ${touchTarget}; }
169
+ }
170
+
171
+ @media (forced-colors: active) {
172
+ .sw-command-palette { border-color: CanvasText; }
173
+ .sw-command-palette-item[aria-selected='true'] { background: Highlight; color: HighlightText; forced-color-adjust: none; }
174
+ .sw-command-palette-item[aria-selected='true'] .sw-command-palette-detail,
175
+ .sw-command-palette-item[aria-selected='true'] .sw-command-palette-key { border-color: HighlightText; color: HighlightText; }
176
+ [data-theme] input.sw-command-palette-field:focus-visible { border-bottom-color: Highlight; box-shadow: inset 0 -1px 0 Highlight; }
177
+ }`;
178
+ }
179
+ /*
180
+ * CommandPalette and CommandPaletteTrigger (see components/CommandPalette.tsx):
181
+ * the trigger is `css-command-palette-trigger.ts`, the shared key cap
182
+ * `css-command-palette-key.ts`.
183
+ */
184
+ export function cssCommandPaletteClasses() {
185
+ return `${cssCommandPaletteKeyClasses()}
186
+
187
+ ${cssCommandPaletteTriggerClasses()}
188
+
189
+ ${frameRules()}
190
+
191
+ ${itemRules()}`;
192
+ }
193
+ export function commandPaletteClassCatalog() {
194
+ return [
195
+ ...commandPaletteTriggerClassCatalog(),
196
+ 'sw-command-palette',
197
+ 'sw-command-palette-body',
198
+ 'sw-command-palette-field',
199
+ 'sw-command-palette-list',
200
+ 'sw-command-palette-heading',
201
+ 'sw-command-palette-item',
202
+ 'sw-command-palette-icon',
203
+ 'sw-command-palette-words',
204
+ 'sw-command-palette-label',
205
+ 'sw-command-palette-detail',
206
+ 'sw-command-palette-message',
207
+ 'sw-command-palette-hint',
208
+ ];
209
+ }