noph-ui 0.32.9 → 0.33.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.
- package/dist/button/Button.svelte +3 -3
- package/dist/button/IconButton.svelte +2 -2
- package/dist/chip/FilterChip.svelte +4 -4
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/keyboard-nav.d.ts +8 -0
- package/dist/keyboard-nav.js +69 -0
- package/dist/navigation-drawer/NavigationDrawer.svelte +15 -3
- package/dist/navigation-drawer/NavigationDrawerItem.svelte +9 -2
- package/dist/navigation-rail/NavigationRail.svelte +16 -2
- package/dist/navigation-rail/NavigationRailItem.svelte +9 -2
- package/dist/snackbar/Snackbar.svelte +2 -2
- package/dist/switch/Switch.svelte +3 -3
- package/dist/tabs/Tab.svelte +30 -19
- package/dist/tabs/Tabs.svelte +13 -27
- package/dist/tabs/types.d.ts +1 -1
- package/dist/text-field/TextField.svelte +3 -5
- package/dist/themes/defaultTheme.css +21 -0
- package/dist/tooltip/Tooltip.svelte +3 -3
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -1
- package/package.json +13 -13
- package/dist/slider/Slider.svelte +0 -26
- package/dist/slider/Slider.svelte.d.ts +0 -4
- package/dist/slider/index.d.ts +0 -1
- package/dist/slider/index.js +0 -1
- package/dist/slider/types.d.ts +0 -11
- package/dist/slider/types.js +0 -1
|
@@ -168,9 +168,9 @@
|
|
|
168
168
|
text-decoration: none;
|
|
169
169
|
--np-icon-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
|
170
170
|
transition:
|
|
171
|
-
background-color
|
|
172
|
-
border-radius
|
|
173
|
-
box-shadow
|
|
171
|
+
background-color var(--np-motion-expressive-default-effects),
|
|
172
|
+
border-radius var(--np-motion-expressive-default-effects),
|
|
173
|
+
box-shadow var(--np-motion-expressive-default-effects);
|
|
174
174
|
}
|
|
175
175
|
.xs {
|
|
176
176
|
font-size: 0.875rem;
|
|
@@ -143,8 +143,8 @@
|
|
|
143
143
|
justify-content: center;
|
|
144
144
|
--np-icon-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
|
145
145
|
transition:
|
|
146
|
-
background-color
|
|
147
|
-
border-radius
|
|
146
|
+
background-color var(--np-motion-expressive-default-effects),
|
|
147
|
+
border-radius var(--np-motion-expressive-default-effects);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
:global(.np-icon-button svg) {
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
--np-icon-size: 1.125rem;
|
|
114
114
|
--np-ripple-pressed-opacity: 0.1;
|
|
115
115
|
min-width: 0;
|
|
116
|
-
transition: box-shadow
|
|
116
|
+
transition: box-shadow var(--np-motion-expressive-slow-effects);
|
|
117
117
|
}
|
|
118
118
|
.np-filter-chip-label input {
|
|
119
119
|
opacity: 0;
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
z-index: 1;
|
|
133
133
|
padding-inline: 1rem;
|
|
134
134
|
overflow: hidden;
|
|
135
|
-
transition: padding
|
|
135
|
+
transition: padding var(--np-motion-expressive-default-effects);
|
|
136
136
|
}
|
|
137
137
|
.np-chip-icon-checked {
|
|
138
138
|
display: flex;
|
|
@@ -141,8 +141,8 @@
|
|
|
141
141
|
margin-inline-end: -0.5rem;
|
|
142
142
|
overflow: hidden;
|
|
143
143
|
transition:
|
|
144
|
-
width
|
|
145
|
-
margin
|
|
144
|
+
width var(--np-motion-expressive-default-effects),
|
|
145
|
+
margin var(--np-motion-expressive-default-effects);
|
|
146
146
|
}
|
|
147
147
|
.np-filter-chip:has(input:checked) .np-chip-icon-checked {
|
|
148
148
|
width: 18px;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,6 @@ export * from './progress/index.js';
|
|
|
13
13
|
export * from './radio/index.js';
|
|
14
14
|
export * from './ripple/index.js';
|
|
15
15
|
export * from './select/index.js';
|
|
16
|
-
export * from './slider/index.js';
|
|
17
16
|
export * from './snackbar/index.js';
|
|
18
17
|
export * from './switch/index.js';
|
|
19
18
|
export * from './tabs/index.js';
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,6 @@ export * from './progress/index.js';
|
|
|
13
13
|
export * from './radio/index.js';
|
|
14
14
|
export * from './ripple/index.js';
|
|
15
15
|
export * from './select/index.js';
|
|
16
|
-
export * from './slider/index.js';
|
|
17
16
|
export * from './snackbar/index.js';
|
|
18
17
|
export * from './switch/index.js';
|
|
19
18
|
export * from './tabs/index.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Attachment } from 'svelte/attachments';
|
|
2
|
+
export declare const rovingTabindex: (itemSelector: string, options?: {
|
|
3
|
+
currentAttr?: string;
|
|
4
|
+
currentValue?: string;
|
|
5
|
+
}) => Attachment<HTMLElement>;
|
|
6
|
+
export declare const arrowKeyNav: (itemSelector: string, orientation?: "vertical" | "horizontal") => (event: KeyboardEvent & {
|
|
7
|
+
currentTarget: EventTarget & HTMLElement;
|
|
8
|
+
}) => void;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export const rovingTabindex = (itemSelector, options = {}) => {
|
|
2
|
+
const { currentAttr = 'aria-current', currentValue = 'page' } = options;
|
|
3
|
+
return (node) => {
|
|
4
|
+
const getItems = () => Array.from(node.querySelectorAll(itemSelector));
|
|
5
|
+
const setTabstop = (target) => {
|
|
6
|
+
for (const i of getItems()) {
|
|
7
|
+
const wanted = i === target ? 0 : -1;
|
|
8
|
+
if (i.tabIndex !== wanted)
|
|
9
|
+
i.tabIndex = wanted;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const sync = () => {
|
|
13
|
+
const items = getItems();
|
|
14
|
+
if (items.length === 0)
|
|
15
|
+
return;
|
|
16
|
+
const focused = node.querySelector(`${itemSelector}:focus`);
|
|
17
|
+
const current = items.find((i) => i.getAttribute(currentAttr) === currentValue);
|
|
18
|
+
setTabstop(focused ?? current ?? items[0]);
|
|
19
|
+
};
|
|
20
|
+
const onFocusIn = (event) => {
|
|
21
|
+
const target = event.target.closest(itemSelector);
|
|
22
|
+
if (!target || !node.contains(target) || target.tabIndex === 0)
|
|
23
|
+
return;
|
|
24
|
+
setTabstop(target);
|
|
25
|
+
};
|
|
26
|
+
sync();
|
|
27
|
+
node.addEventListener('focusin', onFocusIn);
|
|
28
|
+
const observer = new MutationObserver(sync);
|
|
29
|
+
observer.observe(node, {
|
|
30
|
+
attributes: true,
|
|
31
|
+
attributeFilter: [currentAttr],
|
|
32
|
+
subtree: true,
|
|
33
|
+
childList: true,
|
|
34
|
+
});
|
|
35
|
+
return () => {
|
|
36
|
+
node.removeEventListener('focusin', onFocusIn);
|
|
37
|
+
observer.disconnect();
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export const arrowKeyNav = (itemSelector, orientation = 'vertical') => (event) => {
|
|
42
|
+
const [prev, next] = orientation === 'vertical'
|
|
43
|
+
? ['ArrowUp', 'ArrowDown']
|
|
44
|
+
: ['ArrowLeft', 'ArrowRight'];
|
|
45
|
+
const { key } = event;
|
|
46
|
+
if (key !== prev && key !== next && key !== 'Home' && key !== 'End')
|
|
47
|
+
return;
|
|
48
|
+
const items = Array.from(event.currentTarget.querySelectorAll(itemSelector));
|
|
49
|
+
if (items.length === 0)
|
|
50
|
+
return;
|
|
51
|
+
const focused = event.currentTarget.querySelector(`${itemSelector}:focus`);
|
|
52
|
+
if (!focused)
|
|
53
|
+
return;
|
|
54
|
+
const currentIndex = items.indexOf(focused);
|
|
55
|
+
let target;
|
|
56
|
+
if (key === 'Home') {
|
|
57
|
+
target = items[0];
|
|
58
|
+
}
|
|
59
|
+
else if (key === 'End') {
|
|
60
|
+
target = items[items.length - 1];
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const delta = key === next ? 1 : -1;
|
|
64
|
+
const index = currentIndex + delta;
|
|
65
|
+
target = index < 0 ? items[items.length - 1] : index >= items.length ? items[0] : items[index];
|
|
66
|
+
}
|
|
67
|
+
target.focus();
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { arrowKeyNav, rovingTabindex } from '../keyboard-nav.ts'
|
|
2
3
|
import type { NavigationDrawerProps } from './types.ts'
|
|
3
4
|
let {
|
|
4
5
|
modal = false,
|
|
@@ -7,12 +8,22 @@
|
|
|
7
8
|
direction = 'ltr',
|
|
8
9
|
popover,
|
|
9
10
|
children,
|
|
11
|
+
onkeydown: userKeydown,
|
|
10
12
|
...attributes
|
|
11
13
|
}: NavigationDrawerProps = $props()
|
|
14
|
+
|
|
15
|
+
const attach = rovingTabindex('.np-navigation-drawer-item')
|
|
16
|
+
const arrowHandler = arrowKeyNav('.np-navigation-drawer-item')
|
|
17
|
+
|
|
18
|
+
const handleKeydown = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLElement }) => {
|
|
19
|
+
userKeydown?.(event)
|
|
20
|
+
if (!event.defaultPrevented) arrowHandler(event)
|
|
21
|
+
}
|
|
12
22
|
</script>
|
|
13
23
|
|
|
14
24
|
<nav
|
|
15
25
|
{...attributes}
|
|
26
|
+
{@attach attach}
|
|
16
27
|
bind:this={element}
|
|
17
28
|
popover={modal ? popover || 'auto' : undefined}
|
|
18
29
|
style="--np-navigation-drawer-start: {direction === 'ltr'
|
|
@@ -24,6 +35,7 @@
|
|
|
24
35
|
backdrop && 'np-navigation-drawer-backdrop',
|
|
25
36
|
attributes.class,
|
|
26
37
|
]}
|
|
38
|
+
onkeydown={handleKeydown}
|
|
27
39
|
>
|
|
28
40
|
{#if backdrop}
|
|
29
41
|
<div
|
|
@@ -64,13 +76,13 @@
|
|
|
64
76
|
|
|
65
77
|
.np-navigation-drawer-container[popover] .np-navigation-wrapper {
|
|
66
78
|
transform: var(--np-navigation-drawer-start, translateX(-100%));
|
|
67
|
-
transition: transform
|
|
79
|
+
transition: transform var(--np-motion-expressive-slow-effects);
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
.np-navigation-drawer-container[popover] {
|
|
71
83
|
transition:
|
|
72
|
-
overlay 0.
|
|
73
|
-
display 0.
|
|
84
|
+
overlay 0.3s allow-discrete,
|
|
85
|
+
display 0.3s allow-discrete;
|
|
74
86
|
}
|
|
75
87
|
|
|
76
88
|
.np-navigation-drawer-container:popover-open .np-navigation-wrapper {
|
|
@@ -28,12 +28,14 @@
|
|
|
28
28
|
{#if 'href' in attributes}
|
|
29
29
|
<a
|
|
30
30
|
{...attributes}
|
|
31
|
+
href={attributes.href}
|
|
31
32
|
class={[
|
|
32
33
|
'np-navigation-drawer-item',
|
|
33
34
|
selected && 'np-navigation-drawer-item-selected',
|
|
34
35
|
attributes.class,
|
|
35
36
|
]}
|
|
36
37
|
aria-current={selected ? 'page' : undefined}
|
|
38
|
+
tabindex={selected ? 0 : -1}
|
|
37
39
|
>
|
|
38
40
|
{@render content()}
|
|
39
41
|
</a>
|
|
@@ -46,7 +48,8 @@
|
|
|
46
48
|
attributes.class,
|
|
47
49
|
]}
|
|
48
50
|
type="button"
|
|
49
|
-
aria-
|
|
51
|
+
aria-current={selected ? 'page' : undefined}
|
|
52
|
+
tabindex={selected ? 0 : -1}
|
|
50
53
|
>
|
|
51
54
|
{@render content()}
|
|
52
55
|
</button>
|
|
@@ -97,7 +100,11 @@
|
|
|
97
100
|
outline-color: var(--np-color-secondary);
|
|
98
101
|
outline-width: 3px;
|
|
99
102
|
outline-offset: -3px;
|
|
100
|
-
|
|
103
|
+
}
|
|
104
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
105
|
+
.np-navigation-drawer-item:focus-visible {
|
|
106
|
+
animation: focusAnimation 0.3s ease forwards;
|
|
107
|
+
}
|
|
101
108
|
}
|
|
102
109
|
@keyframes focusAnimation {
|
|
103
110
|
0% {
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { arrowKeyNav, rovingTabindex } from '../keyboard-nav.ts'
|
|
2
3
|
import type { NavigationRailProps } from './types.ts'
|
|
3
4
|
|
|
4
|
-
let { children, ...attributes }: NavigationRailProps = $props()
|
|
5
|
+
let { children, onkeydown: userKeydown, ...attributes }: NavigationRailProps = $props()
|
|
6
|
+
|
|
7
|
+
const attach = rovingTabindex('.np-navigation-action')
|
|
8
|
+
const arrowHandler = arrowKeyNav('.np-navigation-action')
|
|
9
|
+
|
|
10
|
+
const handleKeydown = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLElement }) => {
|
|
11
|
+
userKeydown?.(event)
|
|
12
|
+
if (!event.defaultPrevented) arrowHandler(event)
|
|
13
|
+
}
|
|
5
14
|
</script>
|
|
6
15
|
|
|
7
|
-
<nav
|
|
16
|
+
<nav
|
|
17
|
+
{...attributes}
|
|
18
|
+
{@attach attach}
|
|
19
|
+
class="navigation-rail {attributes.class}"
|
|
20
|
+
onkeydown={handleKeydown}
|
|
21
|
+
>
|
|
8
22
|
{#if children}
|
|
9
23
|
{@render children()}
|
|
10
24
|
{/if}
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
{#if 'href' in attributes}
|
|
20
20
|
<a
|
|
21
21
|
{...attributes}
|
|
22
|
+
href={attributes.href}
|
|
22
23
|
class={['np-navigation-action', selected && 'np-navigation-action-selected', attributes.class]}
|
|
23
24
|
aria-current={selected ? 'page' : undefined}
|
|
25
|
+
tabindex={selected ? 0 : -1}
|
|
24
26
|
>
|
|
25
27
|
{@render content()}
|
|
26
28
|
</a>
|
|
@@ -28,7 +30,8 @@
|
|
|
28
30
|
<button
|
|
29
31
|
{...attributes as HTMLButtonAttributes}
|
|
30
32
|
class={['np-navigation-action', selected && 'np-navigation-action-selected', attributes.class]}
|
|
31
|
-
aria-
|
|
33
|
+
aria-current={selected ? 'page' : undefined}
|
|
34
|
+
tabindex={selected ? 0 : -1}
|
|
32
35
|
>
|
|
33
36
|
{@render content()}
|
|
34
37
|
</button>
|
|
@@ -54,7 +57,11 @@
|
|
|
54
57
|
outline-width: 3px;
|
|
55
58
|
outline-offset: 2px;
|
|
56
59
|
border-radius: 1rem;
|
|
57
|
-
|
|
60
|
+
}
|
|
61
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
62
|
+
.np-navigation-action:focus-visible {
|
|
63
|
+
animation: focusAnimation 0.3s ease forwards;
|
|
64
|
+
}
|
|
58
65
|
}
|
|
59
66
|
@keyframes focusAnimation {
|
|
60
67
|
0% {
|
|
@@ -123,13 +123,13 @@
|
|
|
123
123
|
transition:
|
|
124
124
|
overlay 0.2s allow-discrete,
|
|
125
125
|
display 0.2s allow-discrete,
|
|
126
|
-
opacity
|
|
126
|
+
opacity var(--np-motion-expressive-default-effects);
|
|
127
127
|
opacity: 0;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
.np-snackbar:popover-open {
|
|
131
131
|
opacity: 1;
|
|
132
|
-
animation: slideIn
|
|
132
|
+
animation: slideIn var(--np-motion-expressive-default-spatial);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
@keyframes slideIn {
|
|
@@ -98,8 +98,8 @@
|
|
|
98
98
|
height: 1rem;
|
|
99
99
|
margin: 0.5rem;
|
|
100
100
|
transition:
|
|
101
|
-
transform
|
|
102
|
-
background-color
|
|
101
|
+
transform var(--np-motion-expressive-fast-spatial),
|
|
102
|
+
background-color var(--np-motion-expressive-default-effects);
|
|
103
103
|
background-color: var(--np-comp-switch-unselected-handle-color, var(--np-color-outline));
|
|
104
104
|
border-radius: var(--np-comp-switch-handle-shape, var(--np-shape-corner-full));
|
|
105
105
|
}
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
height: var(--np-comp-switch-state-layer-size, 2.5rem);
|
|
115
115
|
margin-block-start: -0.25rem;
|
|
116
116
|
margin-inline-start: -0.25rem;
|
|
117
|
-
transition:
|
|
117
|
+
transition: var(--np-motion-expressive-fast-spatial);
|
|
118
118
|
border-radius: var(--np-comp-switch-state-layer-shape, var(--np-shape-corner-full));
|
|
119
119
|
}
|
|
120
120
|
|
package/dist/tabs/Tab.svelte
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import Ripple from '../ripple/Ripple.svelte'
|
|
3
|
-
import { onMount } from 'svelte'
|
|
4
|
-
import type { TabProps } from './types.ts'
|
|
5
2
|
import Badge from '../badge/Badge.svelte'
|
|
3
|
+
import Ripple from '../ripple/Ripple.svelte'
|
|
4
|
+
import { onMount, tick } from 'svelte'
|
|
6
5
|
import { getTabsContext } from './context.js'
|
|
6
|
+
import type { TabProps } from './types.ts'
|
|
7
7
|
|
|
8
8
|
let {
|
|
9
9
|
children,
|
|
@@ -22,20 +22,17 @@
|
|
|
22
22
|
const tabsContext = getTabsContext()
|
|
23
23
|
let fallbackIndicator = $state(false)
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
tabsContext.value
|
|
27
|
-
|
|
28
|
-
onclick(event)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
const handleKeyDown = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLElement }) => {
|
|
32
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
33
|
-
tabsContext.value = value
|
|
34
|
-
}
|
|
35
|
-
if (onkeydown) {
|
|
36
|
-
onkeydown(event)
|
|
25
|
+
$effect(() => {
|
|
26
|
+
if (tabsContext.value === value) {
|
|
27
|
+
tabsContext.indicatorValue = value
|
|
37
28
|
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const setIndicatorValue = async () => {
|
|
32
|
+
await tick()
|
|
33
|
+
tabsContext.indicatorValue = value
|
|
38
34
|
}
|
|
35
|
+
|
|
39
36
|
onMount(() => {
|
|
40
37
|
if (!('anchorName' in document.documentElement.style)) {
|
|
41
38
|
fallbackIndicator = true
|
|
@@ -69,11 +66,15 @@
|
|
|
69
66
|
{@render children?.()}
|
|
70
67
|
{/if}
|
|
71
68
|
{#if tabsContext.variant === 'primary'}
|
|
72
|
-
<div
|
|
69
|
+
<div
|
|
70
|
+
class={['np-indicator', tabsContext.indicatorValue === value && 'np-indicator-anchor']}
|
|
71
|
+
></div>
|
|
73
72
|
{/if}
|
|
74
73
|
</div>
|
|
75
74
|
{#if tabsContext.variant === 'secondary'}
|
|
76
|
-
<div
|
|
75
|
+
<div
|
|
76
|
+
class={['np-indicator', tabsContext.indicatorValue === value && 'np-indicator-anchor']}
|
|
77
|
+
></div>
|
|
77
78
|
{/if}
|
|
78
79
|
</div>
|
|
79
80
|
<div class="focus-area"></div>
|
|
@@ -112,8 +113,18 @@
|
|
|
112
113
|
tabsContext.variant === 'primary' ? 'primary' : 'secondary',
|
|
113
114
|
attributes.class,
|
|
114
115
|
]}
|
|
115
|
-
onclick={
|
|
116
|
-
|
|
116
|
+
onclick={(event) => {
|
|
117
|
+
tabsContext.value = value
|
|
118
|
+
setIndicatorValue()
|
|
119
|
+
onclick?.(event)
|
|
120
|
+
}}
|
|
121
|
+
onkeydown={(event) => {
|
|
122
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
123
|
+
tabsContext.value = value
|
|
124
|
+
setIndicatorValue()
|
|
125
|
+
}
|
|
126
|
+
onkeydown?.(event)
|
|
127
|
+
}}
|
|
117
128
|
>
|
|
118
129
|
{@render content()}
|
|
119
130
|
</button>
|
package/dist/tabs/Tabs.svelte
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Divider from '../divider/Divider.svelte'
|
|
3
|
+
import { arrowKeyNav, rovingTabindex } from '../keyboard-nav.ts'
|
|
3
4
|
import { setTabsContext } from './context.js'
|
|
4
5
|
import type { TabsContext, TabsProps } from './types.ts'
|
|
5
6
|
|
|
@@ -11,13 +12,11 @@
|
|
|
11
12
|
...attributes
|
|
12
13
|
}: TabsProps = $props()
|
|
13
14
|
|
|
14
|
-
let uid = $props.id()
|
|
15
|
-
|
|
16
15
|
// svelte-ignore state_referenced_locally
|
|
17
16
|
let tabsContext = $state<TabsContext>({
|
|
18
17
|
value,
|
|
18
|
+
indicatorValue: value,
|
|
19
19
|
variant,
|
|
20
|
-
id: uid,
|
|
21
20
|
})
|
|
22
21
|
$effect(() => {
|
|
23
22
|
value = tabsContext.value
|
|
@@ -29,35 +28,22 @@
|
|
|
29
28
|
})
|
|
30
29
|
setTabsContext(tabsContext)
|
|
31
30
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (tabs && tabs.length > 0 && (event.key === 'ArrowRight' || event.key === 'ArrowLeft')) {
|
|
39
|
-
const focusedTab = event.currentTarget.querySelector<HTMLElement>('.np-tab:focus')
|
|
40
|
-
const currentIndex = focusedTab ? tabs.indexOf(focusedTab) : 0
|
|
41
|
-
const index = currentIndex + (event.key === 'ArrowRight' ? 1 : -1)
|
|
42
|
-
const newTab =
|
|
43
|
-
index < 0 ? tabs[tabs.length - 1] : index >= tabs.length ? tabs[0] : tabs[index]
|
|
44
|
-
newTab.focus()
|
|
45
|
-
event.preventDefault()
|
|
46
|
-
}
|
|
47
|
-
}
|
|
31
|
+
const secondaryStyle = $derived(
|
|
32
|
+
tabsContext.variant === 'secondary' ? '--np-indicator-radius: 0;--_indicator-gap: 0' : '',
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const attach = rovingTabindex('.np-tab', { currentAttr: 'aria-selected', currentValue: 'true' })
|
|
36
|
+
const onkeydown = arrowKeyNav('.np-tab', 'horizontal')
|
|
48
37
|
</script>
|
|
49
38
|
|
|
50
|
-
<nav
|
|
51
|
-
{...attributes}
|
|
52
|
-
bind:this={element}
|
|
53
|
-
style={tabsContext.variant === 'secondary' ? '--np-indicator-radius: 0;--_indicator-gap: 0' : ''}
|
|
54
|
-
>
|
|
39
|
+
<nav {...attributes} bind:this={element} style={secondaryStyle}>
|
|
55
40
|
<div
|
|
41
|
+
{@attach attach}
|
|
56
42
|
class={['np-tabs']}
|
|
57
43
|
role="tablist"
|
|
58
44
|
aria-orientation="horizontal"
|
|
59
45
|
tabindex="-1"
|
|
60
|
-
onkeydown
|
|
46
|
+
{onkeydown}
|
|
61
47
|
>
|
|
62
48
|
{@render children?.()}
|
|
63
49
|
</div>
|
|
@@ -65,7 +51,7 @@
|
|
|
65
51
|
</nav>
|
|
66
52
|
|
|
67
53
|
<style>
|
|
68
|
-
:global(.np-tabs .np-
|
|
54
|
+
:global(.np-tabs .np-indicator-anchor) {
|
|
69
55
|
anchor-name: --np-tab-indicator;
|
|
70
56
|
}
|
|
71
57
|
.np-tabs {
|
|
@@ -92,7 +78,7 @@
|
|
|
92
78
|
border-start-start-radius: var(--np-indicator-radius, var(--np-shape-corner-full));
|
|
93
79
|
border-start-end-radius: var(--np-indicator-radius, var(--np-shape-corner-full));
|
|
94
80
|
position-anchor: --np-tab-indicator;
|
|
95
|
-
transition:
|
|
81
|
+
transition: var(--np-motion-expressive-fast-spatial);
|
|
96
82
|
}
|
|
97
83
|
}
|
|
98
84
|
</style>
|
package/dist/tabs/types.d.ts
CHANGED
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
}
|
|
172
172
|
.active-indicator::after {
|
|
173
173
|
opacity: 0;
|
|
174
|
-
transition: opacity
|
|
174
|
+
transition: opacity var(--np-motion-expressive-fast-effects);
|
|
175
175
|
}
|
|
176
176
|
.active-indicator::before,
|
|
177
177
|
.active-indicator::after {
|
|
@@ -572,9 +572,7 @@
|
|
|
572
572
|
inset-inline-start: var(--floating-label-inline-start, 0);
|
|
573
573
|
}
|
|
574
574
|
.label {
|
|
575
|
-
transition
|
|
576
|
-
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
577
|
-
transition-duration: 150ms;
|
|
575
|
+
transition: all var(--np-motion-expressive-fast-effects);
|
|
578
576
|
box-sizing: border-box;
|
|
579
577
|
color: var(--np-color-on-surface-variant);
|
|
580
578
|
overflow: hidden;
|
|
@@ -737,7 +735,7 @@
|
|
|
737
735
|
.outline-end::after,
|
|
738
736
|
.outline-notch::after {
|
|
739
737
|
opacity: 0;
|
|
740
|
-
transition: opacity
|
|
738
|
+
transition: opacity var(--np-motion-expressive-fast-effects);
|
|
741
739
|
}
|
|
742
740
|
.field:has(input:focus-visible) .outline-start::after,
|
|
743
741
|
.field:has(input:focus-visible) .outline-end::after,
|
|
@@ -63,6 +63,27 @@
|
|
|
63
63
|
--np-elevation-3:
|
|
64
64
|
rgba(0, 0, 0, 0.2) 0px 5px 5px -3px, rgba(0, 0, 0, 0.14) 0px 8px 10px 1px,
|
|
65
65
|
rgba(0, 0, 0, 0.12) 0px 3px 14px 2px;
|
|
66
|
+
|
|
67
|
+
/* Motion: Expressive spatial */
|
|
68
|
+
--np-motion-expressive-fast-spatial: 350ms cubic-bezier(0.42, 1.67, 0.21, 0.9);
|
|
69
|
+
--np-motion-expressive-default-spatial: 500ms cubic-bezier(0.38, 1.21, 0.22, 1);
|
|
70
|
+
--np-motion-expressive-slow-spatial: 650ms cubic-bezier(0.39, 1.29, 0.35, 0.98);
|
|
71
|
+
|
|
72
|
+
/* Motion: Expressive effects */
|
|
73
|
+
--np-motion-expressive-fast-effects: 150ms cubic-bezier(0.31, 0.94, 0.34, 1);
|
|
74
|
+
--np-motion-expressive-default-effects: 200ms cubic-bezier(0.34, 0.8, 0.34, 1);
|
|
75
|
+
--np-motion-expressive-slow-effects: 300ms cubic-bezier(0.34, 0.88, 0.34, 1);
|
|
76
|
+
|
|
77
|
+
/* Motion: Standard spatial */
|
|
78
|
+
--np-motion-standard-fast-spatial: 350ms cubic-bezier(0.27, 1.06, 0.18, 1);
|
|
79
|
+
--np-motion-standard-default-spatial: 500ms cubic-bezier(0.27, 1.06, 0.18, 1);
|
|
80
|
+
--np-motion-standard-slow-spatial: 750ms cubic-bezier(0.27, 1.06, 0.18, 1);
|
|
81
|
+
|
|
82
|
+
/* Motion: Standard effects */
|
|
83
|
+
--np-motion-standard-fast-effects: 150ms cubic-bezier(0.31, 0.94, 0.34, 1);
|
|
84
|
+
--np-motion-standard-default-effects: 200ms cubic-bezier(0.34, 0.8, 0.34, 1);
|
|
85
|
+
--np-motion-standard-slow-effects: 300ms cubic-bezier(0.34, 0.88, 0.34, 1);
|
|
86
|
+
|
|
66
87
|
--np-shape-corner-full: 9999px;
|
|
67
88
|
--np-shape-corner-extra-small: 0.25rem;
|
|
68
89
|
--np-shape-corner-small: 0.5rem;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { TooltipProps } from './types.ts'
|
|
3
|
-
import { MediaQuery } from 'svelte/reactivity'
|
|
4
2
|
import { on } from 'svelte/events'
|
|
3
|
+
import { MediaQuery } from 'svelte/reactivity'
|
|
4
|
+
import type { TooltipProps } from './types.ts'
|
|
5
5
|
|
|
6
6
|
let {
|
|
7
7
|
children,
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
}
|
|
128
128
|
.np-tooltip:popover-open {
|
|
129
129
|
opacity: 1;
|
|
130
|
-
animation: scaleIn
|
|
130
|
+
animation: scaleIn var(--np-motion-expressive-fast-spatial);
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
@keyframes scaleIn {
|
package/dist/types.d.ts
CHANGED
|
@@ -13,7 +13,6 @@ export * from './progress/types.ts';
|
|
|
13
13
|
export * from './radio/types.ts';
|
|
14
14
|
export * from './ripple/types.ts';
|
|
15
15
|
export * from './select/types.ts';
|
|
16
|
-
export * from './slider/types.ts';
|
|
17
16
|
export * from './snackbar/types.ts';
|
|
18
17
|
export * from './switch/types.ts';
|
|
19
18
|
export * from './text-field/types.ts';
|
package/dist/types.js
CHANGED
|
@@ -13,7 +13,6 @@ export * from './progress/types.ts';
|
|
|
13
13
|
export * from './radio/types.ts';
|
|
14
14
|
export * from './ripple/types.ts';
|
|
15
15
|
export * from './select/types.ts';
|
|
16
|
-
export * from './slider/types.ts';
|
|
17
16
|
export * from './snackbar/types.ts';
|
|
18
17
|
export * from './switch/types.ts';
|
|
19
18
|
export * from './text-field/types.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noph-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://noph.dev",
|
|
6
6
|
"repository": {
|
|
@@ -70,25 +70,25 @@
|
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@eslint/js": "^10.0.1",
|
|
72
72
|
"@materialx/material-color-utilities": "^0.4.8",
|
|
73
|
-
"@playwright/test": "^1.
|
|
73
|
+
"@playwright/test": "^1.59.1",
|
|
74
74
|
"@sveltejs/adapter-auto": "^7.0.1",
|
|
75
|
-
"@sveltejs/kit": "^2.
|
|
75
|
+
"@sveltejs/kit": "^2.57.1",
|
|
76
76
|
"@sveltejs/package": "^2.5.7",
|
|
77
77
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
78
78
|
"@types/eslint": "^9.6.1",
|
|
79
|
-
"eslint": "^10.1
|
|
79
|
+
"eslint": "^10.2.1",
|
|
80
80
|
"eslint-config-prettier": "^10.1.8",
|
|
81
|
-
"eslint-plugin-svelte": "^3.
|
|
82
|
-
"globals": "^17.
|
|
83
|
-
"prettier": "^3.8.
|
|
81
|
+
"eslint-plugin-svelte": "^3.17.1",
|
|
82
|
+
"globals": "^17.5.0",
|
|
83
|
+
"prettier": "^3.8.3",
|
|
84
84
|
"prettier-plugin-svelte": "^3.5.1",
|
|
85
85
|
"publint": "^0.3.18",
|
|
86
|
-
"svelte": "^5.55.
|
|
87
|
-
"svelte-check": "^4.4.
|
|
88
|
-
"typescript": "^6.0.
|
|
89
|
-
"typescript-eslint": "^8.
|
|
90
|
-
"vite": "8.0.
|
|
91
|
-
"vitest": "^4.1.
|
|
86
|
+
"svelte": "^5.55.4",
|
|
87
|
+
"svelte-check": "^4.4.6",
|
|
88
|
+
"typescript": "^6.0.3",
|
|
89
|
+
"typescript-eslint": "^8.59.0",
|
|
90
|
+
"vite": "8.0.10",
|
|
91
|
+
"vitest": "^4.1.5"
|
|
92
92
|
},
|
|
93
93
|
"svelte": "./dist/index.js",
|
|
94
94
|
"types": "./dist/index.d.ts",
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { SliderProps } from './types.ts'
|
|
3
|
-
|
|
4
|
-
let {
|
|
5
|
-
type = 'standard',
|
|
6
|
-
orientation = 'horizontal',
|
|
7
|
-
size = 'sm',
|
|
8
|
-
stops = false,
|
|
9
|
-
value = $bindable(0),
|
|
10
|
-
min = 0,
|
|
11
|
-
max = 100,
|
|
12
|
-
step = 1,
|
|
13
|
-
element = $bindable(),
|
|
14
|
-
inputElement = $bindable(),
|
|
15
|
-
...attributes
|
|
16
|
-
}: SliderProps = $props()
|
|
17
|
-
</script>
|
|
18
|
-
|
|
19
|
-
<input class={[orientation]} bind:this={inputElement} type="range" />
|
|
20
|
-
|
|
21
|
-
<style>
|
|
22
|
-
.vertical {
|
|
23
|
-
writing-mode: vertical-lr;
|
|
24
|
-
direction: rtl;
|
|
25
|
-
}
|
|
26
|
-
</style>
|
package/dist/slider/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as Slider } from './Slider.svelte';
|
package/dist/slider/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as Slider } from './Slider.svelte';
|
package/dist/slider/types.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Snippet } from 'svelte';
|
|
2
|
-
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
-
export interface SliderProps extends Omit<HTMLInputAttributes, 'type' | 'role' | 'checked' | 'indeterminate' | 'size'> {
|
|
4
|
-
type?: 'standard' | 'centered' | 'range';
|
|
5
|
-
orientation?: 'horizontal' | 'vertical';
|
|
6
|
-
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
7
|
-
stops?: boolean;
|
|
8
|
-
insetIcon?: Snippet;
|
|
9
|
-
inputElement?: HTMLInputElement;
|
|
10
|
-
element?: HTMLDivElement;
|
|
11
|
-
}
|
package/dist/slider/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|