@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.
Files changed (72) hide show
  1. package/dist/assets/styles/variables.scss +28 -24
  2. package/dist/components/Button.svelte +8 -1
  3. package/dist/components/Button.svelte.d.ts +8 -1
  4. package/dist/components/Checkbox.svelte +7 -0
  5. package/dist/components/Checkbox.svelte.d.ts +7 -0
  6. package/dist/components/CheckboxGroup.svelte +5 -0
  7. package/dist/components/CheckboxGroup.svelte.d.ts +5 -0
  8. package/dist/components/ColorPicker.svelte +3 -0
  9. package/dist/components/ColorPicker.svelte.d.ts +3 -0
  10. package/dist/components/Combobox.svelte +6 -0
  11. package/dist/components/Combobox.svelte.d.ts +6 -0
  12. package/dist/components/ConfirmDialog.svelte +7 -0
  13. package/dist/components/ConfirmDialog.svelte.d.ts +7 -3
  14. package/dist/components/Datepicker.svelte +12 -0
  15. package/dist/components/Datepicker.svelte.d.ts +12 -3
  16. package/dist/components/Dialog.svelte +12 -0
  17. package/dist/components/Dialog.svelte.d.ts +12 -4
  18. package/dist/components/Drawer.svelte +13 -0
  19. package/dist/components/Drawer.svelte.d.ts +13 -4
  20. package/dist/components/Fab.svelte +11 -0
  21. package/dist/components/Fab.svelte.d.ts +11 -0
  22. package/dist/components/FileUploader.svelte +4 -0
  23. package/dist/components/FileUploader.svelte.d.ts +4 -0
  24. package/dist/components/Icon.svelte +8 -0
  25. package/dist/components/Icon.svelte.d.ts +8 -0
  26. package/dist/components/IconButton.svelte +13 -0
  27. package/dist/components/IconButton.svelte.d.ts +13 -0
  28. package/dist/components/ImageUploader.svelte +6 -0
  29. package/dist/components/ImageUploader.svelte.d.ts +6 -0
  30. package/dist/components/Input.svelte +13 -0
  31. package/dist/components/Input.svelte.d.ts +13 -0
  32. package/dist/components/LoadingSpinner.svelte +4 -0
  33. package/dist/components/LoadingSpinner.svelte.d.ts +4 -0
  34. package/dist/components/Modal.svelte +9 -0
  35. package/dist/components/Modal.svelte.d.ts +9 -4
  36. package/dist/components/Nav.svelte +172 -44
  37. package/dist/components/Nav.svelte.d.ts +11 -1
  38. package/dist/components/NavItem.svelte +438 -59
  39. package/dist/components/NavItem.svelte.d.ts +19 -2
  40. package/dist/components/Pagination.svelte +7 -0
  41. package/dist/components/Pagination.svelte.d.ts +7 -0
  42. package/dist/components/Popup.svelte +13 -0
  43. package/dist/components/Popup.svelte.d.ts +13 -4
  44. package/dist/components/PopupMenu.svelte +8 -0
  45. package/dist/components/PopupMenu.svelte.d.ts +8 -3
  46. package/dist/components/PopupMenuButton.svelte +7 -0
  47. package/dist/components/PopupMenuButton.svelte.d.ts +7 -0
  48. package/dist/components/Radio.svelte +7 -0
  49. package/dist/components/Radio.svelte.d.ts +7 -0
  50. package/dist/components/RadioGroup.svelte +5 -0
  51. package/dist/components/RadioGroup.svelte.d.ts +5 -0
  52. package/dist/components/SegmentedControl.svelte +5 -0
  53. package/dist/components/SegmentedControl.svelte.d.ts +5 -0
  54. package/dist/components/Select.svelte +4 -0
  55. package/dist/components/Select.svelte.d.ts +4 -0
  56. package/dist/components/Slider.svelte +5 -0
  57. package/dist/components/Slider.svelte.d.ts +5 -0
  58. package/dist/components/Snackbar.svelte +4 -0
  59. package/dist/components/Snackbar.svelte.d.ts +4 -0
  60. package/dist/components/Switch.svelte +3 -0
  61. package/dist/components/Switch.svelte.d.ts +3 -0
  62. package/dist/components/Tab.svelte +51 -118
  63. package/dist/components/Tab.svelte.d.ts +15 -1
  64. package/dist/components/Textarea.svelte +9 -0
  65. package/dist/components/Textarea.svelte.d.ts +9 -0
  66. package/dist/index.d.ts +5 -3
  67. package/dist/index.js +0 -1
  68. package/dist/types/menuItem.d.ts +1 -0
  69. package/dist/types/propOptions.d.ts +10 -1
  70. package/package.json +1 -1
  71. package/dist/components/TabItem.svelte +0 -219
  72. 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 TabItem from './TabItem.svelte';
4
+ import Nav from './Nav.svelte';
5
5
  import type { MenuItem } from '../types/menuItem';
6
- import { subscribeUrlChange } from '../utils/urlChange';
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
- tabItems: MenuItem[];
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
- // スタイル/レイアウト(未指定時は TabItem が variables の tab 用変数を直接参照)
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
- role="tablist"
134
- aria-label={ariaLabelledby ? undefined : ariaLabel}
135
- aria-labelledby={ariaLabelledby}
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
- {#each tabItems as tabItem, index}
141
- <TabItem
142
- {tabItem}
143
- {pathPrefix}
144
- isSelected={index === selectedTabIndex}
145
- isDisabled={tabItem.disabled ?? false}
146
- {textColor}
147
- {selectedTextColor}
148
- {selectedBarColor}
149
- />
150
- {/each}
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
- overflow-x: auto;
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
- tabItems: MenuItem[];
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';
@@ -7,4 +7,5 @@ export type MenuItem = {
7
7
  matchingPath?: string[];
8
8
  strictMatch?: boolean;
9
9
  disabled?: boolean;
10
+ children?: MenuItem[];
10
11
  };
@@ -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' | 'tab';
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
@@ -2,7 +2,7 @@
2
2
  "name": "@14ch/svelte-ui",
3
3
  "description": "Modern Svelte UI components library with TypeScript support",
4
4
  "private": false,
5
- "version": "0.0.27",
5
+ "version": "0.0.29",
6
6
  "type": "module",
7
7
  "keywords": [
8
8
  "svelte",
@@ -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;