noph-ui 0.44.0 → 0.45.1

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 (136) hide show
  1. package/README.md +10 -1
  2. package/dist/animation.d.ts +2 -0
  3. package/dist/animation.js +33 -0
  4. package/dist/app-bar/AppBar.svelte +230 -0
  5. package/dist/app-bar/AppBar.svelte.d.ts +4 -0
  6. package/dist/app-bar/index.d.ts +1 -0
  7. package/dist/app-bar/index.js +1 -0
  8. package/dist/app-bar/types.d.ts +13 -0
  9. package/dist/app-bar/types.js +1 -0
  10. package/dist/autocomplete/AutoComplete.svelte +10 -11
  11. package/dist/autocomplete/AutoComplete.svelte.d.ts +1 -1
  12. package/dist/autocomplete/types.d.ts +1 -1
  13. package/dist/badge/Badge.svelte +6 -1
  14. package/dist/badge/types.d.ts +0 -1
  15. package/dist/button/Button.svelte +5 -15
  16. package/dist/button/IconButton.svelte +5 -15
  17. package/dist/button/SegmentedButton.svelte +15 -1
  18. package/dist/button/SplitButton.svelte +75 -0
  19. package/dist/button/SplitButton.svelte.d.ts +4 -0
  20. package/dist/button/index.d.ts +1 -0
  21. package/dist/button/index.js +1 -0
  22. package/dist/button/types.d.ts +17 -4
  23. package/dist/card/types.d.ts +1 -1
  24. package/dist/checkbox/Checkbox.svelte +47 -58
  25. package/dist/chip/AssistChip.svelte +5 -5
  26. package/dist/chip/ChipSet.svelte +3 -11
  27. package/dist/chip/FilterChip.svelte +33 -27
  28. package/dist/chip/InputChip.svelte +13 -2
  29. package/dist/chip/SuggestionChip.svelte +29 -0
  30. package/dist/chip/SuggestionChip.svelte.d.ts +4 -0
  31. package/dist/chip/index.d.ts +1 -0
  32. package/dist/chip/index.js +1 -0
  33. package/dist/chip/types.d.ts +16 -9
  34. package/dist/date-picker/Calendar.svelte +1 -1
  35. package/dist/date-picker/DatePickerDialog.svelte +23 -11
  36. package/dist/date-picker/DatePickerDialog.svelte.d.ts +4 -1
  37. package/dist/date-picker/DateRangePicker.svelte +40 -10
  38. package/dist/date-picker/DateRangePicker.svelte.d.ts +4 -1
  39. package/dist/date-picker/DockedDatePicker.svelte +13 -10
  40. package/dist/date-picker/DockedDatePicker.svelte.d.ts +4 -1
  41. package/dist/date-picker/DockedDateTimePicker.svelte +13 -7
  42. package/dist/date-picker/DockedDateTimePicker.svelte.d.ts +4 -1
  43. package/dist/date-picker/SelectionList.svelte +6 -32
  44. package/dist/date-picker/SelectionList.svelte.d.ts +2 -12
  45. package/dist/date-picker/TimeColumn.svelte +4 -29
  46. package/dist/date-picker/exitVisibility.svelte.d.ts +5 -0
  47. package/dist/date-picker/exitVisibility.svelte.js +23 -0
  48. package/dist/date-picker/optionList.d.ts +4 -0
  49. package/dist/date-picker/optionList.js +31 -0
  50. package/dist/date-picker/types.d.ts +11 -4
  51. package/dist/dialog/Dialog.svelte +55 -68
  52. package/dist/dialog/Dialog.svelte.d.ts +3 -3
  53. package/dist/dialog/types.d.ts +4 -3
  54. package/dist/fab/ExtendedFab.svelte +255 -0
  55. package/dist/fab/ExtendedFab.svelte.d.ts +5 -0
  56. package/dist/fab/Fab.svelte +211 -0
  57. package/dist/fab/Fab.svelte.d.ts +5 -0
  58. package/dist/fab/FabMenu.svelte +207 -0
  59. package/dist/fab/FabMenu.svelte.d.ts +7 -0
  60. package/dist/fab/index.d.ts +3 -0
  61. package/dist/fab/index.js +3 -0
  62. package/dist/fab/types.d.ts +34 -0
  63. package/dist/fab/types.js +1 -0
  64. package/dist/icons/ArrowBackIcon.svelte +13 -0
  65. package/dist/icons/ArrowBackIcon.svelte.d.ts +7 -0
  66. package/dist/icons/ChevronDownIcon.svelte +3 -0
  67. package/dist/icons/ChevronDownIcon.svelte.d.ts +26 -0
  68. package/dist/icons/SearchIcon.svelte +15 -0
  69. package/dist/icons/SearchIcon.svelte.d.ts +7 -0
  70. package/dist/icons/index.d.ts +3 -0
  71. package/dist/icons/index.js +3 -0
  72. package/dist/index.d.ts +6 -0
  73. package/dist/index.js +6 -0
  74. package/dist/internal/focus-ring.css +12 -0
  75. package/dist/loading-indicator/shapes.js +0 -7
  76. package/dist/menu/Menu.svelte +12 -7
  77. package/dist/menu/Menu.svelte.d.ts +2 -2
  78. package/dist/menu/types.d.ts +0 -2
  79. package/dist/navigation-bar/NavigationBar.svelte +59 -0
  80. package/dist/navigation-bar/NavigationBar.svelte.d.ts +4 -0
  81. package/dist/navigation-bar/NavigationBarItem.svelte +239 -0
  82. package/dist/navigation-bar/NavigationBarItem.svelte.d.ts +5 -0
  83. package/dist/navigation-bar/index.d.ts +2 -0
  84. package/dist/navigation-bar/index.js +2 -0
  85. package/dist/navigation-bar/types.d.ts +19 -0
  86. package/dist/navigation-bar/types.js +1 -0
  87. package/dist/navigation-drawer/NavigationDrawer.svelte +107 -76
  88. package/dist/navigation-drawer/NavigationDrawer.svelte.d.ts +4 -1
  89. package/dist/navigation-drawer/NavigationDrawerItem.svelte +10 -3
  90. package/dist/navigation-drawer/types.d.ts +3 -1
  91. package/dist/navigation-rail/NavigationRailItem.svelte +16 -0
  92. package/dist/navigation-rail/types.d.ts +3 -0
  93. package/dist/popover.svelte.d.ts +5 -0
  94. package/dist/popover.svelte.js +22 -0
  95. package/dist/press.svelte.d.ts +6 -0
  96. package/dist/press.svelte.js +32 -0
  97. package/dist/radio/Radio.svelte +26 -7
  98. package/dist/radio/types.d.ts +3 -0
  99. package/dist/search/Search.svelte +391 -0
  100. package/dist/search/Search.svelte.d.ts +5 -0
  101. package/dist/search/index.d.ts +1 -0
  102. package/dist/search/index.js +1 -0
  103. package/dist/search/types.d.ts +19 -0
  104. package/dist/search/types.js +1 -0
  105. package/dist/select/Select.svelte +48 -87
  106. package/dist/sheet/Sheet.svelte +235 -0
  107. package/dist/sheet/Sheet.svelte.d.ts +7 -0
  108. package/dist/sheet/index.d.ts +1 -0
  109. package/dist/sheet/index.js +1 -0
  110. package/dist/sheet/types.d.ts +11 -0
  111. package/dist/sheet/types.js +1 -0
  112. package/dist/slider/Slider.svelte +118 -30
  113. package/dist/slider/types.d.ts +3 -0
  114. package/dist/snackbar/Snackbar.svelte +32 -35
  115. package/dist/snackbar/Snackbar.svelte.d.ts +2 -2
  116. package/dist/snackbar/types.d.ts +3 -2
  117. package/dist/switch/Switch.svelte +51 -20
  118. package/dist/switch/types.d.ts +3 -0
  119. package/dist/tabs/Tab.svelte +4 -12
  120. package/dist/tabs/Tabs.svelte +5 -5
  121. package/dist/text-field/TextField.svelte +2 -4
  122. package/dist/toolbar/Toolbar.svelte +121 -0
  123. package/dist/toolbar/Toolbar.svelte.d.ts +4 -0
  124. package/dist/toolbar/index.d.ts +1 -0
  125. package/dist/toolbar/index.js +1 -0
  126. package/dist/toolbar/types.d.ts +8 -0
  127. package/dist/toolbar/types.js +1 -0
  128. package/dist/tooltip/RichTooltip.svelte +18 -12
  129. package/dist/tooltip/RichTooltip.svelte.d.ts +2 -2
  130. package/dist/tooltip/Tooltip.svelte +22 -11
  131. package/dist/tooltip/Tooltip.svelte.d.ts +4 -1
  132. package/dist/types.d.ts +6 -0
  133. package/dist/types.js +6 -0
  134. package/package.json +13 -13
  135. package/dist/date-picker/enterAnimation.d.ts +0 -1
  136. package/dist/date-picker/enterAnimation.js +0 -10
