@marianmeres/stuic 3.186.0 → 3.187.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/API.md
CHANGED
|
@@ -153,38 +153,38 @@ Navigation wrapper component.
|
|
|
153
153
|
|
|
154
154
|
Responsive navigation header with leading slot, logo, nav items, locale switcher, action icon buttons, avatar, and configurable responsive collapse (`"hamburger"` fold or `"hide"` for app-like shells). Renders as `<header>`.
|
|
155
155
|
|
|
156
|
-
| Prop | Type | Default | Description
|
|
157
|
-
| ----------------------- | ------------------------------------------------ | --------------------- |
|
|
158
|
-
| `leading` | `Snippet<[{ isCollapsed }]>` | — | Leading (left-side) slot. Overrides `leadingHamburger`.
|
|
159
|
-
| `leadingHamburger` | `boolean \| "collapsed"` | `false` | Built-in hamburger in the leading slot (`"collapsed"` = only below threshold). Ignored when `leading` is provided.
|
|
160
|
-
| `onLeadingHamburger` | `() => void` | — | Click handler for the built-in leading hamburger (typically opens a drawer).
|
|
161
|
-
| `leadingHamburgerIcon` | `THC` | menu icon | Icon override for the built-in leading hamburger.
|
|
162
|
-
| `leadingHamburgerLabel` | `string` | `"Open menu"` | Aria-label for the leading hamburger.
|
|
163
|
-
| `logo` | `Snippet` | — | Logo/brand snippet.
|
|
164
|
-
| `projectName` | `string` | — | Simple text logo alternative.
|
|
165
|
-
| `navVariant` | `ButtonVariant` | `"ghost"` | Button variant for nav items and the locale switcher trigger.
|
|
166
|
-
| `items` | `HeaderNavItem[]` | `[]` | Navigation items — inline when expanded, dropdown when collapsed (hamburger mode).
|
|
167
|
-
| `actions` | `HeaderActionItem[]` | `[]` | Action icon buttons between the locale switcher and the avatar. Always visible — never fold into the dropdown.
|
|
168
|
-
| `onActionSelect` | `(action) => void` | — | Called after the per-item `onclick`.
|
|
169
|
-
| `avatar` | `Snippet` | — | Avatar snippet (far right).
|
|
170
|
-
| `avatarOnClick` | `() => void` | — | Makes the avatar interactive. In `"hamburger"` collapse mode it moves into the dropdown.
|
|
171
|
-
| `avatarLabel` | `THC` | `"Account"` | Label for the avatar entry inside the collapsed dropdown.
|
|
172
|
-
| `locales` | `HeaderLocaleItem[]` | `[]` | Locale items. Switcher only renders when 2+.
|
|
173
|
-
| `activeLocale` | `string` | — | Current locale id.
|
|
174
|
-
| `onLocaleChange` | `(localeId) => void` | — | Locale selection callback.
|
|
175
|
-
| `localeLabel` | `THC` | `"Language"` | Section header inside the collapsed dropdown.
|
|
176
|
-
| `contentMaxWidth` | `string \| number` | — | Max-width of the inner content row (outer header stays 100%). Accepts any CSS length. Maps to `--stuic-header-content-max-width`.
|
|
177
|
-
| `collapseThreshold` | `number` | `768` | Width (px) to collapse; 0 disables.
|
|
178
|
-
| `collapseMode` | `"hamburger" \| "hide"` | `"hamburger"` | Collapse behavior. `"hide"` keeps avatar/actions visible and renders no trailing hamburger (app-shell pattern).
|
|
179
|
-
| `keepLocaleOnCollapse` | `boolean` | `
|
|
180
|
-
| `fixed` | `boolean` | `false` | Fixed positioning at the top.
|
|
181
|
-
| `safeArea` | `boolean` | `false` | PWA: when installed/standalone, offset the **top app bar** below the device safe-area insets (top + side notch). No-op in a browser tab. Set only on the top bar — not in-page/detail/drawer headers. See Header README.
|
|
182
|
-
| `isCollapsed` | `boolean` | — | Bindable: collapsed state.
|
|
183
|
-
| `isMenuOpen` | `boolean` | — | Bindable: hamburger menu open.
|
|
184
|
-
| `dropdownPosition` | `DropdownMenuPosition` | `"bottom-span-right"` | Position of the collapsed dropdown.
|
|
185
|
-
| `iconSize` | `number` | `24` | Hamburger/X icon size in px.
|
|
186
|
-
| `onSelect` | `(item) => void` | — | Item selection callback (both modes).
|
|
187
|
-
| `children` | `Snippet<[{ isCollapsed, items, offsetWidth }]>` | — | Escape hatch: override the entire inner layout.
|
|
156
|
+
| Prop | Type | Default | Description |
|
|
157
|
+
| ----------------------- | ------------------------------------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
158
|
+
| `leading` | `Snippet<[{ isCollapsed }]>` | — | Leading (left-side) slot. Overrides `leadingHamburger`. |
|
|
159
|
+
| `leadingHamburger` | `boolean \| "collapsed"` | `false` | Built-in hamburger in the leading slot (`"collapsed"` = only below threshold). Ignored when `leading` is provided. |
|
|
160
|
+
| `onLeadingHamburger` | `() => void` | — | Click handler for the built-in leading hamburger (typically opens a drawer). |
|
|
161
|
+
| `leadingHamburgerIcon` | `THC` | menu icon | Icon override for the built-in leading hamburger. |
|
|
162
|
+
| `leadingHamburgerLabel` | `string` | `"Open menu"` | Aria-label for the leading hamburger. |
|
|
163
|
+
| `logo` | `Snippet` | — | Logo/brand snippet. |
|
|
164
|
+
| `projectName` | `string` | — | Simple text logo alternative. |
|
|
165
|
+
| `navVariant` | `ButtonVariant` | `"ghost"` | Button variant for nav items and the locale switcher trigger. |
|
|
166
|
+
| `items` | `HeaderNavItem[]` | `[]` | Navigation items — inline when expanded, dropdown when collapsed (hamburger mode). |
|
|
167
|
+
| `actions` | `HeaderActionItem[]` | `[]` | Action icon buttons between the locale switcher and the avatar. Always visible — never fold into the dropdown. |
|
|
168
|
+
| `onActionSelect` | `(action) => void` | — | Called after the per-item `onclick`. |
|
|
169
|
+
| `avatar` | `Snippet` | — | Avatar snippet (far right). |
|
|
170
|
+
| `avatarOnClick` | `() => void` | — | Makes the avatar interactive. In `"hamburger"` collapse mode it moves into the dropdown. |
|
|
171
|
+
| `avatarLabel` | `THC` | `"Account"` | Label for the avatar entry inside the collapsed dropdown. |
|
|
172
|
+
| `locales` | `HeaderLocaleItem[]` | `[]` | Locale items. Switcher only renders when 2+. |
|
|
173
|
+
| `activeLocale` | `string` | — | Current locale id. |
|
|
174
|
+
| `onLocaleChange` | `(localeId) => void` | — | Locale selection callback. |
|
|
175
|
+
| `localeLabel` | `THC` | `"Language"` | Section header inside the collapsed dropdown. |
|
|
176
|
+
| `contentMaxWidth` | `string \| number` | — | Max-width of the inner content row (outer header stays 100%). Accepts any CSS length. Maps to `--stuic-header-content-max-width`. |
|
|
177
|
+
| `collapseThreshold` | `number` | `768` | Width (px) to collapse; 0 disables. |
|
|
178
|
+
| `collapseMode` | `"hamburger" \| "hide"` | `"hamburger"` | Collapse behavior. `"hide"` keeps avatar/actions visible and renders no trailing hamburger (app-shell pattern). |
|
|
179
|
+
| `keepLocaleOnCollapse` | `boolean` | `true` | Keep the locale switcher inline (next to the hamburger) when collapsed — in **both** collapse modes, and without duplicating it inside the dropdown. `false` folds it into the dropdown (`"hamburger"`) or hides it (`"hide"`). |
|
|
180
|
+
| `fixed` | `boolean` | `false` | Fixed positioning at the top. |
|
|
181
|
+
| `safeArea` | `boolean` | `false` | PWA: when installed/standalone, offset the **top app bar** below the device safe-area insets (top + side notch). No-op in a browser tab. Set only on the top bar — not in-page/detail/drawer headers. See Header README. |
|
|
182
|
+
| `isCollapsed` | `boolean` | — | Bindable: collapsed state. |
|
|
183
|
+
| `isMenuOpen` | `boolean` | — | Bindable: hamburger menu open. |
|
|
184
|
+
| `dropdownPosition` | `DropdownMenuPosition` | `"bottom-span-right"` | Position of the collapsed dropdown. |
|
|
185
|
+
| `iconSize` | `number` | `24` | Hamburger/X icon size in px. |
|
|
186
|
+
| `onSelect` | `(item) => void` | — | Item selection callback (both modes). |
|
|
187
|
+
| `children` | `Snippet<[{ isCollapsed, items, offsetWidth }]>` | — | Escape hatch: override the entire inner layout. |
|
|
188
188
|
|
|
189
189
|
Class slots: `class`, `classContent`, `classLeading`, `classLeadingHamburger`, `classLogo`, `classNav`, `classNavItem`, `classNavItemActive`, `classActions`, `classAction`, `classActionActive`, `classEnd`, `classAvatar`, `classLocale`, `classHamburger`, `classDropdown`.
|
|
190
190
|
|
|
@@ -223,9 +223,9 @@ Class slots: `class`, `classContent`, `classLeading`, `classLeadingHamburger`, `
|
|
|
223
223
|
|
|
224
224
|
**HeaderActionItem:** `{ id, icon?, label, onclick?, href?, target?, active?, disabled?, class?, render? }` — `render` is an optional `Snippet<[{ action, class, isCollapsed, onclick }]>` that replaces the default `<Button>` for that action (useful for wrapping in a popover/tooltip directive or adding a count badge while keeping default positioning).
|
|
225
225
|
|
|
226
|
-
**HeaderLocaleItem:** `{ id, label }`
|
|
226
|
+
**HeaderLocaleItem:** `{ id, label, shortLabel? }` — `shortLabel` replaces `label` on the inline trigger **in collapsed mode only** (keeps a long name like "Slovenčina" from crowding a narrow header); the dropdown list always shows the full `label`.
|
|
227
227
|
|
|
228
|
-
CSS tokens: `--stuic-header-padding-x`, `--stuic-header-padding-y`, `--stuic-header-gap`, `--stuic-header-min-height`, `--stuic-header-nav-gap`, `--stuic-header-content-max-width`, `--stuic-header-bg`, `--stuic-header-text`, `--stuic-header-border-width`, `--stuic-header-border-color`, `--stuic-header-nav-item-bg-active`, `--stuic-header-nav-item-text-active`, `--stuic-header-z-index`.
|
|
228
|
+
CSS tokens: `--stuic-header-padding-x`, `--stuic-header-padding-y`, `--stuic-header-gap`, `--stuic-header-end-gap-collapsed`, `--stuic-header-min-height`, `--stuic-header-nav-gap`, `--stuic-header-content-max-width`, `--stuic-header-bg`, `--stuic-header-text`, `--stuic-header-border-width`, `--stuic-header-border-color`, `--stuic-header-nav-item-bg-active`, `--stuic-header-nav-item-text-active`, `--stuic-header-z-index`.
|
|
229
229
|
|
|
230
230
|
---
|
|
231
231
|
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
id: string;
|
|
36
36
|
/** Display label — supports THC (string, html, component, snippet) */
|
|
37
37
|
label: THC;
|
|
38
|
+
/** Compact label for the inline trigger in COLLAPSED mode only.
|
|
39
|
+
* Falls back to `label`. Use it when the full label ("Slovenčina")
|
|
40
|
+
* would crowd the narrow header next to the hamburger — the dropdown
|
|
41
|
+
* list always shows the full `label`, so the long form stays
|
|
42
|
+
* available where there is room to read it. */
|
|
43
|
+
shortLabel?: THC;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
export interface HeaderActionItem {
|
|
@@ -86,11 +92,13 @@
|
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
/** Collapse behavior when the header drops below `collapseThreshold`:
|
|
89
|
-
* - "hamburger": nav items fold into a trailing dropdown along with
|
|
90
|
-
*
|
|
95
|
+
* - "hamburger": nav items fold into a trailing dropdown along with an
|
|
96
|
+
* interactive avatar.
|
|
91
97
|
* - "hide": nav items are hidden entirely. No trailing hamburger renders.
|
|
92
|
-
* Avatar stays visible.
|
|
93
|
-
*
|
|
98
|
+
* Avatar stays visible.
|
|
99
|
+
*
|
|
100
|
+
* In BOTH modes the locale switcher stays inline next to the hamburger
|
|
101
|
+
* by default — see `keepLocaleOnCollapse`. */
|
|
94
102
|
export type HeaderCollapseMode = "hamburger" | "hide";
|
|
95
103
|
|
|
96
104
|
/** Visibility for the built-in leading hamburger button:
|
|
@@ -152,9 +160,27 @@
|
|
|
152
160
|
collapseThreshold?: number;
|
|
153
161
|
/** Collapse behavior when below threshold (defaults to "hamburger") */
|
|
154
162
|
collapseMode?: HeaderCollapseMode;
|
|
155
|
-
/**
|
|
156
|
-
* collapsed
|
|
157
|
-
*
|
|
163
|
+
/** Keep the locale switcher visible — inline, next to the hamburger —
|
|
164
|
+
* when the header is collapsed. Applies to BOTH collapse modes.
|
|
165
|
+
* Defaults to `true`.
|
|
166
|
+
*
|
|
167
|
+
* Why it defaults on: the person who most needs this control is the
|
|
168
|
+
* one who landed in a language they cannot read. For them a visible
|
|
169
|
+
* "EN ▾" trigger is the only self-describing widget in the bar, while
|
|
170
|
+
* a locale entry folded into the hamburger — listed last, under a
|
|
171
|
+
* "Language" heading they also cannot read, often below the fold of a
|
|
172
|
+
* long menu — is effectively invisible.
|
|
173
|
+
*
|
|
174
|
+
* When `true` in `"hamburger"` mode the locale section is NOT also
|
|
175
|
+
* appended to the trailing dropdown, so it is never reachable twice.
|
|
176
|
+
*
|
|
177
|
+
* Set `false` for the alternative: folds into the trailing dropdown
|
|
178
|
+
* in `"hamburger"` mode, hidden entirely in `"hide"` mode (this was
|
|
179
|
+
* the behavior before the prop applied to both modes). Worth doing
|
|
180
|
+
* when the end area is already crowded with actions, or when the
|
|
181
|
+
* app's own drawer owns the language switch. See also
|
|
182
|
+
* `HeaderLocaleItem.shortLabel` for keeping the inline trigger
|
|
183
|
+
* narrow. */
|
|
158
184
|
keepLocaleOnCollapse?: boolean;
|
|
159
185
|
/** Fixed positioning (top of viewport) */
|
|
160
186
|
fixed?: boolean;
|
|
@@ -263,7 +289,7 @@
|
|
|
263
289
|
contentMaxWidth,
|
|
264
290
|
collapseThreshold = 768,
|
|
265
291
|
collapseMode = "hamburger",
|
|
266
|
-
keepLocaleOnCollapse =
|
|
292
|
+
keepLocaleOnCollapse = true,
|
|
267
293
|
fixed = false,
|
|
268
294
|
safeArea = false,
|
|
269
295
|
isCollapsed = $bindable(false),
|
|
@@ -336,17 +362,28 @@
|
|
|
336
362
|
// Locale switcher: only render when 2+ locales
|
|
337
363
|
let _hasLocales = $derived(locales.length > 1);
|
|
338
364
|
|
|
339
|
-
// Visibility of the inline (expanded-form) locale switcher.
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
//
|
|
365
|
+
// Visibility of the inline (expanded-form) locale switcher. Always visible
|
|
366
|
+
// when expanded; when collapsed it stays inline unless explicitly opted out
|
|
367
|
+
// — in BOTH collapse modes. Deliberately mode-agnostic: a user who cannot
|
|
368
|
+
// read the current language must be able to find the switcher without
|
|
369
|
+
// opening a menu whose trigger tells them nothing.
|
|
343
370
|
let _showLocaleSwitcher = $derived(
|
|
344
|
-
_hasLocales && (!_isCollapsed ||
|
|
371
|
+
_hasLocales && (!_isCollapsed || keepLocaleOnCollapse)
|
|
345
372
|
);
|
|
346
373
|
|
|
347
374
|
// Active locale object (for trigger label); fallback to first
|
|
348
375
|
let _activeLocale = $derived(locales.find((l) => l.id === activeLocale) ?? locales[0]);
|
|
349
376
|
|
|
377
|
+
// Inline trigger label. `shortLabel` (when provided) is used ONLY in
|
|
378
|
+
// collapsed mode, where horizontal space next to the hamburger is scarce;
|
|
379
|
+
// expanded mode and the dropdown list always show the full `label`.
|
|
380
|
+
let _localeTriggerLabel = $derived.by((): THC | undefined => {
|
|
381
|
+
if (!_activeLocale) return undefined;
|
|
382
|
+
return _isCollapsed
|
|
383
|
+
? (_activeLocale.shortLabel ?? _activeLocale.label)
|
|
384
|
+
: _activeLocale.label;
|
|
385
|
+
});
|
|
386
|
+
|
|
350
387
|
// Locale items for the expanded-mode DropdownMenu
|
|
351
388
|
let _localeDropdownItems = $derived.by((): DropdownMenuItem[] => {
|
|
352
389
|
return locales.map(
|
|
@@ -389,8 +426,10 @@
|
|
|
389
426
|
}) satisfies DropdownMenuActionItem
|
|
390
427
|
);
|
|
391
428
|
|
|
392
|
-
// Append locale section when
|
|
393
|
-
|
|
429
|
+
// Append the locale section ONLY when the inline switcher is hidden.
|
|
430
|
+
// With the inline trigger visible this would be a second, worse route
|
|
431
|
+
// to the same setting — and the one nobody finds.
|
|
432
|
+
if (_hasLocales && !_showLocaleSwitcher) {
|
|
394
433
|
if (navItems.length > 0) {
|
|
395
434
|
navItems.push({ type: "divider" });
|
|
396
435
|
}
|
|
@@ -556,7 +595,9 @@
|
|
|
556
595
|
|
|
557
596
|
<!-- End area: locale + actions + avatar + trailing hamburger -->
|
|
558
597
|
<div class={_classEnd}>
|
|
559
|
-
<!-- Locale switcher
|
|
598
|
+
<!-- Locale switcher — inline when expanded and, unless
|
|
599
|
+
`keepLocaleOnCollapse={false}`, still inline when collapsed
|
|
600
|
+
(both collapse modes) rather than folded into the dropdown -->
|
|
560
601
|
{#if _showLocaleSwitcher}
|
|
561
602
|
<DropdownMenu
|
|
562
603
|
items={_localeDropdownItems}
|
|
@@ -573,8 +614,8 @@
|
|
|
573
614
|
aria-label="Change language"
|
|
574
615
|
{...triggerProps}
|
|
575
616
|
>
|
|
576
|
-
{#if
|
|
577
|
-
<Thc thc={
|
|
617
|
+
{#if _localeTriggerLabel !== undefined}
|
|
618
|
+
<Thc thc={_localeTriggerLabel} />
|
|
578
619
|
{/if}
|
|
579
620
|
<span
|
|
580
621
|
class={twMerge(
|
|
@@ -28,6 +28,12 @@ export interface HeaderLocaleItem {
|
|
|
28
28
|
id: string;
|
|
29
29
|
/** Display label — supports THC (string, html, component, snippet) */
|
|
30
30
|
label: THC;
|
|
31
|
+
/** Compact label for the inline trigger in COLLAPSED mode only.
|
|
32
|
+
* Falls back to `label`. Use it when the full label ("Slovenčina")
|
|
33
|
+
* would crowd the narrow header next to the hamburger — the dropdown
|
|
34
|
+
* list always shows the full `label`, so the long form stays
|
|
35
|
+
* available where there is room to read it. */
|
|
36
|
+
shortLabel?: THC;
|
|
31
37
|
}
|
|
32
38
|
export interface HeaderActionItem {
|
|
33
39
|
/** Unique identifier */
|
|
@@ -75,11 +81,13 @@ export interface HeaderActionItem {
|
|
|
75
81
|
]>;
|
|
76
82
|
}
|
|
77
83
|
/** Collapse behavior when the header drops below `collapseThreshold`:
|
|
78
|
-
* - "hamburger": nav items fold into a trailing dropdown along with
|
|
79
|
-
*
|
|
84
|
+
* - "hamburger": nav items fold into a trailing dropdown along with an
|
|
85
|
+
* interactive avatar.
|
|
80
86
|
* - "hide": nav items are hidden entirely. No trailing hamburger renders.
|
|
81
|
-
* Avatar stays visible.
|
|
82
|
-
*
|
|
87
|
+
* Avatar stays visible.
|
|
88
|
+
*
|
|
89
|
+
* In BOTH modes the locale switcher stays inline next to the hamburger
|
|
90
|
+
* by default — see `keepLocaleOnCollapse`. */
|
|
83
91
|
export type HeaderCollapseMode = "hamburger" | "hide";
|
|
84
92
|
/** Visibility for the built-in leading hamburger button:
|
|
85
93
|
* - false/undefined: not rendered
|
|
@@ -141,9 +149,27 @@ export interface Props extends Omit<HTMLAttributes<HTMLElement>, "children"> {
|
|
|
141
149
|
collapseThreshold?: number;
|
|
142
150
|
/** Collapse behavior when below threshold (defaults to "hamburger") */
|
|
143
151
|
collapseMode?: HeaderCollapseMode;
|
|
144
|
-
/**
|
|
145
|
-
* collapsed
|
|
146
|
-
*
|
|
152
|
+
/** Keep the locale switcher visible — inline, next to the hamburger —
|
|
153
|
+
* when the header is collapsed. Applies to BOTH collapse modes.
|
|
154
|
+
* Defaults to `true`.
|
|
155
|
+
*
|
|
156
|
+
* Why it defaults on: the person who most needs this control is the
|
|
157
|
+
* one who landed in a language they cannot read. For them a visible
|
|
158
|
+
* "EN ▾" trigger is the only self-describing widget in the bar, while
|
|
159
|
+
* a locale entry folded into the hamburger — listed last, under a
|
|
160
|
+
* "Language" heading they also cannot read, often below the fold of a
|
|
161
|
+
* long menu — is effectively invisible.
|
|
162
|
+
*
|
|
163
|
+
* When `true` in `"hamburger"` mode the locale section is NOT also
|
|
164
|
+
* appended to the trailing dropdown, so it is never reachable twice.
|
|
165
|
+
*
|
|
166
|
+
* Set `false` for the alternative: folds into the trailing dropdown
|
|
167
|
+
* in `"hamburger"` mode, hidden entirely in `"hide"` mode (this was
|
|
168
|
+
* the behavior before the prop applied to both modes). Worth doing
|
|
169
|
+
* when the end area is already crowded with actions, or when the
|
|
170
|
+
* app's own drawer owns the language switch. See also
|
|
171
|
+
* `HeaderLocaleItem.shortLabel` for keeping the inline trigger
|
|
172
|
+
* narrow. */
|
|
147
173
|
keepLocaleOnCollapse?: boolean;
|
|
148
174
|
/** Fixed positioning (top of viewport) */
|
|
149
175
|
fixed?: boolean;
|
|
@@ -4,17 +4,54 @@ Top-bar component with leading slot, project logo, nav items, locale switcher, o
|
|
|
4
4
|
|
|
5
5
|
## Examples
|
|
6
6
|
|
|
7
|
+
### The locale switcher stays visible when collapsed
|
|
8
|
+
|
|
9
|
+
When the header collapses, the locale switcher does **not** fold into the trailing hamburger. It stays inline, immediately before the actions / avatar / hamburger, in **both** collapse modes. Nothing to configure — it is the default.
|
|
10
|
+
|
|
11
|
+
```svelte
|
|
12
|
+
<Header
|
|
13
|
+
projectName="App"
|
|
14
|
+
items={navItems}
|
|
15
|
+
{locales}
|
|
16
|
+
{activeLocale}
|
|
17
|
+
onLocaleChange={(id) => (activeLocale = id)}
|
|
18
|
+
/>
|
|
19
|
+
<!-- collapsed: [App] [EN ▾] [☰] -->
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This is the one control `Header` deliberately treats differently from a nav item, and the reason is worth stating because it looks like an inconsistency:
|
|
23
|
+
|
|
24
|
+
> The person who most needs the language switch is the person who landed in a language they cannot read. A visible `EN ▾` trigger is self-describing to them — it shows the current language and it is obviously a control. An entry inside the hamburger is not: the trigger is an unlabeled icon, the section heading says "Language" in a language they do not speak, and past a handful of nav items it sits below the fold of a menu they have to scroll with a finger. It is reliably never found.
|
|
25
|
+
|
|
26
|
+
What follows from it:
|
|
27
|
+
|
|
28
|
+
| Behavior | Why |
|
|
29
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
30
|
+
| The trailing dropdown has **no** locale section while the inline trigger is visible. | Two routes to one setting, one of which nobody finds. `_dropdownItems` appends the locale section only when the inline switcher is hidden. |
|
|
31
|
+
| A header whose only collapsible content was the locale switcher renders **no hamburger at all**. | `_dropdownItems` comes back empty, and the trailing `DropdownMenu` is gated on it being non-empty. |
|
|
32
|
+
| The inline trigger shows `HeaderLocaleItem.shortLabel` (when set) **only** while collapsed. | `"Slovenčina"` next to a hamburger on a 360px phone is what pushed the switcher into the menu in the first place. The dropdown list always shows the full `label`. |
|
|
33
|
+
| `keepLocaleOnCollapse={false}` restores the alternative: folds into the dropdown in `"hamburger"` mode, hidden entirely in `"hide"` mode. | For an end area already crowded with actions, or an app whose own drawer owns the language switch. |
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
// Long locale names on a narrow header: short form on the inline trigger,
|
|
37
|
+
// full form in the dropdown list.
|
|
38
|
+
const locales: HeaderLocaleItem[] = [
|
|
39
|
+
{ id: "en", label: "English", shortLabel: "EN" },
|
|
40
|
+
{ id: "sk", label: "Slovenčina", shortLabel: "SK" },
|
|
41
|
+
];
|
|
42
|
+
```
|
|
43
|
+
|
|
7
44
|
### App-like collapse: avatar + actions visible, everything else hidden
|
|
8
45
|
|
|
9
|
-
Common "app shell" pattern: when the header collapses below `collapseThreshold`, the avatar and a few key actions (search, notifications, cart…) remain visible, the trailing hamburger is NOT shown, and the nav items
|
|
46
|
+
Common "app shell" pattern: when the header collapses below `collapseThreshold`, the avatar, the locale switcher and a few key actions (search, notifications, cart…) remain visible, the trailing hamburger is NOT shown, and the nav items are hidden entirely (the nav typically lives in a drawer triggered by the leading hamburger instead).
|
|
10
47
|
|
|
11
|
-
| Requirement | Where it's handled
|
|
12
|
-
| ------------------------------- |
|
|
13
|
-
| **Avatar stays visible** | [Header.svelte:
|
|
14
|
-
| **Action buttons stay visible** | [Header.svelte:
|
|
15
|
-
| **No trailing hamburger** | [Header.svelte:
|
|
16
|
-
| **Nav items hidden** | [Header.svelte:
|
|
17
|
-
| **Locale
|
|
48
|
+
| Requirement | Where it's handled | How |
|
|
49
|
+
| ------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
50
|
+
| **Avatar stays visible** | [Header.svelte:673](./Header.svelte#L673) — `{#if avatar && !(_isCollapsed && _avatarInDropdown)}` | `_avatarInDropdown` requires `collapseMode === "hamburger"`. In `"hide"` mode it's always `false`, so the avatar always renders. |
|
|
51
|
+
| **Action buttons stay visible** | [Header.svelte:632](./Header.svelte#L632) — `<!-- Actions (icon buttons, always visible) -->` | The actions loop has no collapse gating — items render in both modes. |
|
|
52
|
+
| **No trailing hamburger** | [Header.svelte:689](./Header.svelte#L689) — `{#if _isCollapsed && _dropdownItems.length > 0}` | In `"hide"` mode, `_dropdownItems` short-circuits to `[]`, so the `{#if}` is false → no trailing hamburger. |
|
|
53
|
+
| **Nav items hidden** | [Header.svelte:564](./Header.svelte#L564) — `{#if !_isCollapsed && items.length > 0}` | Inline nav requires `!_isCollapsed`; combined with the empty `_dropdownItems` above, items don't reappear in a dropdown either. |
|
|
54
|
+
| **Locale stays visible** | [Header.svelte:369](./Header.svelte#L369) — `!_isCollapsed \|\| keepLocaleOnCollapse` | `keepLocaleOnCollapse` defaults to `true` in both modes (see the section above). Pass `false` to hide it, e.g. when the drawer owns the language switch. |
|
|
18
55
|
|
|
19
56
|
Minimal config:
|
|
20
57
|
|
|
@@ -26,7 +63,7 @@ Minimal config:
|
|
|
26
63
|
collapseMode="hide" <!-- no trailing hamburger; avatar stays -->
|
|
27
64
|
leadingHamburger <!-- optional: drives a drawer for the hidden nav -->
|
|
28
65
|
onLeadingHamburger={() => (drawerOpen = true)}
|
|
29
|
-
{locales} {activeLocale} <!--
|
|
66
|
+
{locales} {activeLocale} <!-- stays visible in collapsed (keepLocaleOnCollapse defaults to true) -->
|
|
30
67
|
onLocaleChange={(id) => (activeLocale = id)}
|
|
31
68
|
avatarOnClick={() => alert("Profile")} <!-- safe in "hide" mode — won't move into dropdown -->
|
|
32
69
|
>
|
|
@@ -21,6 +21,14 @@
|
|
|
21
21
|
/* Actions (icon buttons in the end region) */
|
|
22
22
|
--stuic-header-actions-gap: 0.25rem;
|
|
23
23
|
|
|
24
|
+
/* Gap between the end-area regions (locale / actions / avatar / hamburger)
|
|
25
|
+
while collapsed. Expanded uses --stuic-header-gap; collapsed is tighter
|
|
26
|
+
because the locale switcher stays inline there instead of folding into
|
|
27
|
+
the hamburger, and at 320px the full gap pushes a locale + 3 actions +
|
|
28
|
+
hamburger row past the content edge. Set to var(--stuic-header-gap) to
|
|
29
|
+
keep both states equal. */
|
|
30
|
+
--stuic-header-end-gap-collapsed: 0.5rem;
|
|
31
|
+
|
|
24
32
|
/* Hamburger offsets — outdent the built-in hamburger buttons on both
|
|
25
33
|
inline sides so the *icon* (not the invisible iconButton hit area)
|
|
26
34
|
sits flush with the header's content padding on one side and tightens
|
|
@@ -126,6 +134,14 @@
|
|
|
126
134
|
font-size: var(--stuic-header-project-name-font-size, var(--text-lg, 1.125rem));
|
|
127
135
|
font-weight: var(--stuic-header-project-name-font-weight);
|
|
128
136
|
white-space: nowrap;
|
|
137
|
+
/* Truncate instead of overflowing the row. `min-width: 0` lets this
|
|
138
|
+
flex item shrink below its content width (auto minimum size would
|
|
139
|
+
otherwise pin it), and `overflow: hidden` is what makes
|
|
140
|
+
`text-overflow` apply. Matters most in collapsed mode, where the
|
|
141
|
+
name shares the row with the locale trigger + hamburger. */
|
|
142
|
+
min-width: 0;
|
|
143
|
+
overflow: hidden;
|
|
144
|
+
text-overflow: ellipsis;
|
|
129
145
|
}
|
|
130
146
|
|
|
131
147
|
/* ============================================================================
|
|
@@ -193,6 +209,10 @@
|
|
|
193
209
|
flex-shrink: 0;
|
|
194
210
|
}
|
|
195
211
|
|
|
212
|
+
.stuic-header[data-collapsed] .stuic-header-end {
|
|
213
|
+
gap: var(--stuic-header-end-gap-collapsed);
|
|
214
|
+
}
|
|
215
|
+
|
|
196
216
|
.stuic-header-avatar {
|
|
197
217
|
display: flex;
|
|
198
218
|
align-items: center;
|
|
@@ -1515,6 +1515,18 @@ interface HeaderActionItem {
|
|
|
1515
1515
|
}
|
|
1516
1516
|
```
|
|
1517
1517
|
|
|
1518
|
+
### HeaderLocaleItem
|
|
1519
|
+
|
|
1520
|
+
```ts
|
|
1521
|
+
interface HeaderLocaleItem {
|
|
1522
|
+
id: string;
|
|
1523
|
+
label: THC;
|
|
1524
|
+
/** Compact label for the inline trigger in COLLAPSED mode only
|
|
1525
|
+
* (falls back to `label`); the dropdown list always shows `label`. */
|
|
1526
|
+
shortLabel?: THC;
|
|
1527
|
+
}
|
|
1528
|
+
```
|
|
1529
|
+
|
|
1518
1530
|
### Key Props
|
|
1519
1531
|
|
|
1520
1532
|
| Prop | Type | Default | Description |
|
|
@@ -1540,7 +1552,7 @@ interface HeaderActionItem {
|
|
|
1540
1552
|
| `contentMaxWidth` | `string \| number` | — | Inner content row max-width (outer header stays 100%). |
|
|
1541
1553
|
| `collapseThreshold` | `number` | `768` | Width (px) to collapse; 0 disables. |
|
|
1542
1554
|
| `collapseMode` | `"hamburger" \| "hide"` | `"hamburger"` | Collapse behavior. See top of section. |
|
|
1543
|
-
| `keepLocaleOnCollapse` | `boolean` | `
|
|
1555
|
+
| `keepLocaleOnCollapse` | `boolean` | `true` | Keep the locale switcher inline when collapsed (both modes). See below. |
|
|
1544
1556
|
| `fixed` | `boolean` | `false` | Fixed positioning at top. |
|
|
1545
1557
|
| `isCollapsed` | `boolean` | — | Bindable: collapsed state. |
|
|
1546
1558
|
| `isMenuOpen` | `boolean` | — | Bindable: hamburger menu open. |
|
|
@@ -1575,11 +1587,24 @@ Common pattern for app interfaces: the leading hamburger opens a side drawer for
|
|
|
1575
1587
|
|
|
1576
1588
|
See [Header/README.md](../../src/lib/components/Header/README.md) for the breakdown of which markup branch handles each requirement of this pattern.
|
|
1577
1589
|
|
|
1590
|
+
### The locale switcher never folds into the hamburger
|
|
1591
|
+
|
|
1592
|
+
By default (`keepLocaleOnCollapse`) the locale switcher stays **inline** when the header collapses, in both collapse modes — it sits just before the actions/avatar/hamburger instead of becoming the last section of the dropdown.
|
|
1593
|
+
|
|
1594
|
+
This is the one control `Header` deliberately treats differently from nav items. The user who most needs the language switch is the one who landed in a language they cannot read, and for them a visible `EN ▾` trigger is self-describing while a hamburger entry is not: the trigger is an unlabeled icon, the section heading says "Language" in a language they do not speak, and with more than a few nav items it sits below the fold of a menu they must scroll.
|
|
1595
|
+
|
|
1596
|
+
Consequences worth knowing:
|
|
1597
|
+
|
|
1598
|
+
- The dropdown never carries a duplicate locale section while the inline trigger is visible.
|
|
1599
|
+
- If the locales were the only thing the trailing dropdown would have held, no hamburger renders at all.
|
|
1600
|
+
- `HeaderLocaleItem.shortLabel` keeps the inline trigger narrow on phones (`"Slovenčina"` → `"SK"`) without shortening the dropdown entries.
|
|
1601
|
+
- Set `keepLocaleOnCollapse={false}` when the end area is already crowded with actions, or when the app's own drawer owns the language switch.
|
|
1602
|
+
|
|
1578
1603
|
### CSS Tokens
|
|
1579
1604
|
|
|
1580
1605
|
Prefix: `--stuic-header-*`
|
|
1581
1606
|
|
|
1582
|
-
`padding-x`, `padding-y`, `gap`, `min-height`, `nav-gap`, `content-max-width`, `project-name-font-weight`, `z-index`, `bg`, `text`, `border-width`, `border-color`, `nav-item-bg-active`, `nav-item-text-active`
|
|
1607
|
+
`padding-x`, `padding-y`, `gap`, `end-gap-collapsed`, `min-height`, `nav-gap`, `content-max-width`, `project-name-font-weight`, `z-index`, `bg`, `text`, `border-width`, `border-color`, `nav-item-bg-active`, `nav-item-text-active`
|
|
1583
1608
|
|
|
1584
1609
|
---
|
|
1585
1610
|
|