@14ch/svelte-ui 0.0.27 → 0.0.29
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 +28 -24
- package/dist/components/Button.svelte +8 -1
- package/dist/components/Button.svelte.d.ts +8 -1
- package/dist/components/Checkbox.svelte +7 -0
- package/dist/components/Checkbox.svelte.d.ts +7 -0
- package/dist/components/CheckboxGroup.svelte +5 -0
- package/dist/components/CheckboxGroup.svelte.d.ts +5 -0
- package/dist/components/ColorPicker.svelte +3 -0
- package/dist/components/ColorPicker.svelte.d.ts +3 -0
- package/dist/components/Combobox.svelte +6 -0
- package/dist/components/Combobox.svelte.d.ts +6 -0
- package/dist/components/ConfirmDialog.svelte +7 -0
- package/dist/components/ConfirmDialog.svelte.d.ts +7 -3
- package/dist/components/Datepicker.svelte +12 -0
- package/dist/components/Datepicker.svelte.d.ts +12 -3
- package/dist/components/Dialog.svelte +12 -0
- package/dist/components/Dialog.svelte.d.ts +12 -4
- package/dist/components/Drawer.svelte +13 -0
- package/dist/components/Drawer.svelte.d.ts +13 -4
- package/dist/components/Fab.svelte +11 -0
- package/dist/components/Fab.svelte.d.ts +11 -0
- package/dist/components/FileUploader.svelte +4 -0
- package/dist/components/FileUploader.svelte.d.ts +4 -0
- package/dist/components/Icon.svelte +8 -0
- package/dist/components/Icon.svelte.d.ts +8 -0
- package/dist/components/IconButton.svelte +13 -0
- package/dist/components/IconButton.svelte.d.ts +13 -0
- package/dist/components/ImageUploader.svelte +6 -0
- package/dist/components/ImageUploader.svelte.d.ts +6 -0
- package/dist/components/Input.svelte +13 -0
- package/dist/components/Input.svelte.d.ts +13 -0
- package/dist/components/LoadingSpinner.svelte +4 -0
- package/dist/components/LoadingSpinner.svelte.d.ts +4 -0
- package/dist/components/Modal.svelte +9 -0
- package/dist/components/Modal.svelte.d.ts +9 -4
- package/dist/components/Nav.svelte +172 -44
- package/dist/components/Nav.svelte.d.ts +11 -1
- package/dist/components/NavItem.svelte +438 -59
- package/dist/components/NavItem.svelte.d.ts +19 -2
- package/dist/components/Pagination.svelte +7 -0
- package/dist/components/Pagination.svelte.d.ts +7 -0
- package/dist/components/Popup.svelte +13 -0
- package/dist/components/Popup.svelte.d.ts +13 -4
- package/dist/components/PopupMenu.svelte +8 -0
- package/dist/components/PopupMenu.svelte.d.ts +8 -3
- package/dist/components/PopupMenuButton.svelte +7 -0
- package/dist/components/PopupMenuButton.svelte.d.ts +7 -0
- package/dist/components/Radio.svelte +7 -0
- package/dist/components/Radio.svelte.d.ts +7 -0
- package/dist/components/RadioGroup.svelte +5 -0
- package/dist/components/RadioGroup.svelte.d.ts +5 -0
- package/dist/components/SegmentedControl.svelte +5 -0
- package/dist/components/SegmentedControl.svelte.d.ts +5 -0
- package/dist/components/Select.svelte +4 -0
- package/dist/components/Select.svelte.d.ts +4 -0
- package/dist/components/Slider.svelte +5 -0
- package/dist/components/Slider.svelte.d.ts +5 -0
- package/dist/components/Snackbar.svelte +4 -0
- package/dist/components/Snackbar.svelte.d.ts +4 -0
- package/dist/components/Switch.svelte +3 -0
- package/dist/components/Switch.svelte.d.ts +3 -0
- package/dist/components/Tab.svelte +51 -118
- package/dist/components/Tab.svelte.d.ts +15 -1
- package/dist/components/Textarea.svelte +9 -0
- package/dist/components/Textarea.svelte.d.ts +9 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +0 -1
- package/dist/types/menuItem.d.ts +1 -0
- package/dist/types/propOptions.d.ts +10 -1
- package/package.json +1 -1
- package/dist/components/TabItem.svelte +0 -219
- package/dist/components/TabItem.svelte.d.ts +0 -19
|
@@ -1,24 +1,40 @@
|
|
|
1
1
|
<!-- Tab.svelte -->
|
|
2
2
|
|
|
3
3
|
<script lang="ts">
|
|
4
|
-
import
|
|
4
|
+
import Nav from './Nav.svelte';
|
|
5
5
|
import type { MenuItem } from '../types/menuItem';
|
|
6
|
-
import {
|
|
7
|
-
import { getCurrentPath as resolveCurrentPath, matchPath as doMatchPath } from '../utils/navPath';
|
|
6
|
+
import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
|
|
8
7
|
|
|
9
8
|
// =========================================================================
|
|
10
9
|
// Props, States & Constants
|
|
11
10
|
// =========================================================================
|
|
12
11
|
export type TabProps = {
|
|
13
12
|
// 基本プロパティ
|
|
14
|
-
|
|
13
|
+
/** `{ label, href, icon?, disabled? }[]` */
|
|
14
|
+
tabItems?: MenuItem[];
|
|
15
|
+
/** Prepended to each item's href for active-state matching. */
|
|
15
16
|
pathPrefix?: string;
|
|
17
|
+
/** Custom function to determine if an item is active. */
|
|
16
18
|
customPathMatcher?: (currentPath: string, itemHref: string, item: MenuItem) => boolean;
|
|
19
|
+
/** Overrides the auto-detected current path. */
|
|
17
20
|
currentPath?: string;
|
|
18
21
|
|
|
22
|
+
// HTML属性
|
|
23
|
+
id?: string;
|
|
24
|
+
|
|
25
|
+
// アイコン関連
|
|
26
|
+
iconFilled?: boolean;
|
|
27
|
+
iconWeight?: IconWeight;
|
|
28
|
+
iconGrade?: IconGrade;
|
|
29
|
+
iconOpticalSize?: IconOpticalSize;
|
|
30
|
+
iconVariant?: IconVariant;
|
|
31
|
+
|
|
19
32
|
// スタイル/レイアウト
|
|
33
|
+
/** Custom CSS color for tab labels. */
|
|
20
34
|
textColor?: string;
|
|
35
|
+
/** Custom CSS color for the active tab label. */
|
|
21
36
|
selectedTextColor?: string;
|
|
37
|
+
/** Custom CSS color for the active tab indicator bar. */
|
|
22
38
|
selectedBarColor?: string;
|
|
23
39
|
|
|
24
40
|
// ARIA/アクセシビリティ
|
|
@@ -33,7 +49,17 @@
|
|
|
33
49
|
customPathMatcher,
|
|
34
50
|
currentPath,
|
|
35
51
|
|
|
36
|
-
//
|
|
52
|
+
// HTML属性
|
|
53
|
+
id,
|
|
54
|
+
|
|
55
|
+
// アイコン関連
|
|
56
|
+
iconFilled = false,
|
|
57
|
+
iconWeight = 300,
|
|
58
|
+
iconGrade = 0,
|
|
59
|
+
iconOpticalSize = 24,
|
|
60
|
+
iconVariant = 'outlined',
|
|
61
|
+
|
|
62
|
+
// スタイル/レイアウト
|
|
37
63
|
textColor,
|
|
38
64
|
selectedTextColor,
|
|
39
65
|
selectedBarColor,
|
|
@@ -42,128 +68,35 @@
|
|
|
42
68
|
ariaLabel = 'Tabs',
|
|
43
69
|
ariaLabelledby
|
|
44
70
|
}: TabProps = $props();
|
|
45
|
-
|
|
46
|
-
let resolvedCurrentPath = $state('');
|
|
47
|
-
|
|
48
|
-
// =========================================================================
|
|
49
|
-
// Effects
|
|
50
|
-
// =========================================================================
|
|
51
|
-
$effect(() => {
|
|
52
|
-
// props の currentPath が変更されたとき
|
|
53
|
-
resolvedCurrentPath = resolveCurrentPath(currentPath);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
$effect(() => {
|
|
57
|
-
// URL の変更を subscribe
|
|
58
|
-
return subscribeUrlChange(() => {
|
|
59
|
-
resolvedCurrentPath = resolveCurrentPath(currentPath);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// =========================================================================
|
|
64
|
-
// Methods
|
|
65
|
-
// =========================================================================
|
|
66
|
-
|
|
67
|
-
// シンプルなキーボードナビゲーション(disabled タブはスキップ)
|
|
68
|
-
const handleKeyDown = (event: KeyboardEvent) => {
|
|
69
|
-
if (tabItems.length === 0 || enabledIndices.length === 0) return;
|
|
70
|
-
|
|
71
|
-
const tabList = event.currentTarget as HTMLElement;
|
|
72
|
-
const tabs = Array.from(tabList.querySelectorAll('[role="tab"]')) as HTMLElement[];
|
|
73
|
-
const currentTab = event.target as HTMLElement;
|
|
74
|
-
const currentIndex = tabs.indexOf(currentTab);
|
|
75
|
-
|
|
76
|
-
if (currentIndex === -1) return;
|
|
77
|
-
|
|
78
|
-
const currentEnabledPosition = enabledIndices.indexOf(currentIndex);
|
|
79
|
-
let nextEnabledPosition = currentEnabledPosition;
|
|
80
|
-
|
|
81
|
-
switch (event.key) {
|
|
82
|
-
case 'ArrowLeft':
|
|
83
|
-
event.preventDefault();
|
|
84
|
-
nextEnabledPosition =
|
|
85
|
-
currentEnabledPosition > 0 ? currentEnabledPosition - 1 : enabledIndices.length - 1;
|
|
86
|
-
break;
|
|
87
|
-
case 'ArrowRight':
|
|
88
|
-
event.preventDefault();
|
|
89
|
-
nextEnabledPosition =
|
|
90
|
-
currentEnabledPosition < enabledIndices.length - 1 ? currentEnabledPosition + 1 : 0;
|
|
91
|
-
break;
|
|
92
|
-
case 'Home':
|
|
93
|
-
event.preventDefault();
|
|
94
|
-
nextEnabledPosition = 0;
|
|
95
|
-
break;
|
|
96
|
-
case 'End':
|
|
97
|
-
event.preventDefault();
|
|
98
|
-
nextEnabledPosition = enabledIndices.length - 1;
|
|
99
|
-
break;
|
|
100
|
-
default:
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const nextIndex = enabledIndices[nextEnabledPosition];
|
|
105
|
-
tabs[nextIndex]?.focus();
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
// =========================================================================
|
|
109
|
-
// $derived
|
|
110
|
-
// =========================================================================
|
|
111
|
-
|
|
112
|
-
// アクティブなタブのインデックスを現在のパスに基づいて計算
|
|
113
|
-
const selectedTabIndex = $derived.by(() => {
|
|
114
|
-
for (let i = 0; i < tabItems.length; i++) {
|
|
115
|
-
const item = tabItems[i];
|
|
116
|
-
if (!item.href) continue;
|
|
117
|
-
|
|
118
|
-
if (doMatchPath(resolvedCurrentPath, item.href, item, pathPrefix, customPathMatcher)) {
|
|
119
|
-
return i;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return -1;
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// 有効なタブのインデックス一覧(disabled を除く)
|
|
126
|
-
const enabledIndices = $derived(
|
|
127
|
-
tabItems.map((item, i) => (item.disabled ? -1 : i)).filter((i) => i >= 0)
|
|
128
|
-
);
|
|
129
71
|
</script>
|
|
130
72
|
|
|
131
73
|
<div
|
|
132
74
|
class="tab"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
tabindex="-1"
|
|
137
|
-
onkeydown={handleKeyDown}
|
|
75
|
+
style:--svelte-ui-nav-item-underline-text-color={textColor}
|
|
76
|
+
style:--svelte-ui-nav-item-underline-selected-text-color={selectedTextColor}
|
|
77
|
+
style:--svelte-ui-nav-item-underline-bar-color={selectedBarColor}
|
|
138
78
|
data-testid="tab"
|
|
139
79
|
>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
80
|
+
<Nav
|
|
81
|
+
navItems={tabItems}
|
|
82
|
+
variant="horizontal"
|
|
83
|
+
selectedStyle="underline"
|
|
84
|
+
{pathPrefix}
|
|
85
|
+
{customPathMatcher}
|
|
86
|
+
{currentPath}
|
|
87
|
+
{id}
|
|
88
|
+
{iconFilled}
|
|
89
|
+
{iconWeight}
|
|
90
|
+
{iconGrade}
|
|
91
|
+
{iconOpticalSize}
|
|
92
|
+
{iconVariant}
|
|
93
|
+
{ariaLabel}
|
|
94
|
+
{ariaLabelledby}
|
|
95
|
+
/>
|
|
151
96
|
</div>
|
|
152
97
|
|
|
153
98
|
<style>.tab {
|
|
154
|
-
display: flex;
|
|
155
|
-
justify-content: start;
|
|
156
|
-
position: relative;
|
|
157
|
-
width: 100%;
|
|
158
|
-
height: 100%;
|
|
159
99
|
min-height: var(--svelte-ui-tab-min-height);
|
|
160
|
-
|
|
161
|
-
overflow-y: visible;
|
|
162
|
-
-ms-overflow-style: none;
|
|
163
|
-
overscroll-behavior: contain;
|
|
100
|
+
width: 100%;
|
|
164
101
|
box-sizing: border-box;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.tab::-webkit-scrollbar {
|
|
168
|
-
display: none;
|
|
169
102
|
}</style>
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import type { MenuItem } from '../types/menuItem';
|
|
2
|
+
import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
|
|
2
3
|
export type TabProps = {
|
|
3
|
-
|
|
4
|
+
/** `{ label, href, icon?, disabled? }[]` */
|
|
5
|
+
tabItems?: MenuItem[];
|
|
6
|
+
/** Prepended to each item's href for active-state matching. */
|
|
4
7
|
pathPrefix?: string;
|
|
8
|
+
/** Custom function to determine if an item is active. */
|
|
5
9
|
customPathMatcher?: (currentPath: string, itemHref: string, item: MenuItem) => boolean;
|
|
10
|
+
/** Overrides the auto-detected current path. */
|
|
6
11
|
currentPath?: string;
|
|
12
|
+
id?: string;
|
|
13
|
+
iconFilled?: boolean;
|
|
14
|
+
iconWeight?: IconWeight;
|
|
15
|
+
iconGrade?: IconGrade;
|
|
16
|
+
iconOpticalSize?: IconOpticalSize;
|
|
17
|
+
iconVariant?: IconVariant;
|
|
18
|
+
/** Custom CSS color for tab labels. */
|
|
7
19
|
textColor?: string;
|
|
20
|
+
/** Custom CSS color for the active tab label. */
|
|
8
21
|
selectedTextColor?: string;
|
|
22
|
+
/** Custom CSS color for the active tab indicator bar. */
|
|
9
23
|
selectedBarColor?: string;
|
|
10
24
|
ariaLabel?: string;
|
|
11
25
|
ariaLabelledby?: string;
|
|
@@ -35,10 +35,15 @@
|
|
|
35
35
|
autocapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters' | null;
|
|
36
36
|
// スタイル/レイアウト
|
|
37
37
|
rows?: number;
|
|
38
|
+
/** Min height as px number or CSS string. */
|
|
38
39
|
minHeight?: string | number | null;
|
|
40
|
+
/** Max height as px number or CSS string. Applied when `autoResize` is enabled. */
|
|
39
41
|
maxHeight?: string | number | null;
|
|
42
|
+
/** Renders inline. @default false */
|
|
40
43
|
inline?: boolean;
|
|
44
|
+
/** @default 'outline' */
|
|
41
45
|
focusStyle?: FocusStyle;
|
|
46
|
+
/** Width as px number or CSS string. */
|
|
42
47
|
width?: string | number | null;
|
|
43
48
|
fullWidth?: boolean;
|
|
44
49
|
fullHeight?: boolean;
|
|
@@ -55,11 +60,15 @@
|
|
|
55
60
|
|
|
56
61
|
// 状態/動作
|
|
57
62
|
disabled?: boolean;
|
|
63
|
+
/** Automatically grows height to fit content. @default false */
|
|
58
64
|
autoResize?: boolean;
|
|
65
|
+
/** Allows manual resize via drag handle. @default false */
|
|
59
66
|
resizable?: boolean;
|
|
67
|
+
/** Shows a clear (×) button when the textarea has a value. @default false */
|
|
60
68
|
clearable?: boolean;
|
|
61
69
|
readonly?: boolean;
|
|
62
70
|
required?: boolean;
|
|
71
|
+
/** Converts URLs in the value to clickable links (non-focused view). @default false */
|
|
63
72
|
linkify?: boolean;
|
|
64
73
|
|
|
65
74
|
// フォーカスイベント
|
|
@@ -14,10 +14,15 @@ export type TextareaProps = {
|
|
|
14
14
|
spellcheck?: boolean | null;
|
|
15
15
|
autocapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters' | null;
|
|
16
16
|
rows?: number;
|
|
17
|
+
/** Min height as px number or CSS string. */
|
|
17
18
|
minHeight?: string | number | null;
|
|
19
|
+
/** Max height as px number or CSS string. Applied when `autoResize` is enabled. */
|
|
18
20
|
maxHeight?: string | number | null;
|
|
21
|
+
/** Renders inline. @default false */
|
|
19
22
|
inline?: boolean;
|
|
23
|
+
/** @default 'outline' */
|
|
20
24
|
focusStyle?: FocusStyle;
|
|
25
|
+
/** Width as px number or CSS string. */
|
|
21
26
|
width?: string | number | null;
|
|
22
27
|
fullWidth?: boolean;
|
|
23
28
|
fullHeight?: boolean;
|
|
@@ -30,11 +35,15 @@ export type TextareaProps = {
|
|
|
30
35
|
iconOpticalSize?: IconOpticalSize;
|
|
31
36
|
iconVariant?: IconVariant;
|
|
32
37
|
disabled?: boolean;
|
|
38
|
+
/** Automatically grows height to fit content. @default false */
|
|
33
39
|
autoResize?: boolean;
|
|
40
|
+
/** Allows manual resize via drag handle. @default false */
|
|
34
41
|
resizable?: boolean;
|
|
42
|
+
/** Shows a clear (×) button when the textarea has a value. @default false */
|
|
35
43
|
clearable?: boolean;
|
|
36
44
|
readonly?: boolean;
|
|
37
45
|
required?: boolean;
|
|
46
|
+
/** Converts URLs in the value to clickable links (non-focused view). @default false */
|
|
38
47
|
linkify?: boolean;
|
|
39
48
|
onfocus?: FocusHandler;
|
|
40
49
|
onblur?: FocusHandler;
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,6 @@ export { default as Switch } from './components/Switch.svelte';
|
|
|
35
35
|
export { default as Nav } from './components/Nav.svelte';
|
|
36
36
|
export { default as NavItem } from './components/NavItem.svelte';
|
|
37
37
|
export { default as Tab } from './components/Tab.svelte';
|
|
38
|
-
export { default as TabItem } from './components/TabItem.svelte';
|
|
39
38
|
export { default as Textarea } from './components/Textarea.svelte';
|
|
40
39
|
export type { ButtonProps } from './components/Button.svelte';
|
|
41
40
|
export type { CheckboxProps } from './components/Checkbox.svelte';
|
|
@@ -78,11 +77,14 @@ export type { SwitchProps } from './components/Switch.svelte';
|
|
|
78
77
|
export type { NavProps } from './components/Nav.svelte';
|
|
79
78
|
export type { NavItemProps, NavItemSelectedStyle } from './components/NavItem.svelte';
|
|
80
79
|
export type { TabProps } from './components/Tab.svelte';
|
|
81
|
-
export type { TabItemProps } from './components/TabItem.svelte';
|
|
82
80
|
export type { TextareaProps } from './components/Textarea.svelte';
|
|
83
|
-
export type { PopupPosition, SnackbarPosition, FabPosition, ButtonVariant, ButtonSize, SnackbarType, SnackbarVariant, BadgeVariant, DatepickerMode, FocusStyle, NavVariant } from './types/propOptions';
|
|
81
|
+
export type { PopupPosition, SnackbarPosition, FabPosition, ButtonVariant, ButtonSize, SnackbarType, SnackbarVariant, BadgeVariant, DatepickerMode, FocusStyle, NavVariant, SubMenuMode } from './types/propOptions';
|
|
84
82
|
export type { MenuItem } from './types/menuItem';
|
|
85
83
|
export type { SegmentedControlItem } from './types/segmentedControlItem';
|
|
84
|
+
export type { Option, OptionValue } from './types/options';
|
|
85
|
+
export type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from './types/icon';
|
|
86
|
+
export type { DomEventHandler, FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler, BivariantValueHandler } from './types/callbackHandlers';
|
|
87
|
+
export type { SkeletonPatternConfig, SkeletonBoxConfig, SkeletonTextConfig, SkeletonAvatarConfig, SkeletonAvatarImageConfig, SkeletonMediaConfig, SkeletonThumbnailConfig, SkeletonButtonConfig, SkeletonHeadingConfig, SkeletonPresetConfig } from './types/skeleton';
|
|
86
88
|
export * from './utils/accessibility';
|
|
87
89
|
export * from './utils/formatText';
|
|
88
90
|
export * from './utils/mobile';
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,6 @@ export { default as Switch } from './components/Switch.svelte';
|
|
|
36
36
|
export { default as Nav } from './components/Nav.svelte';
|
|
37
37
|
export { default as NavItem } from './components/NavItem.svelte';
|
|
38
38
|
export { default as Tab } from './components/Tab.svelte';
|
|
39
|
-
export { default as TabItem } from './components/TabItem.svelte';
|
|
40
39
|
export { default as Textarea } from './components/Textarea.svelte';
|
|
41
40
|
// Utils
|
|
42
41
|
export * from './utils/accessibility';
|
package/dist/types/menuItem.d.ts
CHANGED
|
@@ -56,4 +56,13 @@ export type FocusStyle = 'background' | 'outline' | 'none';
|
|
|
56
56
|
* Nav variant type
|
|
57
57
|
* Used by Nav component
|
|
58
58
|
*/
|
|
59
|
-
export type NavVariant = 'vertical' | 'horizontal' | 'mobile'
|
|
59
|
+
export type NavVariant = 'vertical' | 'horizontal' | 'mobile';
|
|
60
|
+
/**
|
|
61
|
+
* Sub-menu display mode for Nav hierarchical menus.
|
|
62
|
+
* - `popup`: child items appear in a floating panel (all variants)
|
|
63
|
+
* - `accordion`: child items expand/collapse inline (vertical only)
|
|
64
|
+
* - `expanded`: child items are always visible inline (vertical only)
|
|
65
|
+
* - `bar`: child items appear in a secondary bar below the nav (horizontal only)
|
|
66
|
+
* - `bottom-sheet`: child items appear in a fixed bottom sheet overlay (mobile only)
|
|
67
|
+
*/
|
|
68
|
+
export type SubMenuMode = 'popup' | 'accordion' | 'expanded' | 'bar' | 'bottom-sheet';
|
package/package.json
CHANGED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
<!-- TabItem.svelte -->
|
|
2
|
-
|
|
3
|
-
<script lang="ts">
|
|
4
|
-
import Icon from './Icon.svelte';
|
|
5
|
-
import type { MenuItem } from '../types/menuItem';
|
|
6
|
-
import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
|
|
7
|
-
|
|
8
|
-
// =========================================================================
|
|
9
|
-
// Props, States & Constants
|
|
10
|
-
// =========================================================================
|
|
11
|
-
export type TabItemProps = {
|
|
12
|
-
// 基本プロパティ
|
|
13
|
-
tabItem: MenuItem;
|
|
14
|
-
pathPrefix?: string;
|
|
15
|
-
|
|
16
|
-
// スタイル/レイアウト(未指定時は variables の tab 用変数を参照)
|
|
17
|
-
textColor?: string;
|
|
18
|
-
selectedTextColor?: string;
|
|
19
|
-
selectedBarColor?: string;
|
|
20
|
-
|
|
21
|
-
// アイコン関連
|
|
22
|
-
iconFilled?: boolean;
|
|
23
|
-
iconWeight?: IconWeight;
|
|
24
|
-
iconGrade?: IconGrade;
|
|
25
|
-
iconOpticalSize?: IconOpticalSize;
|
|
26
|
-
iconVariant?: IconVariant;
|
|
27
|
-
|
|
28
|
-
// 状態/動作
|
|
29
|
-
isSelected?: boolean;
|
|
30
|
-
isDisabled?: boolean;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
let {
|
|
34
|
-
// 基本プロパティ
|
|
35
|
-
tabItem,
|
|
36
|
-
pathPrefix = '',
|
|
37
|
-
|
|
38
|
-
// スタイル/レイアウト
|
|
39
|
-
textColor,
|
|
40
|
-
selectedTextColor,
|
|
41
|
-
selectedBarColor,
|
|
42
|
-
|
|
43
|
-
// アイコン関連
|
|
44
|
-
iconFilled = false,
|
|
45
|
-
iconWeight = 300,
|
|
46
|
-
iconGrade = 0,
|
|
47
|
-
iconOpticalSize = 24,
|
|
48
|
-
iconVariant = 'outlined',
|
|
49
|
-
|
|
50
|
-
// 状態/動作
|
|
51
|
-
isSelected = false,
|
|
52
|
-
isDisabled = false
|
|
53
|
-
}: TabItemProps = $props();
|
|
54
|
-
|
|
55
|
-
// =========================================================================
|
|
56
|
-
// $derived
|
|
57
|
-
// =========================================================================
|
|
58
|
-
|
|
59
|
-
// pathPrefixを付与したhrefを計算
|
|
60
|
-
const hrefWithPrefix = $derived.by(() => {
|
|
61
|
-
if (!tabItem.href) return undefined;
|
|
62
|
-
if (!pathPrefix) return tabItem.href;
|
|
63
|
-
|
|
64
|
-
// 既にpathPrefixが含まれている場合はそのまま
|
|
65
|
-
// pathPrefixが完全一致、またはpathPrefix + '/'で始まる場合
|
|
66
|
-
if (tabItem.href === pathPrefix || tabItem.href.startsWith(`${pathPrefix}/`)) {
|
|
67
|
-
return tabItem.href;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// pathPrefixを付与
|
|
71
|
-
return `${pathPrefix}${tabItem.href.startsWith('/') ? '' : '/'}${tabItem.href}`;
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// 明示的に渡されたときだけ内部CSS変数で上書き。未渡しなら variables の tab 用変数をそのまま参照
|
|
75
|
-
const internalTextColor = $derived(textColor);
|
|
76
|
-
const internalSelectedTextColor = $derived(selectedTextColor);
|
|
77
|
-
const internalSelectedBarColor = $derived(selectedBarColor);
|
|
78
|
-
</script>
|
|
79
|
-
|
|
80
|
-
{#if isDisabled}
|
|
81
|
-
<span
|
|
82
|
-
class="tab-item tab-item--disabled"
|
|
83
|
-
class:tab-item--selected={isSelected}
|
|
84
|
-
style:--internal-tab-item-text-color={internalTextColor}
|
|
85
|
-
style:--internal-tab-item-selected-text-color={internalSelectedTextColor}
|
|
86
|
-
style:--internal-tab-item-selected-bar-color={internalSelectedBarColor}
|
|
87
|
-
role="tab"
|
|
88
|
-
aria-selected={isSelected}
|
|
89
|
-
aria-disabled="true"
|
|
90
|
-
tabindex="-1"
|
|
91
|
-
data-testid="tab-item"
|
|
92
|
-
>
|
|
93
|
-
{#if tabItem.icon}
|
|
94
|
-
<div class="tab-item__icon">
|
|
95
|
-
<Icon
|
|
96
|
-
filled={iconFilled || isSelected}
|
|
97
|
-
weight={iconWeight}
|
|
98
|
-
grade={iconGrade}
|
|
99
|
-
opticalSize={iconOpticalSize}
|
|
100
|
-
variant={iconVariant}>{tabItem.icon}</Icon
|
|
101
|
-
>
|
|
102
|
-
</div>
|
|
103
|
-
{/if}
|
|
104
|
-
{#if tabItem.label}
|
|
105
|
-
<div class="tab-item__label">
|
|
106
|
-
{tabItem.label}
|
|
107
|
-
</div>
|
|
108
|
-
{/if}
|
|
109
|
-
</span>
|
|
110
|
-
{:else}
|
|
111
|
-
<a
|
|
112
|
-
href={hrefWithPrefix}
|
|
113
|
-
class="tab-item"
|
|
114
|
-
class:tab-item--selected={isSelected}
|
|
115
|
-
style:--internal-tab-item-text-color={internalTextColor}
|
|
116
|
-
style:--internal-tab-item-selected-text-color={internalSelectedTextColor}
|
|
117
|
-
style:--internal-tab-item-selected-bar-color={internalSelectedBarColor}
|
|
118
|
-
role="tab"
|
|
119
|
-
aria-selected={isSelected}
|
|
120
|
-
tabindex={0}
|
|
121
|
-
data-testid="tab-item"
|
|
122
|
-
>
|
|
123
|
-
{#if tabItem.icon}
|
|
124
|
-
<div class="tab-item__icon">
|
|
125
|
-
<Icon
|
|
126
|
-
filled={iconFilled || isSelected}
|
|
127
|
-
weight={iconWeight}
|
|
128
|
-
grade={iconGrade}
|
|
129
|
-
opticalSize={iconOpticalSize}
|
|
130
|
-
variant={iconVariant}>{tabItem.icon}</Icon
|
|
131
|
-
>
|
|
132
|
-
</div>
|
|
133
|
-
{/if}
|
|
134
|
-
{#if tabItem.label}
|
|
135
|
-
<div class="tab-item__label">
|
|
136
|
-
{tabItem.label}
|
|
137
|
-
</div>
|
|
138
|
-
{/if}
|
|
139
|
-
</a>
|
|
140
|
-
{/if}
|
|
141
|
-
|
|
142
|
-
<style>@charset "UTF-8";
|
|
143
|
-
.tab-item {
|
|
144
|
-
display: flex;
|
|
145
|
-
justify-content: center;
|
|
146
|
-
align-items: center;
|
|
147
|
-
gap: var(--svelte-ui-tab-item-icon-gap);
|
|
148
|
-
position: relative;
|
|
149
|
-
padding: var(--svelte-ui-tab-item-padding);
|
|
150
|
-
color: var(--internal-tab-item-text-color, var(--svelte-ui-tab-item-text-color));
|
|
151
|
-
white-space: nowrap;
|
|
152
|
-
text-decoration: none;
|
|
153
|
-
transition-property: background-color, color, outline;
|
|
154
|
-
transition-duration: var(--svelte-ui-transition-duration);
|
|
155
|
-
outline: none;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
@media (hover: hover) {
|
|
159
|
-
.tab-item:hover:not(.tab-item--selected) {
|
|
160
|
-
color: var(--internal-tab-item-selected-text-color, var(--svelte-ui-tab-item-selected-text-color));
|
|
161
|
-
}
|
|
162
|
-
.tab-item:hover:not(.tab-item--selected)::before {
|
|
163
|
-
opacity: 1;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/* キーボードフォーカス時のみ枠線を表示 */
|
|
167
|
-
.tab-item:focus-visible {
|
|
168
|
-
outline: var(--svelte-ui-focus-outline-inner);
|
|
169
|
-
outline-offset: var(--svelte-ui-focus-outline-offset-inner);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/* フォールバック: :focus-visible未対応ブラウザ用 */
|
|
173
|
-
@supports not selector(:focus-visible) {
|
|
174
|
-
.tab-item:focus {
|
|
175
|
-
outline: var(--svelte-ui-focus-outline-inner);
|
|
176
|
-
outline-offset: var(--svelte-ui-focus-outline-offset-inner);
|
|
177
|
-
}
|
|
178
|
-
.tab-item:focus:not(.tab-item--selected) {
|
|
179
|
-
color: var(--internal-tab-item-selected-text-color, var(--svelte-ui-tab-item-selected-text-color));
|
|
180
|
-
}
|
|
181
|
-
.tab-item:focus:not(.tab-item--selected)::before {
|
|
182
|
-
opacity: 1;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/* 選択状態のインジケーター */
|
|
186
|
-
.tab-item::before {
|
|
187
|
-
content: "";
|
|
188
|
-
display: block;
|
|
189
|
-
position: absolute;
|
|
190
|
-
left: calc(var(--svelte-ui-tab-item-padding-x) - var(--svelte-ui-tab-item-selected-bar-offset));
|
|
191
|
-
bottom: 0;
|
|
192
|
-
width: calc(100% - 2 * var(--svelte-ui-tab-item-padding-x) + 2 * var(--svelte-ui-tab-item-selected-bar-offset));
|
|
193
|
-
height: var(--svelte-ui-tab-item-selected-bar-height);
|
|
194
|
-
background-color: var(--internal-tab-item-selected-bar-color, var(--svelte-ui-tab-item-selected-bar-color));
|
|
195
|
-
border-radius: var(--svelte-ui-tab-item-selected-bar-radius);
|
|
196
|
-
opacity: 0;
|
|
197
|
-
transition-property: opacity;
|
|
198
|
-
transition-duration: var(--svelte-ui-transition-duration);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
.tab-item--selected {
|
|
202
|
-
color: var(--internal-tab-item-selected-text-color, var(--svelte-ui-tab-item-selected-text-color));
|
|
203
|
-
background-color: transparent;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
.tab-item--selected::before {
|
|
207
|
-
opacity: 1;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.tab-item--disabled {
|
|
211
|
-
opacity: var(--svelte-ui-tab-item-disabled-opacity);
|
|
212
|
-
pointer-events: none;
|
|
213
|
-
cursor: default;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.tab-item__label {
|
|
217
|
-
text-box-trim: trim-both;
|
|
218
|
-
text-box-edge: cap alphabetic;
|
|
219
|
-
}</style>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { MenuItem } from '../types/menuItem';
|
|
2
|
-
import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
|
|
3
|
-
export type TabItemProps = {
|
|
4
|
-
tabItem: MenuItem;
|
|
5
|
-
pathPrefix?: string;
|
|
6
|
-
textColor?: string;
|
|
7
|
-
selectedTextColor?: string;
|
|
8
|
-
selectedBarColor?: string;
|
|
9
|
-
iconFilled?: boolean;
|
|
10
|
-
iconWeight?: IconWeight;
|
|
11
|
-
iconGrade?: IconGrade;
|
|
12
|
-
iconOpticalSize?: IconOpticalSize;
|
|
13
|
-
iconVariant?: IconVariant;
|
|
14
|
-
isSelected?: boolean;
|
|
15
|
-
isDisabled?: boolean;
|
|
16
|
-
};
|
|
17
|
-
declare const TabItem: import("svelte").Component<TabItemProps, {}, "">;
|
|
18
|
-
type TabItem = ReturnType<typeof TabItem>;
|
|
19
|
-
export default TabItem;
|