noph-ui 0.51.2 → 0.52.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/dist/animation.d.ts +26 -1
- package/dist/animation.js +50 -2
- package/dist/app-bar/AppBar.svelte +71 -7
- package/dist/app-bar/types.d.ts +6 -0
- package/dist/autocomplete/AutoComplete.svelte +6 -31
- package/dist/autocomplete/AutoComplete.svelte.d.ts +1 -1
- package/dist/badge/Badge.svelte +12 -4
- package/dist/badge/description.d.ts +4 -0
- package/dist/badge/description.js +4 -0
- package/dist/button/Button.svelte +55 -20
- package/dist/button/ButtonGroup.svelte +139 -19
- package/dist/button/ButtonGroup.svelte.d.ts +1 -1
- package/dist/button/IconButton.svelte +49 -18
- package/dist/button/SegmentedButton.svelte +44 -4
- package/dist/button/SplitButton.svelte +77 -3
- package/dist/button/SplitButton.svelte.d.ts +1 -1
- package/dist/button/buttonGroup.d.ts +0 -4
- package/dist/button/buttonGroup.js +0 -9
- package/dist/button/groupContext.d.ts +10 -0
- package/dist/button/groupContext.js +4 -0
- package/dist/button/types.d.ts +19 -0
- package/dist/checkbox/Checkbox.svelte +5 -0
- package/dist/chip/ChipSet.svelte +28 -2
- package/dist/chip/FilterChip.svelte +32 -4
- package/dist/chip/InputChip.svelte +127 -26
- package/dist/chip/SuggestionChip.svelte +6 -22
- package/dist/chip/SuggestionChip.svelte.d.ts +1 -2
- package/dist/chip/types.d.ts +8 -1
- package/dist/date-picker/Calendar.svelte +4 -2
- package/dist/date-picker/DatePickerDialog.svelte +1 -0
- package/dist/date-picker/DatePickerDialog.svelte.d.ts +1 -1
- package/dist/date-picker/DateRangePicker.svelte +1 -0
- package/dist/date-picker/DateRangePicker.svelte.d.ts +1 -1
- package/dist/date-picker/DockedDatePicker.svelte +12 -5
- package/dist/date-picker/DockedDatePicker.svelte.d.ts +1 -1
- package/dist/date-picker/DockedDateTimePicker.svelte +12 -5
- package/dist/date-picker/DockedDateTimePicker.svelte.d.ts +1 -1
- package/dist/date-picker/YearGrid.svelte +4 -2
- package/dist/dialog/Dialog.svelte +171 -33
- package/dist/dialog/Dialog.svelte.d.ts +2 -1
- package/dist/dialog/types.d.ts +6 -0
- package/dist/divider/Divider.svelte +36 -10
- package/dist/divider/types.d.ts +1 -0
- package/dist/fab/ExtendedFab.svelte +26 -60
- package/dist/fab/Fab.svelte +13 -48
- package/dist/fab/FabMenu.svelte +102 -44
- package/dist/fab/FabMenu.svelte.d.ts +2 -1
- package/dist/fab/FabMenuItem.svelte +8 -0
- package/dist/fab/FabMenuItem.svelte.d.ts +4 -0
- package/dist/fab/index.d.ts +1 -0
- package/dist/fab/index.js +1 -0
- package/dist/fab/types.d.ts +6 -4
- package/dist/icons/MenuIcon.svelte +13 -0
- package/dist/icons/MenuIcon.svelte.d.ts +7 -0
- package/dist/icons/MenuOpenIcon.svelte +15 -0
- package/dist/icons/MenuOpenIcon.svelte.d.ts +7 -0
- package/dist/icons/index.d.ts +2 -0
- package/dist/icons/index.js +2 -0
- package/dist/internal/exit.css +47 -0
- package/dist/internal/fieldTypes.d.ts +6 -0
- package/dist/internal/focus-ring.css +4 -0
- package/dist/internal/interest.css +3 -1
- package/dist/keyboard-nav.d.ts +12 -1
- package/dist/keyboard-nav.js +104 -14
- package/dist/list/Item.svelte +52 -6
- package/dist/list/List.svelte +35 -2
- package/dist/list/ListItem.svelte +16 -2
- package/dist/list/context.d.ts +6 -0
- package/dist/list/context.js +4 -0
- package/dist/list/types.d.ts +7 -0
- package/dist/media.d.ts +1 -0
- package/dist/media.js +1 -0
- package/dist/menu/Menu.svelte +70 -18
- package/dist/menu/Menu.svelte.d.ts +2 -1
- package/dist/menu/MenuItem.svelte +9 -3
- package/dist/menu/types.d.ts +7 -1
- package/dist/navigation-bar/NavigationBar.svelte +9 -3
- package/dist/navigation-bar/NavigationBarItem.svelte +29 -13
- package/dist/navigation-drawer/NavigationDrawer.svelte +96 -20
- package/dist/navigation-drawer/NavigationDrawer.svelte.d.ts +2 -1
- package/dist/navigation-drawer/NavigationDrawerItem.svelte +8 -3
- package/dist/navigation-drawer/NavigationDrawerSection.svelte +49 -0
- package/dist/navigation-drawer/NavigationDrawerSection.svelte.d.ts +4 -0
- package/dist/navigation-drawer/index.d.ts +1 -0
- package/dist/navigation-drawer/index.js +1 -0
- package/dist/navigation-drawer/types.d.ts +12 -0
- package/dist/navigation-rail/NavigationRail.svelte +367 -7
- package/dist/navigation-rail/NavigationRail.svelte.d.ts +1 -1
- package/dist/navigation-rail/NavigationRailItem.svelte +190 -53
- package/dist/navigation-rail/NavigationRailSection.svelte +57 -0
- package/dist/navigation-rail/NavigationRailSection.svelte.d.ts +4 -0
- package/dist/navigation-rail/context.d.ts +6 -0
- package/dist/navigation-rail/context.js +5 -0
- package/dist/navigation-rail/index.d.ts +1 -0
- package/dist/navigation-rail/index.js +1 -0
- package/dist/navigation-rail/morph.d.ts +14 -0
- package/dist/navigation-rail/morph.js +78 -0
- package/dist/navigation-rail/types.d.ts +21 -0
- package/dist/progress/CircularProgress.svelte +2 -1
- package/dist/progress/LinearProgress.svelte +48 -20
- package/dist/progress/types.d.ts +1 -0
- package/dist/radio/Radio.svelte +7 -1
- package/dist/search/Search.svelte +245 -58
- package/dist/search/types.d.ts +4 -0
- package/dist/select/NativeSelect.svelte +20 -12
- package/dist/select/Option.svelte +1 -2
- package/dist/select/Select.svelte +59 -140
- package/dist/select/VirtualList.svelte +16 -1
- package/dist/select/VirtualList.svelte.d.ts +6 -2
- package/dist/sheet/Sheet.svelte +223 -5
- package/dist/sheet/Sheet.svelte.d.ts +3 -1
- package/dist/sheet/types.d.ts +10 -0
- package/dist/slider/Slider.svelte +1 -27
- package/dist/snackbar/Snackbar.svelte +3 -0
- package/dist/snackbar/Snackbar.svelte.d.ts +2 -1
- package/dist/switch/Switch.svelte +51 -11
- package/dist/tabs/Tab.svelte +28 -14
- package/dist/tabs/Tabs.svelte +28 -4
- package/dist/tabs/types.d.ts +3 -0
- package/dist/text-field/TextField.svelte +49 -13
- package/dist/time-picker/DockedTimePicker.svelte +6 -1
- package/dist/time-picker/DockedTimePicker.svelte.d.ts +1 -1
- package/dist/time-picker/TimePickerDialog.svelte +1 -0
- package/dist/time-picker/TimePickerDialog.svelte.d.ts +1 -1
- package/dist/toolbar/Toolbar.svelte +21 -11
- package/dist/toolbar/types.d.ts +1 -0
- package/dist/tooltip/RichTooltip.svelte.d.ts +1 -1
- package/dist/tooltip/Tooltip.svelte +3 -1
- package/dist/tooltip/Tooltip.svelte.d.ts +1 -1
- package/package.json +1 -1
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
cursor: pointer;
|
|
88
88
|
-webkit-tap-highlight-color: transparent;
|
|
89
89
|
margin: var(--np-checkbox-margin, max(0px, (48px - 18px)/2));
|
|
90
|
+
--np-ripple-pressed-color: var(--np-color-primary);
|
|
90
91
|
}
|
|
91
92
|
:global(label) {
|
|
92
93
|
-webkit-tap-highlight-color: transparent;
|
|
@@ -188,6 +189,10 @@
|
|
|
188
189
|
.np-error .np-icon {
|
|
189
190
|
fill: var(--np-color-on-error);
|
|
190
191
|
}
|
|
192
|
+
.np-checkbox-container:not(.np-error):has(input:checked, input:indeterminate) {
|
|
193
|
+
--np-ripple-hover-color: var(--np-color-primary);
|
|
194
|
+
--np-ripple-pressed-color: var(--np-color-on-surface);
|
|
195
|
+
}
|
|
191
196
|
.np-error {
|
|
192
197
|
--np-ripple-hover-color: var(--np-color-error);
|
|
193
198
|
--np-ripple-pressed-color: var(--np-color-error);
|
package/dist/chip/ChipSet.svelte
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { arrowKeyNav, rovingTabindex } from '../keyboard-nav.js'
|
|
2
|
+
import { arrowKeyNav, focusableItems, focusedItem, rovingTabindex } from '../keyboard-nav.js'
|
|
3
3
|
import type { ChipSetProps } from './types.js'
|
|
4
4
|
|
|
5
5
|
let { children, element = $bindable(), ...attributes }: ChipSetProps = $props()
|
|
@@ -9,7 +9,33 @@
|
|
|
9
9
|
currentAttr: 'aria-current',
|
|
10
10
|
currentValue: 'true',
|
|
11
11
|
})
|
|
12
|
-
const
|
|
12
|
+
const arrows = arrowKeyNav(CHIP_SELECTOR, 'horizontal')
|
|
13
|
+
|
|
14
|
+
// In a set that wraps, ↑ and ↓ move to the chip closest above or below the focused one.
|
|
15
|
+
const toRow = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLElement }) => {
|
|
16
|
+
const chips = focusableItems(event.currentTarget, CHIP_SELECTOR)
|
|
17
|
+
const focused = focusedItem(event.currentTarget, CHIP_SELECTOR, chips)
|
|
18
|
+
if (!focused) return
|
|
19
|
+
const from = focused.getBoundingClientRect()
|
|
20
|
+
const fromX = from.left + from.width / 2
|
|
21
|
+
const below = event.key === 'ArrowDown'
|
|
22
|
+
const candidates = chips
|
|
23
|
+
.map((chip) => ({ chip, box: chip.getBoundingClientRect() }))
|
|
24
|
+
.filter(({ box }) => (below ? box.top >= from.bottom : box.bottom <= from.top))
|
|
25
|
+
if (!candidates.length) return
|
|
26
|
+
const rowTop = below
|
|
27
|
+
? Math.min(...candidates.map(({ box }) => box.top))
|
|
28
|
+
: Math.max(...candidates.map(({ box }) => box.top))
|
|
29
|
+
const row = candidates.filter(({ box }) => Math.abs(box.top - rowTop) < 1)
|
|
30
|
+
const distance = (box: DOMRect) => Math.abs(box.left + box.width / 2 - fromX)
|
|
31
|
+
row.sort((a, b) => distance(a.box) - distance(b.box))[0].chip.focus()
|
|
32
|
+
event.preventDefault()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const onkeydown = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLElement }) => {
|
|
36
|
+
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') toRow(event)
|
|
37
|
+
else arrows(event)
|
|
38
|
+
}
|
|
13
39
|
</script>
|
|
14
40
|
|
|
15
41
|
<div {...attributes} bind:this={element} class={['np-chip-set-wrapper', attributes.class]}>
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
disabled = false,
|
|
14
14
|
label = '',
|
|
15
15
|
icon,
|
|
16
|
+
trailingIcon,
|
|
16
17
|
element = $bindable(),
|
|
17
|
-
removeAriaLabel
|
|
18
|
+
removeAriaLabel,
|
|
18
19
|
onremove,
|
|
19
20
|
name,
|
|
20
21
|
value,
|
|
@@ -69,10 +70,22 @@
|
|
|
69
70
|
<CheckIcon width={18} height={18} />
|
|
70
71
|
</div>
|
|
71
72
|
<div class="np-chip-label">{label}</div>
|
|
73
|
+
{#if trailingIcon && !removable}
|
|
74
|
+
<div class="np-chip-trailing-icon" aria-hidden="true">
|
|
75
|
+
{@render trailingIcon()}
|
|
76
|
+
</div>
|
|
77
|
+
{/if}
|
|
78
|
+
<span class="np-touch"></span>
|
|
72
79
|
<input
|
|
73
80
|
type="checkbox"
|
|
74
81
|
checked={isChecked}
|
|
75
82
|
{onchange}
|
|
83
|
+
onkeydown={(event) => {
|
|
84
|
+
// Enter selects like Space, instead of submitting the form.
|
|
85
|
+
if (event.key !== 'Enter') return
|
|
86
|
+
event.preventDefault()
|
|
87
|
+
event.currentTarget.click()
|
|
88
|
+
}}
|
|
76
89
|
{value}
|
|
77
90
|
{name}
|
|
78
91
|
{disabled}
|
|
@@ -89,7 +102,7 @@
|
|
|
89
102
|
type="button"
|
|
90
103
|
size="xs"
|
|
91
104
|
--np-icon-button-icon-size="1.125rem"
|
|
92
|
-
aria-label={removeAriaLabel}
|
|
105
|
+
aria-label={removeAriaLabel ?? `Remove ${label}`}
|
|
93
106
|
onclick={onremove}
|
|
94
107
|
>
|
|
95
108
|
<CloseIcon />
|
|
@@ -99,6 +112,7 @@
|
|
|
99
112
|
|
|
100
113
|
<style>
|
|
101
114
|
.np-filter-chip {
|
|
115
|
+
box-sizing: border-box;
|
|
102
116
|
position: relative;
|
|
103
117
|
display: inline-flex;
|
|
104
118
|
align-items: center;
|
|
@@ -107,7 +121,6 @@
|
|
|
107
121
|
--np-icon-button-icon-color: var(--np-color-on-surface-variant);
|
|
108
122
|
--np-icon-size: 1.125rem;
|
|
109
123
|
--np-ripple-pressed-opacity: 0.1;
|
|
110
|
-
min-width: 0;
|
|
111
124
|
transition: box-shadow var(--np-motion-expressive-slow-effects);
|
|
112
125
|
}
|
|
113
126
|
.np-filter-chip-label input {
|
|
@@ -117,6 +130,7 @@
|
|
|
117
130
|
}
|
|
118
131
|
.np-filter-chip-label {
|
|
119
132
|
cursor: pointer;
|
|
133
|
+
position: relative;
|
|
120
134
|
display: inline-flex;
|
|
121
135
|
align-items: center;
|
|
122
136
|
height: 2rem;
|
|
@@ -126,7 +140,19 @@
|
|
|
126
140
|
gap: 0.5rem;
|
|
127
141
|
z-index: 1;
|
|
128
142
|
padding-inline: 1rem;
|
|
129
|
-
|
|
143
|
+
}
|
|
144
|
+
.np-chip-trailing-icon {
|
|
145
|
+
display: flex;
|
|
146
|
+
margin-inline-end: -0.5rem;
|
|
147
|
+
}
|
|
148
|
+
/* The chip keeps a 48px tall target. */
|
|
149
|
+
.np-touch {
|
|
150
|
+
position: absolute;
|
|
151
|
+
inset-inline: 0;
|
|
152
|
+
top: 50%;
|
|
153
|
+
translate: 0 -50%;
|
|
154
|
+
height: 3rem;
|
|
155
|
+
z-index: -1;
|
|
130
156
|
}
|
|
131
157
|
.np-chip-icon-checked {
|
|
132
158
|
display: flex;
|
|
@@ -154,8 +180,10 @@
|
|
|
154
180
|
.np-filter-chip-icon .np-filter-chip-label {
|
|
155
181
|
padding-inline-start: 0.5rem;
|
|
156
182
|
}
|
|
183
|
+
/* Removable, selecting and removing each keep a 48px target side by side. */
|
|
157
184
|
.np-filter-chip-removable {
|
|
158
185
|
padding-inline-end: 1px;
|
|
186
|
+
min-width: 5.5rem;
|
|
159
187
|
}
|
|
160
188
|
.np-filter-chip-removable .np-filter-chip-label {
|
|
161
189
|
padding-inline-end: 1px;
|
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
disabled = false,
|
|
11
11
|
label = '',
|
|
12
12
|
icon,
|
|
13
|
+
avatar,
|
|
14
|
+
variant = 'outlined',
|
|
13
15
|
element = $bindable(),
|
|
14
16
|
actionElement = $bindable(),
|
|
15
|
-
removeAriaLabel
|
|
17
|
+
removeAriaLabel,
|
|
16
18
|
onclick,
|
|
17
19
|
onremove,
|
|
18
20
|
name,
|
|
@@ -22,46 +24,88 @@
|
|
|
22
24
|
}: InputChipProps = $props()
|
|
23
25
|
|
|
24
26
|
let hasError = $derived(!!issues?.length)
|
|
27
|
+
let content = $derived(label || `${value ?? ''}`)
|
|
28
|
+
// With nothing to do but remove, the chip is one focusable element: its remove button.
|
|
29
|
+
let hasAction = $derived(!!onclick)
|
|
30
|
+
let removeElement: HTMLElement | undefined = $state()
|
|
31
|
+
|
|
32
|
+
// Backspace and Delete remove the focused chip. Focus moves on to a neighbour first, so it is
|
|
33
|
+
// not lost when the chip leaves the page.
|
|
34
|
+
const removeWithKey = (event: KeyboardEvent) => {
|
|
35
|
+
if (disabled || (event.key !== 'Backspace' && event.key !== 'Delete')) return
|
|
36
|
+
event.preventDefault()
|
|
37
|
+
const set = element?.closest('.np-chip-set')
|
|
38
|
+
if (set && element) {
|
|
39
|
+
const chips = [
|
|
40
|
+
...set.querySelectorAll<HTMLElement>('.np-input-chip:not(.np-input-chip-disabled)'),
|
|
41
|
+
]
|
|
42
|
+
const index = chips.indexOf(element)
|
|
43
|
+
const neighbour = chips[index + 1] ?? chips[index - 1]
|
|
44
|
+
neighbour?.querySelector<HTMLElement>('button')?.focus()
|
|
45
|
+
}
|
|
46
|
+
removeElement?.click()
|
|
47
|
+
}
|
|
25
48
|
</script>
|
|
26
49
|
|
|
27
50
|
<div
|
|
28
51
|
{...attributes}
|
|
29
52
|
bind:this={element}
|
|
30
53
|
aria-disabled={disabled}
|
|
54
|
+
onkeydown={(event) => {
|
|
55
|
+
attributes.onkeydown?.(event)
|
|
56
|
+
if (!event.defaultPrevented) removeWithKey(event)
|
|
57
|
+
}}
|
|
31
58
|
class={[
|
|
32
59
|
'np-input-chip',
|
|
33
|
-
|
|
60
|
+
variant === 'elevated' && 'np-input-chip-elevated',
|
|
61
|
+
hasAction && 'np-input-chip-action',
|
|
62
|
+
avatar ? 'np-input-chip-avatar' : icon ? 'np-input-chip-icon' : '',
|
|
34
63
|
disabled ? 'np-input-chip-disabled' : '',
|
|
35
64
|
selected ? 'np-input-chip-selected' : '',
|
|
36
65
|
hasError && !disabled && 'np-chip-error',
|
|
37
66
|
attributes.class,
|
|
38
67
|
]}
|
|
39
68
|
>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{
|
|
46
|
-
|
|
47
|
-
>
|
|
48
|
-
{#if icon}
|
|
49
|
-
<div class="np-chip-icon">
|
|
69
|
+
{#snippet chipContent()}
|
|
70
|
+
{#if avatar}
|
|
71
|
+
<div class="np-chip-avatar" aria-hidden="true">
|
|
72
|
+
{@render avatar()}
|
|
73
|
+
</div>
|
|
74
|
+
{:else if icon}
|
|
75
|
+
<div class="np-chip-icon" aria-hidden="true">
|
|
50
76
|
{@render icon()}
|
|
51
77
|
</div>
|
|
52
78
|
{/if}
|
|
53
|
-
<div class="np-chip-label">{
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
<div class="np-chip-label">{content}</div>
|
|
80
|
+
{/snippet}
|
|
81
|
+
{#if hasAction}
|
|
82
|
+
<button
|
|
83
|
+
bind:this={actionElement}
|
|
84
|
+
type="button"
|
|
85
|
+
class="np-input-chip-label"
|
|
86
|
+
aria-pressed={selected === undefined ? undefined : selected}
|
|
87
|
+
{disabled}
|
|
88
|
+
{onclick}
|
|
89
|
+
>
|
|
90
|
+
{@render chipContent()}
|
|
91
|
+
<span class="np-touch"></span>
|
|
92
|
+
</button>
|
|
93
|
+
{#if !disabled}
|
|
94
|
+
<Ripple forElement={actionElement} />
|
|
95
|
+
{/if}
|
|
96
|
+
{:else}
|
|
97
|
+
<div class="np-input-chip-label">
|
|
98
|
+
{@render chipContent()}
|
|
99
|
+
</div>
|
|
58
100
|
{/if}
|
|
101
|
+
<input type="hidden" {value} {name} {disabled} />
|
|
59
102
|
<IconButton
|
|
60
103
|
{disabled}
|
|
61
104
|
type="button"
|
|
62
105
|
size="xs"
|
|
63
106
|
--np-icon-button-icon-size="1.125rem"
|
|
64
|
-
aria-label={removeAriaLabel}
|
|
107
|
+
aria-label={removeAriaLabel ?? `Remove ${content}`}
|
|
108
|
+
bind:element={removeElement}
|
|
65
109
|
onclick={onremove}
|
|
66
110
|
>
|
|
67
111
|
<CloseIcon />
|
|
@@ -70,6 +114,7 @@
|
|
|
70
114
|
|
|
71
115
|
<style>
|
|
72
116
|
.np-input-chip {
|
|
117
|
+
box-sizing: border-box;
|
|
73
118
|
position: relative;
|
|
74
119
|
display: inline-flex;
|
|
75
120
|
align-items: center;
|
|
@@ -78,8 +123,12 @@
|
|
|
78
123
|
--np-icon-button-icon-color: var(--np-color-on-surface-variant);
|
|
79
124
|
--np-icon-size: 1.125rem;
|
|
80
125
|
--np-ripple-pressed-opacity: 0.1;
|
|
126
|
+
--_state-color: var(--np-color-on-surface-variant);
|
|
81
127
|
padding-inline-end: 1px;
|
|
82
|
-
|
|
128
|
+
}
|
|
129
|
+
/* With an action besides remove, both keep a 48px target side by side. */
|
|
130
|
+
.np-input-chip-action {
|
|
131
|
+
min-width: 5.5rem;
|
|
83
132
|
}
|
|
84
133
|
.np-chip-error {
|
|
85
134
|
--np-input-chip-outline-color: var(--np-color-error);
|
|
@@ -97,6 +146,7 @@
|
|
|
97
146
|
padding-block: 0;
|
|
98
147
|
padding-inline-end: 0;
|
|
99
148
|
cursor: pointer;
|
|
149
|
+
position: relative;
|
|
100
150
|
display: inline-flex;
|
|
101
151
|
align-items: center;
|
|
102
152
|
height: 2rem;
|
|
@@ -119,9 +169,36 @@
|
|
|
119
169
|
.np-input-chip-selected .np-chip-icon {
|
|
120
170
|
color: var(--np-color-primary);
|
|
121
171
|
}
|
|
172
|
+
div.np-input-chip-label {
|
|
173
|
+
cursor: default;
|
|
174
|
+
}
|
|
122
175
|
.np-input-chip-icon .np-input-chip-label {
|
|
123
176
|
padding-inline-start: 0.5rem;
|
|
124
177
|
}
|
|
178
|
+
.np-input-chip-avatar .np-input-chip-label {
|
|
179
|
+
padding-inline-start: 0.25rem;
|
|
180
|
+
}
|
|
181
|
+
/* A leading image is larger than an icon, with rounded corners. */
|
|
182
|
+
.np-chip-avatar {
|
|
183
|
+
display: flex;
|
|
184
|
+
flex: none;
|
|
185
|
+
width: 1.5rem;
|
|
186
|
+
height: 1.5rem;
|
|
187
|
+
border-radius: 0.75rem;
|
|
188
|
+
overflow: hidden;
|
|
189
|
+
}
|
|
190
|
+
.np-chip-avatar > :global(*) {
|
|
191
|
+
width: 100%;
|
|
192
|
+
height: 100%;
|
|
193
|
+
object-fit: cover;
|
|
194
|
+
}
|
|
195
|
+
.np-touch {
|
|
196
|
+
position: absolute;
|
|
197
|
+
inset-inline: 0;
|
|
198
|
+
top: 50%;
|
|
199
|
+
translate: 0 -50%;
|
|
200
|
+
height: 3rem;
|
|
201
|
+
}
|
|
125
202
|
.np-chip-label {
|
|
126
203
|
line-height: 1.25rem;
|
|
127
204
|
font-size: 0.875rem;
|
|
@@ -150,17 +227,47 @@
|
|
|
150
227
|
outline-color: transparent;
|
|
151
228
|
background-color: var(--np-color-secondary-container);
|
|
152
229
|
}
|
|
230
|
+
.np-input-chip-elevated {
|
|
231
|
+
box-shadow: var(--np-elevation-1);
|
|
232
|
+
}
|
|
233
|
+
.np-input-chip-elevated:not(.np-input-chip-selected)::before {
|
|
234
|
+
outline-color: transparent;
|
|
235
|
+
background-color: var(--np-color-surface-container-low);
|
|
236
|
+
}
|
|
237
|
+
.np-input-chip-elevated.np-input-chip-disabled {
|
|
238
|
+
box-shadow: none;
|
|
239
|
+
}
|
|
153
240
|
.np-input-chip-selected {
|
|
154
241
|
--np-icon-button-icon-color: var(--np-color-on-secondary-container);
|
|
242
|
+
--_state-color: var(--np-color-on-secondary-container);
|
|
155
243
|
}
|
|
156
244
|
.np-input-chip-selected .np-input-chip-label {
|
|
157
245
|
color: var(--np-color-on-secondary-container);
|
|
158
246
|
}
|
|
159
|
-
|
|
247
|
+
/* The whole chip shows focus, with its ring and a 10% state layer: when its body is focused, or its
|
|
248
|
+
remove button when that is all the chip has. A remove button next to a body shows its own. */
|
|
249
|
+
.np-input-chip:is(
|
|
250
|
+
:has(> .np-input-chip-label:focus-visible),
|
|
251
|
+
:not(.np-input-chip-action):has(:global(:focus-visible))
|
|
252
|
+
) {
|
|
160
253
|
outline-style: solid;
|
|
161
254
|
outline-color: var(--np-color-secondary);
|
|
162
255
|
outline-width: 3px;
|
|
163
256
|
outline-offset: 2px;
|
|
257
|
+
&::before {
|
|
258
|
+
--_state-layer: color-mix(in srgb, var(--_state-color) 10%, transparent);
|
|
259
|
+
background-image: linear-gradient(var(--_state-layer), var(--_state-layer));
|
|
260
|
+
}
|
|
261
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
262
|
+
animation: focusAnimation var(--np-motion-expressive-slow-effects) forwards;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
.np-input-chip:not(.np-input-chip-action) {
|
|
266
|
+
--np-ripple-focus-opacity: 0;
|
|
267
|
+
}
|
|
268
|
+
.np-input-chip:not(.np-input-chip-action) :global(.np-icon-button:focus-visible) {
|
|
269
|
+
outline: none;
|
|
270
|
+
animation: none;
|
|
164
271
|
}
|
|
165
272
|
|
|
166
273
|
.np-input-chip-disabled .np-input-chip-label {
|
|
@@ -176,10 +283,4 @@
|
|
|
176
283
|
outline-color: transparent;
|
|
177
284
|
background-color: color-mix(in srgb, var(--np-color-on-surface) 12%, transparent);
|
|
178
285
|
}
|
|
179
|
-
|
|
180
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
181
|
-
.np-input-chip:has(:focus-visible) {
|
|
182
|
-
animation: focusAnimation var(--np-motion-expressive-slow-effects) forwards;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
286
|
</style>
|
|
@@ -1,29 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import AssistChip from './AssistChip.svelte'
|
|
3
3
|
import type { SuggestionChipProps } from './types.js'
|
|
4
4
|
|
|
5
|
-
let {
|
|
6
|
-
variant = 'outlined',
|
|
7
|
-
disabled = false,
|
|
8
|
-
label = '',
|
|
9
|
-
element = $bindable(),
|
|
10
|
-
...attributes
|
|
11
|
-
}: SuggestionChipProps = $props()
|
|
5
|
+
let { element = $bindable(), ...attributes }: SuggestionChipProps = $props()
|
|
12
6
|
</script>
|
|
13
7
|
|
|
14
|
-
<
|
|
15
|
-
--np-
|
|
16
|
-
--np-
|
|
17
|
-
--np-outlined-button-outline-color="var(--np-suggestion-chip-outline-color, var(--np-color-outline-variant))"
|
|
18
|
-
--np-button-padding="1rem"
|
|
19
|
-
--np-button-shape="0.5rem"
|
|
20
|
-
style="letter-spacing: 0.006rem"
|
|
8
|
+
<AssistChip
|
|
9
|
+
--np-assist-chip-label-text-color="var(--np-suggestion-chip-label-text-color, var(--np-color-on-surface-variant))"
|
|
10
|
+
--np-assist-chip-outline-color="var(--np-suggestion-chip-outline-color, var(--np-color-outline-variant))"
|
|
21
11
|
bind:element
|
|
22
12
|
{...attributes}
|
|
23
|
-
|
|
24
|
-
shape="square"
|
|
25
|
-
{variant}
|
|
26
|
-
{disabled}
|
|
27
|
-
>
|
|
28
|
-
{label}
|
|
29
|
-
</Button>
|
|
13
|
+
/>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
declare const SuggestionChip: import("svelte").Component<SuggestionChipProps, {}, "element">;
|
|
1
|
+
declare const SuggestionChip: import("svelte").Component<import("./types.js").AssistChipProps, {}, "element">;
|
|
3
2
|
type SuggestionChip = ReturnType<typeof SuggestionChip>;
|
|
4
3
|
export default SuggestionChip;
|
package/dist/chip/types.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface AssistChipProps extends HTMLAttributes<ButtonElement>, Omit<HTM
|
|
|
13
13
|
element?: HTMLElement;
|
|
14
14
|
type?: 'submit' | 'reset' | 'button' | (string & {}) | null;
|
|
15
15
|
}
|
|
16
|
-
export type SuggestionChipProps =
|
|
16
|
+
export type SuggestionChipProps = AssistChipProps;
|
|
17
17
|
export interface FilterChipProps extends HTMLAttributes<HTMLDivElement> {
|
|
18
18
|
selected?: boolean;
|
|
19
19
|
removable?: boolean;
|
|
@@ -21,6 +21,9 @@ export interface FilterChipProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
21
21
|
variant?: 'outlined' | 'elevated';
|
|
22
22
|
label?: string;
|
|
23
23
|
icon?: Snippet;
|
|
24
|
+
/** Trailing icon, such as an arrow for a chip that opens a menu. Not shown when `removable`. */
|
|
25
|
+
trailingIcon?: Snippet;
|
|
26
|
+
/** Defaults to "Remove" and the chip's label. */
|
|
24
27
|
removeAriaLabel?: string;
|
|
25
28
|
element?: HTMLDivElement;
|
|
26
29
|
name?: string;
|
|
@@ -33,8 +36,12 @@ export interface FilterChipProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
33
36
|
export interface InputChipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onclick'> {
|
|
34
37
|
selected?: boolean;
|
|
35
38
|
disabled?: boolean | null;
|
|
39
|
+
variant?: 'outlined' | 'elevated';
|
|
36
40
|
label?: string;
|
|
37
41
|
icon?: Snippet;
|
|
42
|
+
/** A 24px leading image, such as a person's photo. Takes the place of `icon`. */
|
|
43
|
+
avatar?: Snippet;
|
|
44
|
+
/** Defaults to "Remove" and the chip's label. */
|
|
38
45
|
removeAriaLabel?: string;
|
|
39
46
|
element?: HTMLDivElement;
|
|
40
47
|
actionElement?: HTMLButtonElement;
|
|
@@ -110,12 +110,14 @@
|
|
|
110
110
|
const handleKeydown = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLElement }) => {
|
|
111
111
|
const grid = event.currentTarget
|
|
112
112
|
const from = focusedDate ?? tabStop
|
|
113
|
+
// Right to left, the next day sits to the left.
|
|
114
|
+
const forward = getComputedStyle(grid).direction === 'rtl' ? -1 : 1
|
|
113
115
|
switch (event.key) {
|
|
114
116
|
case 'ArrowLeft':
|
|
115
|
-
moveFocus(grid, addDays(from, -
|
|
117
|
+
moveFocus(grid, addDays(from, -forward))
|
|
116
118
|
break
|
|
117
119
|
case 'ArrowRight':
|
|
118
|
-
moveFocus(grid, addDays(from,
|
|
120
|
+
moveFocus(grid, addDays(from, forward))
|
|
119
121
|
break
|
|
120
122
|
case 'ArrowUp':
|
|
121
123
|
moveFocus(grid, addDays(from, -DAYS_IN_WEEK))
|
|
@@ -2,6 +2,6 @@ import type { DatePickerDialogProps } from './types.js';
|
|
|
2
2
|
declare const DatePickerDialog: import("svelte").Component<DatePickerDialogProps, {
|
|
3
3
|
show: () => void;
|
|
4
4
|
close: () => void;
|
|
5
|
-
}, "
|
|
5
|
+
}, "open" | "element" | "value" | "displayMonth">;
|
|
6
6
|
type DatePickerDialog = ReturnType<typeof DatePickerDialog>;
|
|
7
7
|
export default DatePickerDialog;
|
|
@@ -2,6 +2,6 @@ import type { DateRangePickerProps } from './types.js';
|
|
|
2
2
|
declare const DateRangePicker: import("svelte").Component<DateRangePickerProps, {
|
|
3
3
|
show: () => void;
|
|
4
4
|
close: () => void;
|
|
5
|
-
}, "
|
|
5
|
+
}, "open" | "element" | "value">;
|
|
6
6
|
type DateRangePicker = ReturnType<typeof DateRangePicker>;
|
|
7
7
|
export default DateRangePicker;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { exitVisibility } from './exitVisibility.svelte.js'
|
|
2
3
|
import Button from '../button/Button.svelte'
|
|
3
4
|
import IconButton from '../button/IconButton.svelte'
|
|
4
5
|
import Divider from '../divider/Divider.svelte'
|
|
@@ -82,6 +83,8 @@
|
|
|
82
83
|
const uid = $props.id()
|
|
83
84
|
|
|
84
85
|
let menuElement = $state<HTMLDivElement>()
|
|
86
|
+
// Keeps the content while the menu animates out, so it fades as a whole, not as an empty box.
|
|
87
|
+
const exit = exitVisibility()
|
|
85
88
|
let mode = $state<'days' | 'months' | 'years'>('days')
|
|
86
89
|
let listMode = $state<'months' | 'years' | undefined>(undefined)
|
|
87
90
|
let pending = $state<ISODate | undefined>(undefined)
|
|
@@ -368,19 +371,23 @@
|
|
|
368
371
|
bind:open
|
|
369
372
|
ontoggle={({ newState }) => {
|
|
370
373
|
if (newState === 'open') {
|
|
374
|
+
exit.show()
|
|
371
375
|
monthBeforeOpen = displayMonth
|
|
372
376
|
pending = current
|
|
373
377
|
focusCalendarWhenReady()
|
|
374
378
|
}
|
|
375
379
|
if (newState === 'closed') {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
+
// Reset once the menu is gone, not while it still shows.
|
|
381
|
+
exit.scheduleExit(menuElement, () => {
|
|
382
|
+
setMode('days')
|
|
383
|
+
listMode = undefined
|
|
384
|
+
focusedDay = undefined
|
|
385
|
+
displayMonth = monthBeforeOpen
|
|
386
|
+
})
|
|
380
387
|
}
|
|
381
388
|
}}
|
|
382
389
|
>
|
|
383
|
-
{#if open}
|
|
390
|
+
{#if open || exit.visible}
|
|
384
391
|
<div class="np-docked-date-picker-container" style:--np-calendar-rows={rowCount}>
|
|
385
392
|
<CalendarHeader
|
|
386
393
|
{mode}
|
|
@@ -2,6 +2,6 @@ import type { DockedDatePickerProps } from './types.js';
|
|
|
2
2
|
declare const DockedDatePicker: import("svelte").Component<DockedDatePickerProps, {
|
|
3
3
|
show: () => void;
|
|
4
4
|
close: () => void;
|
|
5
|
-
}, "
|
|
5
|
+
}, "open" | "element" | "value" | "displayMonth">;
|
|
6
6
|
type DockedDatePicker = ReturnType<typeof DockedDatePicker>;
|
|
7
7
|
export default DockedDatePicker;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { exitVisibility } from './exitVisibility.svelte.js'
|
|
2
3
|
import Button from '../button/Button.svelte'
|
|
3
4
|
import IconButton from '../button/IconButton.svelte'
|
|
4
5
|
import Divider from '../divider/Divider.svelte'
|
|
@@ -103,6 +104,8 @@
|
|
|
103
104
|
const uid = $props.id()
|
|
104
105
|
|
|
105
106
|
let menuElement = $state<HTMLDivElement>()
|
|
107
|
+
// Keeps the content while the menu animates out, so it fades as a whole, not as an empty box.
|
|
108
|
+
const exit = exitVisibility()
|
|
106
109
|
let mode = $state<'days' | 'months' | 'years'>('days')
|
|
107
110
|
let listMode = $state<'months' | 'years' | undefined>(undefined)
|
|
108
111
|
let pendingDay = $state<ISODate | undefined>(undefined)
|
|
@@ -461,20 +464,24 @@
|
|
|
461
464
|
bind:open
|
|
462
465
|
ontoggle={({ newState }) => {
|
|
463
466
|
if (newState === 'open') {
|
|
467
|
+
exit.show()
|
|
464
468
|
monthBeforeOpen = displayMonth
|
|
465
469
|
pendingDay = committed ? toISODate(committed) : undefined
|
|
466
470
|
pendingMinutes = committed ? minutesOfDay(committed) : defaultMinutes
|
|
467
471
|
focusCalendarWhenReady()
|
|
468
472
|
}
|
|
469
473
|
if (newState === 'closed') {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
+
// Reset once the menu is gone, not while it still shows.
|
|
475
|
+
exit.scheduleExit(menuElement, () => {
|
|
476
|
+
setMode('days')
|
|
477
|
+
listMode = undefined
|
|
478
|
+
focusedDay = undefined
|
|
479
|
+
displayMonth = monthBeforeOpen
|
|
480
|
+
})
|
|
474
481
|
}
|
|
475
482
|
}}
|
|
476
483
|
>
|
|
477
|
-
{#if open}
|
|
484
|
+
{#if open || exit.visible}
|
|
478
485
|
<div
|
|
479
486
|
class="np-docked-date-time-picker-container"
|
|
480
487
|
style:--np-calendar-rows={rowCount}
|
|
@@ -2,6 +2,6 @@ import type { DockedDateTimePickerProps } from './types.js';
|
|
|
2
2
|
declare const DockedDateTimePicker: import("svelte").Component<DockedDateTimePickerProps, {
|
|
3
3
|
show: () => void;
|
|
4
4
|
close: () => void;
|
|
5
|
-
}, "
|
|
5
|
+
}, "open" | "element" | "value" | "displayMonth">;
|
|
6
6
|
type DockedDateTimePicker = ReturnType<typeof DockedDateTimePicker>;
|
|
7
7
|
export default DockedDateTimePicker;
|
|
@@ -45,12 +45,14 @@
|
|
|
45
45
|
)
|
|
46
46
|
const from = Number(focused?.dataset.year)
|
|
47
47
|
if (!focused || Number.isNaN(from)) return
|
|
48
|
+
// Right to left, the next year sits to the left.
|
|
49
|
+
const forward = getComputedStyle(grid).direction === 'rtl' ? -1 : 1
|
|
48
50
|
switch (event.key) {
|
|
49
51
|
case 'ArrowLeft':
|
|
50
|
-
focusYear(grid, from -
|
|
52
|
+
focusYear(grid, from - forward)
|
|
51
53
|
break
|
|
52
54
|
case 'ArrowRight':
|
|
53
|
-
focusYear(grid, from +
|
|
55
|
+
focusYear(grid, from + forward)
|
|
54
56
|
break
|
|
55
57
|
case 'ArrowUp':
|
|
56
58
|
focusYear(grid, from - YEARS_IN_ROW)
|