@@ -0,0 +1,239 @@
1
+ <script lang="ts">
2
+ import '../internal/focus-ring.css'
3
+ import Badge from '../badge/Badge.svelte'
4
+ import Ripple from '../ripple/Ripple.svelte'
5
+ import type { HTMLButtonAttributes } from 'svelte/elements'
6
+ import type { NavigationBarItemProps } from './types.js'
7
+
8
+ let {
9
+ selected = false,
10
+ icon,
11
+ label,
12
+ badge = false,
13
+ badgeLabel,
14
+ badgeAriaLabel,
15
+ labelBehavior = 'always',
16
+ orientation = 'vertical',
17
+ element = $bindable(),
18
+ ...attributes
19
+ }: NavigationBarItemProps = $props()
20
+
21
+ let touchEl: HTMLSpanElement | undefined = $state()
22
+
23
+ const isLink = $derived(attributes.href != null)
24
+ const classes = $derived([
25
+ 'np-navigation-bar-item',
26
+ selected && 'np-navigation-bar-item-selected',
27
+ labelBehavior === 'selected' && 'np-navigation-bar-item-label-selected',
28
+ orientation === 'horizontal' && 'np-navigation-bar-item-horizontal',
29
+ attributes.class,
30
+ ])
31
+ </script>
32
+
33
+ {#snippet content()}
34
+ {#if orientation === 'horizontal'}
35
+ <Ripple forElement={touchEl} />
36
+ {/if}
37
+ <span class="np-navigation-bar-item-icon">
38
+ {#if orientation === 'vertical'}
39
+ <Ripple forElement={touchEl} />
40
+ {/if}
41
+ {@render icon()}
42
+ {#if badge}
43
+ <span class="np-navigation-bar-item-badge">
44
+ <Badge label={badgeLabel} aria-label={badgeAriaLabel} />
45
+ </span>
46
+ {/if}
47
+ </span>
48
+ <span class="np-navigation-bar-item-label">{label}</span>
49
+ <span class="np-touch" bind:this={touchEl}></span>
50
+ {/snippet}
51
+
52
+ {#if isLink}
53
+ <a
54
+ {...attributes}
55
+ bind:this={element}
56
+ href={attributes.href}
57
+ class={classes}
58
+ aria-current={selected ? 'page' : undefined}
59
+ tabindex={selected ? 0 : -1}
60
+ >
61
+ {@render content()}
62
+ </a>
63
+ {:else}
64
+ <button
65
+ {...attributes as HTMLButtonAttributes}
66
+ bind:this={element}
67
+ class={classes}
68
+ aria-current={selected ? 'page' : undefined}
69
+ tabindex={selected ? 0 : -1}
70
+ >
71
+ {@render content()}
72
+ </button>
73
+ {/if}
74
+
75
+ <style>
76
+ .np-navigation-bar-item {
77
+ position: relative;
78
+ flex: 1;
79
+ min-width: 0;
80
+ cursor: pointer;
81
+ font: inherit;
82
+ border-width: 0;
83
+ background: none;
84
+ display: flex;
85
+ flex-direction: column;
86
+ align-items: center;
87
+ justify-content: flex-start;
88
+ gap: 0.25rem;
89
+ color: var(--np-color-on-surface);
90
+ text-decoration: none;
91
+ -webkit-tap-highlight-color: transparent;
92
+ }
93
+
94
+ .np-navigation-bar-item-icon {
95
+ position: relative;
96
+ display: flex;
97
+ align-items: center;
98
+ justify-content: center;
99
+ width: 3.5rem;
100
+ height: 2rem;
101
+ border-radius: var(--np-shape-corner-full);
102
+ color: var(--np-color-on-surface-variant);
103
+ z-index: 1;
104
+ }
105
+
106
+ .np-navigation-bar-item-icon::before {
107
+ content: '';
108
+ position: absolute;
109
+ inset: 0;
110
+ opacity: 0;
111
+ transform: scaleX(0.32);
112
+ background-color: var(
113
+ --np-navigation-bar-item-active-indicator-color,
114
+ var(--np-color-secondary-container)
115
+ );
116
+ border-radius: inherit;
117
+ z-index: -1;
118
+ }
119
+ .np-navigation-bar-item-selected .np-navigation-bar-item-icon::before {
120
+ opacity: 1;
121
+ transform: scaleX(1);
122
+ }
123
+
124
+ .np-navigation-bar-item-selected .np-navigation-bar-item-icon {
125
+ color: var(--np-color-on-secondary-container);
126
+ --np-icon-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
127
+ }
128
+
129
+ .np-navigation-bar-item-label {
130
+ font-size: 0.75rem;
131
+ line-height: 1rem;
132
+ letter-spacing: 0.031rem;
133
+ font-weight: var(--np-navigation-bar-item-font-weight, 500);
134
+ color: var(--np-color-on-surface-variant);
135
+ max-width: 100%;
136
+ overflow: hidden;
137
+ text-overflow: ellipsis;
138
+ white-space: nowrap;
139
+ }
140
+ .np-navigation-bar-item-selected .np-navigation-bar-item-label {
141
+ font-weight: var(--np-navigation-bar-item-selected-font-weight, 500);
142
+ color: var(--np-color-on-surface);
143
+ }
144
+
145
+ .np-navigation-bar-item-label-selected .np-navigation-bar-item-label {
146
+ visibility: hidden;
147
+ }
148
+ .np-navigation-bar-item-label-selected.np-navigation-bar-item-selected
149
+ .np-navigation-bar-item-label {
150
+ visibility: visible;
151
+ }
152
+
153
+ .np-navigation-bar-item-horizontal {
154
+ flex-direction: row;
155
+ justify-content: center;
156
+ gap: 0.25rem;
157
+ padding: 0.5rem 1rem;
158
+ border-radius: var(--np-shape-corner-full);
159
+ align-self: center;
160
+ z-index: 0;
161
+ }
162
+ .np-navigation-bar-item-horizontal::before {
163
+ content: '';
164
+ position: absolute;
165
+ inset: 0;
166
+ opacity: 0;
167
+ transform: scaleX(0.32);
168
+ background-color: var(
169
+ --np-navigation-bar-item-active-indicator-color,
170
+ var(--np-color-secondary-container)
171
+ );
172
+ border-radius: inherit;
173
+ z-index: -1;
174
+ }
175
+ .np-navigation-bar-item-horizontal.np-navigation-bar-item-selected::before {
176
+ opacity: 1;
177
+ transform: scaleX(1);
178
+ }
179
+ .np-navigation-bar-item-horizontal .np-navigation-bar-item-icon {
180
+ width: 1.5rem;
181
+ height: 1.5rem;
182
+ }
183
+ .np-navigation-bar-item-horizontal .np-navigation-bar-item-icon::before {
184
+ content: none;
185
+ }
186
+ .np-navigation-bar-item-horizontal .np-navigation-bar-item-badge {
187
+ --np-badge-top: -0.25rem;
188
+ --np-badge-end: -0.25rem;
189
+ }
190
+ .np-navigation-bar-item-horizontal .np-touch {
191
+ inset: 0;
192
+ width: 100%;
193
+ height: 100%;
194
+ }
195
+ @media (prefers-reduced-motion: no-preference) {
196
+ .np-navigation-bar-item-horizontal::before {
197
+ transition:
198
+ transform var(--np-motion-expressive-default-spatial),
199
+ opacity var(--np-motion-expressive-fast-effects);
200
+ }
201
+ }
202
+
203
+ .np-navigation-bar-item-badge {
204
+ --np-badge-top: 0;
205
+ --np-badge-end: 1rem;
206
+ position: absolute;
207
+ inset: 0;
208
+ pointer-events: none;
209
+ }
210
+
211
+ .np-touch {
212
+ position: absolute;
213
+ inset-block-start: 0;
214
+ height: 3.5rem;
215
+ width: 4rem;
216
+ }
217
+
218
+ .np-navigation-bar-item:focus-visible {
219
+ outline-style: solid;
220
+ outline-color: var(--np-color-secondary);
221
+ outline-width: 3px;
222
+ outline-offset: 2px;
223
+ border-radius: 1rem;
224
+ }
225
+
226
+ @media (prefers-reduced-motion: no-preference) {
227
+ .np-navigation-bar-item-icon::before {
228
+ transition:
229
+ transform var(--np-motion-expressive-default-spatial),
230
+ opacity var(--np-motion-expressive-fast-effects);
231
+ }
232
+ .np-navigation-bar-item-label {
233
+ transition: color var(--np-motion-expressive-default-effects);
234
+ }
235
+ .np-navigation-bar-item:focus-visible {
236
+ animation: focusAnimation var(--np-motion-expressive-slow-effects) forwards;
237
+ }
238
+ }
239
+ </style>
@@ -0,0 +1,5 @@
1
+ import '../internal/focus-ring.css';
2
+ import type { NavigationBarItemProps } from './types.ts';
3
+ declare const NavigationBarItem: import("svelte").Component<NavigationBarItemProps, {}, "element">;
4
+ type NavigationBarItem = ReturnType<typeof NavigationBarItem>;
5
+ export default NavigationBarItem;
@@ -0,0 +1,2 @@
1
+ export { default as NavigationBar } from './NavigationBar.svelte';
2
+ export { default as NavigationBarItem } from './NavigationBarItem.svelte';
@@ -0,0 +1,2 @@
1
+ export { default as NavigationBar } from './NavigationBar.svelte';
2
+ export { default as NavigationBarItem } from './NavigationBarItem.svelte';
@@ -0,0 +1,19 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAnchorAttributes, HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements';
3
+ export interface NavigationBarProps extends HTMLAttributes<HTMLElement> {
4
+ arrangement?: 'equal-weight' | 'centered';
5
+ element?: HTMLElement;
6
+ }
7
+ export type NavigationBarItemElement = HTMLButtonElement | HTMLAnchorElement;
8
+ export interface NavigationBarItemProps extends HTMLAttributes<NavigationBarItemElement>, Omit<HTMLButtonAttributes, keyof HTMLAttributes<HTMLButtonElement> | 'type'>, Omit<HTMLAnchorAttributes, keyof HTMLAttributes<HTMLAnchorElement> | 'type'> {
9
+ icon: Snippet;
10
+ label: string;
11
+ selected?: boolean;
12
+ badge?: boolean;
13
+ badgeLabel?: string | number;
14
+ badgeAriaLabel?: string;
15
+ labelBehavior?: 'always' | 'selected';
16
+ orientation?: 'vertical' | 'horizontal';
17
+ type?: 'submit' | 'reset' | 'button' | (string & {}) | null;
18
+ element?: NavigationBarItemElement;
19
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,12 +1,14 @@
1
1
  <script lang="ts">
2
2
  import { arrowKeyNav, rovingTabindex } from '../keyboard-nav.js'
3
+ import { syncOpenEffect } from '../popover.svelte.js'
3
4
  import type { NavigationDrawerProps } from './types.js'
5
+
4
6
  let {
5
7
  modal = false,
6
8
  backdrop = false,
9
+ open = $bindable(false),
7
10
  element = $bindable(),
8
11
  direction = 'ltr',
9
- popover,
10
12
  children,
11
13
  onkeydown: userKeydown,
12
14
  ontoggle,
@@ -21,83 +23,103 @@
21
23
  if (!event.defaultPrevented) arrowHandler(event)
22
24
  }
23
25
 
24
- let previouslyFocused: HTMLElement | undefined
25
- let inerted: HTMLElement[] = []
26
+ const asDialog = () => (element instanceof HTMLDialogElement ? element : undefined)
26
27
 
27
- const trapFocus = () => {
28
- if (!element) return
29
- previouslyFocused = document.activeElement as HTMLElement | undefined
30
- for (let node: HTMLElement | null = element; node && node !== document.body;) {
31
- const parent: HTMLElement | null = node.parentElement
32
- if (!parent) break
33
- for (const sibling of parent.children) {
34
- if (sibling === node || !(sibling instanceof HTMLElement) || sibling.inert) continue
35
- sibling.inert = true
36
- inerted.push(sibling)
37
- }
38
- node = parent
39
- }
40
- element.querySelector<HTMLElement>('.np-navigation-drawer-item')?.focus()
28
+ export const show = () => {
29
+ const dialog = asDialog()
30
+ if (dialog && !dialog.open) dialog.showModal()
41
31
  }
42
32
 
43
- const releaseFocus = () => {
44
- for (const node of inerted) node.inert = false
45
- inerted = []
46
- previouslyFocused?.focus?.()
47
- previouslyFocused = undefined
33
+ export const close = () => {
34
+ asDialog()?.close()
48
35
  }
36
+
37
+ syncOpenEffect(
38
+ () => (modal ? element : undefined),
39
+ () => open,
40
+ show,
41
+ close,
42
+ )
43
+
44
+ const start = $derived(direction === 'ltr' ? 'translateX(-100%)' : 'translateX(100%)')
49
45
  </script>
50
46
 
51
- <nav
52
- {...attributes}
53
- {@attach attach}
54
- bind:this={element}
55
- popover={modal ? popover || 'auto' : undefined}
56
- style="--np-navigation-drawer-start: {direction === 'ltr'
57
- ? 'translateX(-100%)'
58
- : 'translateX(100%)'}"
59
- class={[
60
- 'np-navigation-drawer-container',
61
- modal && 'np-navigation-drawer-container-modal',
62
- backdrop && 'np-navigation-drawer-backdrop',
63
- attributes.class,
64
- ]}
65
- onkeydown={handleKeydown}
66
- ontoggle={(event) => {
67
- if (modal) {
68
- if (event.newState === 'open') trapFocus()
69
- else releaseFocus()
70
- }
71
- ontoggle?.(event)
72
- }}
73
- >
74
- {#if backdrop}
75
- <div
76
- role="none"
77
- class="np-backdrop"
78
- onclick={() => {
79
- element?.hidePopover()
80
- }}
81
- ></div>
82
- {/if}
83
- <div class={['np-navigation-wrapper', modal && 'np-navigation-drawer-shade']}>
84
- <div class="np-navigation-drawer">
85
- {#if children}
86
- {@render children()}
87
- {/if}
47
+ {#if modal}
48
+ <dialog
49
+ {...attributes}
50
+ {@attach attach}
51
+ bind:this={element}
52
+ tabindex="-1"
53
+ aria-label={null}
54
+ aria-labelledby={null}
55
+ closedby="any"
56
+ style="--np-navigation-drawer-start: {start}; {attributes.style ?? ''}"
57
+ class={[
58
+ 'np-navigation-drawer-container',
59
+ 'np-navigation-drawer-container-modal',
60
+ backdrop && 'np-navigation-drawer-backdrop',
61
+ attributes.class,
62
+ ]}
63
+ onkeydown={handleKeydown}
64
+ ontoggle={(event) => {
65
+ open = event.newState === 'open'
66
+ if (event.newState === 'open') element?.focus()
67
+ ontoggle?.(event)
68
+ }}
69
+ onclick={(event) => {
70
+ attributes.onclick?.(event)
71
+ if (event.target === element) close()
72
+ }}
73
+ >
74
+ <nav
75
+ aria-label={attributes['aria-label']}
76
+ aria-labelledby={attributes['aria-labelledby']}
77
+ class="np-navigation-wrapper np-navigation-drawer-shade"
78
+ >
79
+ <div class="np-navigation-drawer">
80
+ {@render children?.()}
81
+ </div>
82
+ </nav>
83
+ </dialog>
84
+ {:else}
85
+ <nav
86
+ {...attributes}
87
+ {@attach attach}
88
+ bind:this={element}
89
+ class={['np-navigation-drawer-container', attributes.class]}
90
+ onkeydown={handleKeydown}
91
+ >
92
+ <div class="np-navigation-wrapper">
93
+ <div class="np-navigation-drawer">
94
+ {@render children?.()}
95
+ </div>
88
96
  </div>
89
- </div>
90
- </nav>
97
+ </nav>
98
+ {/if}
91
99
 
92
100
  <style>
93
101
  .np-navigation-drawer-container {
94
102
  color: var(--np-color-on-surface-variant);
95
103
  width: calc(var(--np-navigation-drawer-width, 22.5rem) + 3px);
96
104
  border: 0;
105
+ outline: none;
97
106
  margin: 0;
98
107
  padding: 0;
99
108
  background-color: transparent;
109
+ max-width: none;
110
+ max-height: none;
100
111
  }
112
+
113
+ .np-navigation-drawer-container-modal {
114
+ position: fixed;
115
+ inset-block: 0;
116
+ height: 100dvh;
117
+ overflow: visible;
118
+ }
119
+ .np-navigation-drawer-container-modal:not([open]) {
120
+ display: none;
121
+ }
122
+
101
123
  .np-navigation-wrapper {
102
124
  background-color: var(--np-navigation-drawer-background, var(--np-color-surface-container-low));
103
125
  border-start-end-radius: var(--np-shape-corner-large);
@@ -106,33 +128,32 @@
106
128
  height: var(--np-navigation-drawer-height, 100dvh);
107
129
  overflow-y: auto;
108
130
  scrollbar-width: thin;
131
+ display: block;
109
132
  }
110
133
 
111
- .np-navigation-drawer-container[popover] .np-navigation-wrapper {
134
+ .np-navigation-drawer-container-modal .np-navigation-wrapper {
112
135
  transform: var(--np-navigation-drawer-start, translateX(-100%));
113
136
  }
114
-
115
- .np-navigation-drawer-container:popover-open .np-navigation-wrapper {
137
+ .np-navigation-drawer-container-modal[open] .np-navigation-wrapper {
116
138
  transform: translateX(0);
117
139
  }
118
140
 
119
141
  @media (prefers-reduced-motion: no-preference) {
120
- .np-navigation-drawer-container[popover] .np-navigation-wrapper {
142
+ .np-navigation-drawer-container-modal .np-navigation-wrapper {
121
143
  transition: transform var(--np-motion-standard-slow-spatial);
122
144
  }
123
-
124
- .np-navigation-drawer-container[popover] {
145
+ .np-navigation-drawer-container-modal {
125
146
  transition:
126
147
  overlay var(--np-motion-standard-slow-spatial) allow-discrete,
127
148
  display var(--np-motion-standard-slow-spatial) allow-discrete;
128
149
  }
129
-
130
- .np-navigation-drawer-container:popover-open .np-navigation-wrapper {
150
+ .np-navigation-drawer-container-modal[open] .np-navigation-wrapper {
131
151
  @starting-style {
132
152
  transform: var(--np-navigation-drawer-start, translateX(-100%));
133
153
  }
134
154
  }
135
155
  }
156
+
136
157
  .np-navigation-drawer {
137
158
  display: flex;
138
159
  padding: var(--np-navigation-drawer-padding, 1.25rem 0.75rem);
@@ -142,17 +163,27 @@
142
163
  box-shadow: var(--np-elevation-1);
143
164
  }
144
165
 
145
- .np-navigation-drawer-backdrop[popover] .np-backdrop {
146
- inset: 0;
147
- position: fixed;
166
+ .np-navigation-drawer-container-modal::backdrop {
167
+ background-color: transparent;
168
+ }
169
+ .np-navigation-drawer-backdrop::backdrop {
148
170
  background-color: var(--np-color-scrim);
149
171
  opacity: 0;
150
- transition: opacity var(--np-motion-expressive-slow-effects);
151
172
  }
152
- .np-navigation-drawer-backdrop[popover]:popover-open .np-backdrop {
173
+ .np-navigation-drawer-backdrop[open]::backdrop {
153
174
  opacity: 0.32;
154
- @starting-style {
155
- opacity: 0;
175
+ }
176
+ @media (prefers-reduced-motion: no-preference) {
177
+ .np-navigation-drawer-backdrop::backdrop {
178
+ transition:
179
+ opacity var(--np-motion-expressive-slow-effects),
180
+ overlay var(--np-motion-expressive-slow-effects) allow-discrete,
181
+ display var(--np-motion-expressive-slow-effects) allow-discrete;
182
+ }
183
+ .np-navigation-drawer-backdrop[open]::backdrop {
184
+ @starting-style {
185
+ opacity: 0;
186
+ }
156
187
  }
157
188
  }
158
189
  </style>
@@ -1,4 +1,7 @@
1
1
  import type { NavigationDrawerProps } from './types.ts';
2
- declare const NavigationDrawer: import("svelte").Component<NavigationDrawerProps, {}, "element">;
2
+ declare const NavigationDrawer: import("svelte").Component<NavigationDrawerProps, {
3
+ show: () => void;
4
+ close: () => void;
5
+ }, "element" | "open">;
3
6
  type NavigationDrawer = ReturnType<typeof NavigationDrawer>;
4
7
  export default NavigationDrawer;
@@ -7,7 +7,8 @@
7
7
  let {
8
8
  selected = false,
9
9
  label,
10
- badgeLabelText,
10
+ badgeLabel,
11
+ badgeAriaLabel,
11
12
  icon,
12
13
  element = $bindable(),
13
14
  ...attributes
@@ -24,8 +25,14 @@
24
25
  {/if}
25
26
  </div>
26
27
  <div class="np-navigation-drawer-item-label">{label}</div>
27
- {#if badgeLabelText}
28
- <div class="np-navigation-drawer-item-badge">{badgeLabelText}</div>
28
+ {#if badgeLabel}
29
+ <div
30
+ class="np-navigation-drawer-item-badge"
31
+ role={badgeAriaLabel ? 'status' : undefined}
32
+ aria-label={badgeAriaLabel}
33
+ >
34
+ {badgeLabel}
35
+ </div>
29
36
  {/if}
30
37
  {/snippet}
31
38
 
@@ -3,6 +3,7 @@ import type { HTMLAnchorAttributes, HTMLAttributes, HTMLButtonAttributes } from
3
3
  export interface NavigationDrawerProps extends HTMLAttributes<HTMLElement> {
4
4
  modal?: boolean;
5
5
  backdrop?: boolean;
6
+ open?: boolean;
6
7
  element?: HTMLElement;
7
8
  direction?: 'rtl' | 'ltr';
8
9
  }
@@ -11,7 +12,8 @@ export interface NavigationDrawerItemProps extends HTMLAttributes<NavigationDraw
11
12
  icon?: Snippet;
12
13
  label: string;
13
14
  selected?: boolean;
14
- badgeLabelText?: string;
15
+ badgeLabel?: string;
16
+ badgeAriaLabel?: string;
15
17
  type?: 'submit' | 'reset' | 'button' | (string & {}) | null;
16
18
  element?: NavigationDrawerItemElement;
17
19
  }
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import '../internal/focus-ring.css'
3
+ import Badge from '../badge/Badge.svelte'
3
4
  import Ripple from '../ripple/Ripple.svelte'
4
5
  import type { HTMLButtonAttributes } from 'svelte/elements'
5
6
  import type { NavigationRailItemProps } from './types.js'
@@ -8,6 +9,9 @@
8
9
  selected,
9
10
  icon,
10
11
  label,
12
+ badge = false,
13
+ badgeLabel,
14
+ badgeAriaLabel,
11
15
  element = $bindable(),
12
16
  ...attributes
13
17
  }: NavigationRailItemProps = $props()
@@ -20,6 +24,11 @@
20
24
  <div class="np-navigation-action-icon">
21
25
  <Ripple forElement={touchEl} />
22
26
  {@render icon()}
27
+ {#if badge}
28
+ <div class="np-navigation-action-badge">
29
+ <Badge label={badgeLabel} aria-label={badgeAriaLabel} />
30
+ </div>
31
+ {/if}
23
32
  </div>
24
33
  <div class="np-navigation-action-label">{label}</div>
25
34
  <span class="np-touch" bind:this={touchEl}></span>
@@ -92,6 +101,13 @@
92
101
  transition: color var(--np-motion-expressive-default-effects);
93
102
  color: var(--np-color-on-surface-variant);
94
103
  }
104
+ .np-navigation-action-badge {
105
+ --np-badge-top: 0;
106
+ --np-badge-end: 0.75rem;
107
+ position: absolute;
108
+ inset: 0;
109
+ pointer-events: none;
110
+ }
95
111
  .np-navigation-action-selected .np-navigation-action-icon {
96
112
  color: var(--np-color-on-secondary-container);
97
113
  --np-icon-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
@@ -8,6 +8,9 @@ export interface NavigationRailItemProps extends HTMLAttributes<NavigationRailIt
8
8
  icon: Snippet;
9
9
  label: string;
10
10
  selected?: boolean;
11
+ badge?: boolean;
12
+ badgeLabel?: string | number;
13
+ badgeAriaLabel?: string;
11
14
  type?: 'submit' | 'reset' | 'button' | (string & {}) | null;
12
15
  element?: NavigationRailItemElement;
13
16
  }
@@ -0,0 +1,5 @@
1
+ export declare const popoverController: (getElement: () => HTMLElement | undefined) => {
2
+ show: () => void;
3
+ close: () => void;
4
+ };
5
+ export declare const syncOpenEffect: (getElement: () => HTMLElement | undefined, getOpen: () => boolean | undefined, show: () => void, close: () => void) => void;
@@ -0,0 +1,22 @@
1
+ export const popoverController = (getElement) => ({
2
+ show: () => {
3
+ const element = getElement();
4
+ if (element && !element.matches(':popover-open'))
5
+ element.showPopover();
6
+ },
7
+ close: () => {
8
+ const element = getElement();
9
+ if (element?.matches(':popover-open'))
10
+ element.hidePopover();
11
+ },
12
+ });
13
+ export const syncOpenEffect = (getElement, getOpen, show, close) => {
14
+ $effect(() => {
15
+ if (!getElement())
16
+ return;
17
+ if (getOpen() === true)
18
+ show();
19
+ else if (getOpen() === false)
20
+ close();
21
+ });
22
+ };
@@ -0,0 +1,6 @@
1
+ export declare const PRESS_DURATION = 100;
2
+ export declare const pressMorph: () => {
3
+ readonly pressed: boolean;
4
+ press: () => void;
5
+ supersededBy: (shape: () => unknown) => void;
6
+ };