@14ch/svelte-ui 0.0.32 → 0.0.34
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/assets/styles/variables.scss +2 -0
- package/dist/components/Nav.svelte +66 -53
- package/dist/components/NavItem.svelte +2 -13
- package/dist/components/NavItem.svelte.d.ts +0 -4
- package/dist/components/Tab.svelte +16 -0
- package/dist/components/Tab.svelte.d.ts +8 -0
- package/package.json +1 -1
|
@@ -395,6 +395,8 @@
|
|
|
395
395
|
--svelte-ui-nav-vertical-item-gap: 8px;
|
|
396
396
|
/* Nav: horizontal */
|
|
397
397
|
--svelte-ui-nav-horizontal-item-gap: 8px;
|
|
398
|
+
/* Nav: children spacing (parent item → children list) */
|
|
399
|
+
--svelte-ui-nav-children-offset: 8px;
|
|
398
400
|
/* Nav: sub-menu */
|
|
399
401
|
--svelte-ui-nav-sub-popup-min-width: 160px;
|
|
400
402
|
--svelte-ui-nav-sub-popup-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
@@ -78,7 +78,11 @@
|
|
|
78
78
|
// スタイル/レイアウト
|
|
79
79
|
selectedVariant,
|
|
80
80
|
gap,
|
|
81
|
-
childrenVariant = variant === 'mobile'
|
|
81
|
+
childrenVariant = variant === 'mobile'
|
|
82
|
+
? 'bottom-sheet'
|
|
83
|
+
: variant === 'vertical'
|
|
84
|
+
? 'accordion'
|
|
85
|
+
: 'bar',
|
|
82
86
|
chevron,
|
|
83
87
|
customContainerStyle,
|
|
84
88
|
customItemStyle,
|
|
@@ -107,21 +111,24 @@
|
|
|
107
111
|
$effect(() => {
|
|
108
112
|
return subscribeUrlChange(() => {
|
|
109
113
|
resolvedCurrentPath = getCurrentPath(currentPath);
|
|
110
|
-
if (childrenVariant !== 'accordion') expandedParent = null;
|
|
111
114
|
});
|
|
112
115
|
});
|
|
113
116
|
|
|
114
|
-
// accordion モード:
|
|
117
|
+
// accordion / bar モード: アクティブな親を自動展開
|
|
115
118
|
$effect(() => {
|
|
116
|
-
if (childrenVariant !== 'accordion' || !resolvedCurrentPath)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
if ((childrenVariant !== 'accordion' && childrenVariant !== 'bar') || !resolvedCurrentPath)
|
|
120
|
+
return;
|
|
121
|
+
const selected = navItems[selectedIndex];
|
|
122
|
+
const activeParent = navItems.find(
|
|
123
|
+
(item) =>
|
|
124
|
+
(item === selected && !!item.children?.length) ||
|
|
125
|
+
item.children?.some(
|
|
126
|
+
(child) =>
|
|
127
|
+
child.href &&
|
|
128
|
+
matchPath(resolvedCurrentPath, child.href, child, pathPrefix, customPathMatcher)
|
|
129
|
+
)
|
|
123
130
|
);
|
|
124
|
-
|
|
131
|
+
expandedParent = activeParent ?? null;
|
|
125
132
|
});
|
|
126
133
|
|
|
127
134
|
// =========================================================================
|
|
@@ -140,7 +147,7 @@
|
|
|
140
147
|
const selector = includeChildren ? '[data-nav-item], [data-nav-item-child]' : '[data-nav-item]';
|
|
141
148
|
const navItemEls = Array.from(
|
|
142
149
|
(event.currentTarget as HTMLElement).querySelectorAll<HTMLElement>(selector)
|
|
143
|
-
).filter(el => el.tabIndex !== -1);
|
|
150
|
+
).filter((el) => el.tabIndex !== -1);
|
|
144
151
|
|
|
145
152
|
if (navItemEls.length === 0) return;
|
|
146
153
|
|
|
@@ -223,29 +230,27 @@
|
|
|
223
230
|
}
|
|
224
231
|
};
|
|
225
232
|
|
|
226
|
-
const handleToggle = (item: MenuItem) => {
|
|
227
|
-
if (childrenVariant === 'accordion') {
|
|
228
|
-
// accordion: 開くのみ(再クリックで閉じない、他は自動的に閉じる)
|
|
229
|
-
expandedParent = item;
|
|
230
|
-
} else {
|
|
231
|
-
// bar: トグル
|
|
232
|
-
expandedParent = expandedParent === item ? null : item;
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
233
|
// =========================================================================
|
|
237
234
|
// $derived
|
|
238
235
|
// =========================================================================
|
|
239
236
|
const selectedIndex = $derived.by(() => {
|
|
240
237
|
for (let i = 0; i < navItems.length; i++) {
|
|
241
238
|
const item = navItems[i];
|
|
242
|
-
if (
|
|
239
|
+
if (
|
|
240
|
+
item.href &&
|
|
241
|
+
matchPath(resolvedCurrentPath, item.href, item, pathPrefix, customPathMatcher)
|
|
242
|
+
) {
|
|
243
243
|
return i;
|
|
244
244
|
}
|
|
245
245
|
// bar モード: 子が選択されていれば親も選択とみなす
|
|
246
|
-
if (
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
if (
|
|
247
|
+
childrenVariant === 'bar' &&
|
|
248
|
+
item.children?.some(
|
|
249
|
+
(child) =>
|
|
250
|
+
child.href &&
|
|
251
|
+
matchPath(resolvedCurrentPath, child.href, child, pathPrefix, customPathMatcher)
|
|
252
|
+
)
|
|
253
|
+
) {
|
|
249
254
|
return i;
|
|
250
255
|
}
|
|
251
256
|
}
|
|
@@ -257,7 +262,8 @@
|
|
|
257
262
|
);
|
|
258
263
|
|
|
259
264
|
const isChildSelected = (child: MenuItem) =>
|
|
260
|
-
!!child.href &&
|
|
265
|
+
!!child.href &&
|
|
266
|
+
matchPath(resolvedCurrentPath, child.href, child, pathPrefix, customPathMatcher);
|
|
261
267
|
</script>
|
|
262
268
|
|
|
263
269
|
<nav
|
|
@@ -271,37 +277,43 @@
|
|
|
271
277
|
bind:this={navEl}
|
|
272
278
|
>
|
|
273
279
|
<div style="display: contents" role="presentation" onkeydown={handleKeyDown}>
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
{/each}
|
|
280
|
+
{#each navItems as item, index}
|
|
281
|
+
<NavItem
|
|
282
|
+
{item}
|
|
283
|
+
{variant}
|
|
284
|
+
{pathPrefix}
|
|
285
|
+
isSelected={index === selectedIndex}
|
|
286
|
+
isDisabled={item.disabled ?? false}
|
|
287
|
+
{iconFilled}
|
|
288
|
+
{iconWeight}
|
|
289
|
+
{iconGrade}
|
|
290
|
+
{iconOpticalSize}
|
|
291
|
+
{iconVariant}
|
|
292
|
+
{selectedVariant}
|
|
293
|
+
{childrenVariant}
|
|
294
|
+
{chevron}
|
|
295
|
+
customStyle={customItemStyle}
|
|
296
|
+
customChildrenStyle={customChildrenItemStyle}
|
|
297
|
+
{customChildrenContainerStyle}
|
|
298
|
+
{resolvedCurrentPath}
|
|
299
|
+
{customPathMatcher}
|
|
300
|
+
isChildrenVisible={(childrenVariant === 'bar' || childrenVariant === 'accordion') &&
|
|
301
|
+
expandedParent === item}
|
|
302
|
+
/>
|
|
303
|
+
{/each}
|
|
299
304
|
</div>
|
|
300
305
|
</nav>
|
|
301
306
|
|
|
302
307
|
<!-- bar モード: 選択中の親の子アイテムを横バーとして表示 -->
|
|
303
308
|
{#if showSubBar && expandedParent?.children}
|
|
304
|
-
<div
|
|
309
|
+
<div
|
|
310
|
+
class="nav__children-bar"
|
|
311
|
+
role="menu"
|
|
312
|
+
tabindex="-1"
|
|
313
|
+
style={customChildrenContainerStyle}
|
|
314
|
+
onkeydown={handleSubBarKeyDown}
|
|
315
|
+
bind:this={subBarEl}
|
|
316
|
+
>
|
|
305
317
|
{#each expandedParent.children as child}
|
|
306
318
|
<NavItem
|
|
307
319
|
item={child}
|
|
@@ -362,4 +374,5 @@
|
|
|
362
374
|
flex-direction: row;
|
|
363
375
|
gap: var(--internal-nav-gap, var(--svelte-ui-nav-horizontal-item-gap));
|
|
364
376
|
align-items: center;
|
|
377
|
+
padding-top: var(--svelte-ui-nav-children-offset);
|
|
365
378
|
}</style>
|
|
@@ -55,11 +55,6 @@
|
|
|
55
55
|
/** Whether this item's children are currently visible. */
|
|
56
56
|
isChildrenVisible?: boolean;
|
|
57
57
|
|
|
58
|
-
// イベントハンドラ
|
|
59
|
-
/** For bar/accordion mode: called when this parent is clicked. */
|
|
60
|
-
onChildrenToggle?: (item: MenuItem) => void;
|
|
61
|
-
/** Called when a leaf item (no children) is clicked — used to close any open sub-menu. */
|
|
62
|
-
onChildrenClose?: () => void;
|
|
63
58
|
};
|
|
64
59
|
|
|
65
60
|
let {
|
|
@@ -91,9 +86,6 @@
|
|
|
91
86
|
isChild = false,
|
|
92
87
|
isChildrenVisible = $bindable(false),
|
|
93
88
|
|
|
94
|
-
// イベントハンドラ
|
|
95
|
-
onChildrenToggle,
|
|
96
|
-
onChildrenClose
|
|
97
89
|
}: NavItemProps = $props();
|
|
98
90
|
|
|
99
91
|
// =========================================================================
|
|
@@ -222,8 +214,6 @@
|
|
|
222
214
|
popupMenuRef?.toggle();
|
|
223
215
|
} else if (childrenVariant === 'bottom-sheet') {
|
|
224
216
|
toggleOpen();
|
|
225
|
-
} else if (childrenVariant !== 'expanded') {
|
|
226
|
-
onChildrenToggle?.(item);
|
|
227
217
|
}
|
|
228
218
|
};
|
|
229
219
|
|
|
@@ -373,6 +363,7 @@
|
|
|
373
363
|
role="menu"
|
|
374
364
|
tabindex="-1"
|
|
375
365
|
transition:fly={{ y: 100, duration: 250 }}
|
|
366
|
+
onclick={closeOpen}
|
|
376
367
|
onkeydown={(e) => handleChildKeyDown(e, true)}
|
|
377
368
|
bind:this={bottomSheetEl}
|
|
378
369
|
>
|
|
@@ -393,7 +384,6 @@
|
|
|
393
384
|
{customPathMatcher}
|
|
394
385
|
isSelected={isChildSelected(child)}
|
|
395
386
|
isDisabled={child.disabled ?? false}
|
|
396
|
-
onChildrenClose={closeOpen}
|
|
397
387
|
/>
|
|
398
388
|
{/each}
|
|
399
389
|
</div>
|
|
@@ -418,7 +408,6 @@
|
|
|
418
408
|
data-nav-item={!isChild ? '' : undefined}
|
|
419
409
|
data-nav-item-child={isChild ? '' : undefined}
|
|
420
410
|
data-testid="nav-item"
|
|
421
|
-
onclick={onChildrenClose}
|
|
422
411
|
>
|
|
423
412
|
{#if item.icon}
|
|
424
413
|
<div class="nav-item__icon">
|
|
@@ -623,7 +612,7 @@
|
|
|
623
612
|
.nav-item__children {
|
|
624
613
|
display: flex;
|
|
625
614
|
flex-direction: column;
|
|
626
|
-
padding-top: var(--
|
|
615
|
+
padding-top: var(--svelte-ui-nav-children-offset);
|
|
627
616
|
gap: var(--internal-nav-gap, var(--svelte-ui-nav-vertical-item-gap));
|
|
628
617
|
overflow: hidden;
|
|
629
618
|
}
|
|
@@ -33,10 +33,6 @@ export type NavItemProps = {
|
|
|
33
33
|
isChild?: boolean;
|
|
34
34
|
/** Whether this item's children are currently visible. */
|
|
35
35
|
isChildrenVisible?: boolean;
|
|
36
|
-
/** For bar/accordion mode: called when this parent is clicked. */
|
|
37
|
-
onChildrenToggle?: (item: MenuItem) => void;
|
|
38
|
-
/** Called when a leaf item (no children) is clicked — used to close any open sub-menu. */
|
|
39
|
-
onChildrenClose?: () => void;
|
|
40
36
|
};
|
|
41
37
|
declare const NavItem: import("svelte").Component<NavItemProps, {}, "isChildrenVisible">;
|
|
42
38
|
type NavItem = ReturnType<typeof NavItem>;
|
|
@@ -36,6 +36,14 @@
|
|
|
36
36
|
selectedTextColor?: string;
|
|
37
37
|
/** Custom CSS color for the active tab indicator bar. */
|
|
38
38
|
selectedBarColor?: string;
|
|
39
|
+
/** Inline style applied to the tab container element. */
|
|
40
|
+
customContainerStyle?: string;
|
|
41
|
+
/** Inline style applied to each tab item element. */
|
|
42
|
+
customItemStyle?: string;
|
|
43
|
+
/** Inline style applied to the children container. */
|
|
44
|
+
customChildrenContainerStyle?: string;
|
|
45
|
+
/** Inline style applied to each child item element. */
|
|
46
|
+
customChildrenItemStyle?: string;
|
|
39
47
|
|
|
40
48
|
// ARIA/アクセシビリティ
|
|
41
49
|
ariaLabel?: string;
|
|
@@ -63,6 +71,10 @@
|
|
|
63
71
|
textColor,
|
|
64
72
|
selectedTextColor,
|
|
65
73
|
selectedBarColor,
|
|
74
|
+
customContainerStyle,
|
|
75
|
+
customItemStyle,
|
|
76
|
+
customChildrenContainerStyle,
|
|
77
|
+
customChildrenItemStyle,
|
|
66
78
|
|
|
67
79
|
// ARIA/アクセシビリティ
|
|
68
80
|
ariaLabel = 'Tabs',
|
|
@@ -90,6 +102,10 @@
|
|
|
90
102
|
{iconGrade}
|
|
91
103
|
{iconOpticalSize}
|
|
92
104
|
{iconVariant}
|
|
105
|
+
{customContainerStyle}
|
|
106
|
+
{customItemStyle}
|
|
107
|
+
{customChildrenContainerStyle}
|
|
108
|
+
{customChildrenItemStyle}
|
|
93
109
|
{ariaLabel}
|
|
94
110
|
{ariaLabelledby}
|
|
95
111
|
/>
|
|
@@ -21,6 +21,14 @@ export type TabProps = {
|
|
|
21
21
|
selectedTextColor?: string;
|
|
22
22
|
/** Custom CSS color for the active tab indicator bar. */
|
|
23
23
|
selectedBarColor?: string;
|
|
24
|
+
/** Inline style applied to the tab container element. */
|
|
25
|
+
customContainerStyle?: string;
|
|
26
|
+
/** Inline style applied to each tab item element. */
|
|
27
|
+
customItemStyle?: string;
|
|
28
|
+
/** Inline style applied to the children container. */
|
|
29
|
+
customChildrenContainerStyle?: string;
|
|
30
|
+
/** Inline style applied to each child item element. */
|
|
31
|
+
customChildrenItemStyle?: string;
|
|
24
32
|
ariaLabel?: string;
|
|
25
33
|
ariaLabelledby?: string;
|
|
26
34
|
};
|