@celestia-island/hikari 0.4.4

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 (187) hide show
  1. package/README.md +54 -0
  2. package/package.json +63 -0
  3. package/src/DemoApp.vue +400 -0
  4. package/src/components/HkActionBar.tsx +15 -0
  5. package/src/components/HkAlert.scss +121 -0
  6. package/src/components/HkAlert.tsx +85 -0
  7. package/src/components/HkAvatar.tsx +29 -0
  8. package/src/components/HkBadge.scss +117 -0
  9. package/src/components/HkBadge.tsx +50 -0
  10. package/src/components/HkBreadcrumb.tsx +39 -0
  11. package/src/components/HkButton.scss +128 -0
  12. package/src/components/HkButton.tsx +74 -0
  13. package/src/components/HkCard.scss +49 -0
  14. package/src/components/HkCard.tsx +38 -0
  15. package/src/components/HkCheckbox.scss +102 -0
  16. package/src/components/HkCheckbox.tsx +85 -0
  17. package/src/components/HkCollapse.tsx +48 -0
  18. package/src/components/HkColorPicker.scss +148 -0
  19. package/src/components/HkColorPicker.tsx +204 -0
  20. package/src/components/HkConfirmDialog.scss +12 -0
  21. package/src/components/HkConfirmDialog.tsx +75 -0
  22. package/src/components/HkDateTimePicker.scss +361 -0
  23. package/src/components/HkDateTimePicker.tsx +424 -0
  24. package/src/components/HkDivider.scss +85 -0
  25. package/src/components/HkDivider.tsx +51 -0
  26. package/src/components/HkDraggableGrid.scss +132 -0
  27. package/src/components/HkDraggableGrid.tsx +291 -0
  28. package/src/components/HkDraggableList.scss +110 -0
  29. package/src/components/HkDraggableList.tsx +238 -0
  30. package/src/components/HkDrawer.scss +175 -0
  31. package/src/components/HkDrawer.tsx +171 -0
  32. package/src/components/HkEmptyState.scss +42 -0
  33. package/src/components/HkEmptyState.tsx +41 -0
  34. package/src/components/HkErrorBoundary.scss +64 -0
  35. package/src/components/HkErrorBoundary.tsx +82 -0
  36. package/src/components/HkGaugeRing.scss +4 -0
  37. package/src/components/HkGaugeRing.tsx +130 -0
  38. package/src/components/HkHoverRevealAction.scss +66 -0
  39. package/src/components/HkHoverRevealAction.tsx +77 -0
  40. package/src/components/HkIcon.tsx +43 -0
  41. package/src/components/HkIconButton.tsx +42 -0
  42. package/src/components/HkImageViewer.scss +33 -0
  43. package/src/components/HkImageViewer.tsx +250 -0
  44. package/src/components/HkInput.scss +172 -0
  45. package/src/components/HkInput.tsx +175 -0
  46. package/src/components/HkKbd.tsx +26 -0
  47. package/src/components/HkKeywordSearchModal.scss +148 -0
  48. package/src/components/HkKeywordSearchModal.tsx +308 -0
  49. package/src/components/HkListTransition.scss +131 -0
  50. package/src/components/HkListTransition.tsx +29 -0
  51. package/src/components/HkLocalePickerPopup.tsx +42 -0
  52. package/src/components/HkLogo.scss +36 -0
  53. package/src/components/HkLogo.tsx +93 -0
  54. package/src/components/HkMarkdownRenderer.scss +141 -0
  55. package/src/components/HkMarkdownRenderer.tsx +123 -0
  56. package/src/components/HkMediaControlBar.scss +65 -0
  57. package/src/components/HkMediaControlBar.tsx +115 -0
  58. package/src/components/HkMediaPlayer.scss +29 -0
  59. package/src/components/HkMediaPlayer.tsx +210 -0
  60. package/src/components/HkMediaSlider.scss +54 -0
  61. package/src/components/HkMediaSlider.tsx +81 -0
  62. package/src/components/HkMediaVisualizer.scss +7 -0
  63. package/src/components/HkMediaVisualizer.tsx +119 -0
  64. package/src/components/HkMinimap.scss +76 -0
  65. package/src/components/HkMinimap.tsx +255 -0
  66. package/src/components/HkModal.scss +318 -0
  67. package/src/components/HkModal.tsx +481 -0
  68. package/src/components/HkModalBreadcrumb.scss +55 -0
  69. package/src/components/HkModalBreadcrumb.tsx +113 -0
  70. package/src/components/HkMorphingTabs.scss +141 -0
  71. package/src/components/HkMorphingTabs.tsx +113 -0
  72. package/src/components/HkNavItem.scss +81 -0
  73. package/src/components/HkNavItem.tsx +157 -0
  74. package/src/components/HkNumberInput.scss +154 -0
  75. package/src/components/HkNumberInput.tsx +161 -0
  76. package/src/components/HkPasswordInput.scss +289 -0
  77. package/src/components/HkPasswordInput.tsx +533 -0
  78. package/src/components/HkPhaseTransition.scss +25 -0
  79. package/src/components/HkPhaseTransition.tsx +40 -0
  80. package/src/components/HkPopover.scss +82 -0
  81. package/src/components/HkPopover.tsx +410 -0
  82. package/src/components/HkPopupSelect.scss +136 -0
  83. package/src/components/HkPopupSelect.tsx +199 -0
  84. package/src/components/HkProgressBar.scss +106 -0
  85. package/src/components/HkProgressBar.tsx +61 -0
  86. package/src/components/HkProgressDialog.tsx +67 -0
  87. package/src/components/HkProgressRing.scss +56 -0
  88. package/src/components/HkProgressRing.tsx +82 -0
  89. package/src/components/HkRadio.scss +119 -0
  90. package/src/components/HkRadio.tsx +75 -0
  91. package/src/components/HkRollingNumber.scss +37 -0
  92. package/src/components/HkRollingNumber.tsx +97 -0
  93. package/src/components/HkScrollContainer.scss +101 -0
  94. package/src/components/HkScrollContainer.tsx +362 -0
  95. package/src/components/HkSearchInput.scss +132 -0
  96. package/src/components/HkSearchInput.tsx +117 -0
  97. package/src/components/HkSelect.scss +143 -0
  98. package/src/components/HkSelect.tsx +230 -0
  99. package/src/components/HkSelectionGrid.scss +177 -0
  100. package/src/components/HkSelectionGrid.tsx +99 -0
  101. package/src/components/HkSelectionWaterfall.scss +93 -0
  102. package/src/components/HkSelectionWaterfall.tsx +90 -0
  103. package/src/components/HkSidebar.scss +81 -0
  104. package/src/components/HkSidebar.tsx +45 -0
  105. package/src/components/HkSkeleton.scss +44 -0
  106. package/src/components/HkSkeleton.tsx +30 -0
  107. package/src/components/HkSkeletonList.tsx +35 -0
  108. package/src/components/HkSpinner.scss +67 -0
  109. package/src/components/HkSpinner.tsx +41 -0
  110. package/src/components/HkSplash.scss +60 -0
  111. package/src/components/HkSplash.tsx +40 -0
  112. package/src/components/HkSwitch.scss +157 -0
  113. package/src/components/HkSwitch.tsx +72 -0
  114. package/src/components/HkTable.scss +198 -0
  115. package/src/components/HkTable.tsx +246 -0
  116. package/src/components/HkTabs.scss +109 -0
  117. package/src/components/HkTabs.tsx +111 -0
  118. package/src/components/HkTag.scss +88 -0
  119. package/src/components/HkTag.tsx +44 -0
  120. package/src/components/HkTextarea.scss +72 -0
  121. package/src/components/HkTextarea.tsx +104 -0
  122. package/src/components/HkTimeline.scss +139 -0
  123. package/src/components/HkTimeline.tsx +95 -0
  124. package/src/components/HkToast.scss +168 -0
  125. package/src/components/HkToast.tsx +175 -0
  126. package/src/components/HkTooltip.scss +47 -0
  127. package/src/components/HkTooltip.tsx +108 -0
  128. package/src/components/HkTree.scss +188 -0
  129. package/src/components/HkTree.tsx +217 -0
  130. package/src/components/HkTrendChart.tsx +180 -0
  131. package/src/components/HkWindowedItem.scss +17 -0
  132. package/src/components/HkWindowedItem.tsx +107 -0
  133. package/src/components/HkZoomToolbar.scss +35 -0
  134. package/src/components/HkZoomToolbar.tsx +69 -0
  135. package/src/composables/useBreakpoint.ts +41 -0
  136. package/src/composables/useClipboard.ts +37 -0
  137. package/src/composables/useConfirm.ts +35 -0
  138. package/src/composables/useProgressDialog.ts +52 -0
  139. package/src/composables/useReportedTransition.ts +69 -0
  140. package/src/composables/useScrollWindow.ts +21 -0
  141. package/src/composables/useToast.ts +2 -0
  142. package/src/demo.scss +24 -0
  143. package/src/demo.ts +22 -0
  144. package/src/env.d.ts +6 -0
  145. package/src/i18n/context.ts +89 -0
  146. package/src/i18n/locales/ar/components.json +43 -0
  147. package/src/i18n/locales/ar/components.toml +40 -0
  148. package/src/i18n/locales/de/components.json +43 -0
  149. package/src/i18n/locales/de/components.toml +40 -0
  150. package/src/i18n/locales/en/components.json +43 -0
  151. package/src/i18n/locales/en/components.toml +40 -0
  152. package/src/i18n/locales/es/components.json +43 -0
  153. package/src/i18n/locales/es/components.toml +40 -0
  154. package/src/i18n/locales/fr/components.json +43 -0
  155. package/src/i18n/locales/fr/components.toml +40 -0
  156. package/src/i18n/locales/ja/components.json +43 -0
  157. package/src/i18n/locales/ja/components.toml +40 -0
  158. package/src/i18n/locales/ko/components.json +43 -0
  159. package/src/i18n/locales/ko/components.toml +40 -0
  160. package/src/i18n/locales/pt/components.json +43 -0
  161. package/src/i18n/locales/pt/components.toml +40 -0
  162. package/src/i18n/locales/ru/components.json +43 -0
  163. package/src/i18n/locales/ru/components.toml +40 -0
  164. package/src/i18n/locales/zh-Hans/components.json +43 -0
  165. package/src/i18n/locales/zh-Hans/components.toml +40 -0
  166. package/src/i18n/locales/zh-Hant/components.json +43 -0
  167. package/src/i18n/locales/zh-Hant/components.toml +40 -0
  168. package/src/index.ts +137 -0
  169. package/src/render.ts +18 -0
  170. package/src/runtime/animationBus.ts +278 -0
  171. package/src/runtime/cronBus.ts +25 -0
  172. package/src/runtime/index.ts +12 -0
  173. package/src/runtime/useAsyncData.ts +30 -0
  174. package/src/runtime/useBreakpoint.ts +62 -0
  175. package/src/runtime/useClipboard.ts +37 -0
  176. package/src/runtime/useConfirm.ts +35 -0
  177. package/src/runtime/useOverlay.ts +82 -0
  178. package/src/runtime/usePopupManager.ts +89 -0
  179. package/src/runtime/useToast.ts +140 -0
  180. package/src/styles/index.scss +10 -0
  181. package/src/theme/index.ts +5 -0
  182. package/src/theme/presets.ts +307 -0
  183. package/src/theme/theme.scss +17 -0
  184. package/src/theme/useBackgroundLuminance.ts +145 -0
  185. package/src/theme/useSolarTime.ts +101 -0
  186. package/src/theme/useTheme.ts +141 -0
  187. package/src/tokens.scss +26 -0
@@ -0,0 +1,177 @@
1
+ .hk-selection-grid {
2
+ margin-bottom: 1rem;
3
+ }
4
+
5
+ .hk-selection-grid-title {
6
+ font-size: 0.6875rem;
7
+ font-weight: 600;
8
+ text-transform: uppercase;
9
+ letter-spacing: 0.05em;
10
+ color: var(--hi-color-text-tertiary, #8b949e);
11
+ margin: 0 0 0.5rem;
12
+ }
13
+
14
+ .hk-selection-grid-grid {
15
+ display: grid;
16
+ gap: 0.5rem;
17
+
18
+ &[data-cols="2"] { grid-template-columns: repeat(2, 1fr); }
19
+ &[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }
20
+ &[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
21
+
22
+ &[data-dense] {
23
+ gap: 0.375rem;
24
+
25
+ .hk-selection-grid-item {
26
+ padding: 0.5rem 0.625rem;
27
+ }
28
+ }
29
+ }
30
+
31
+ .hk-selection-grid-item {
32
+ position: relative;
33
+ display: flex;
34
+ align-items: flex-start;
35
+ gap: 0.5rem;
36
+ padding: 0.75rem;
37
+ border-radius: 4px;
38
+ border: 1px solid var(--hi-color-border, #30363d);
39
+ cursor: pointer;
40
+ transition:
41
+ border-color 200ms ease,
42
+ background 200ms ease,
43
+ box-shadow 200ms ease;
44
+ user-select: none;
45
+
46
+ &:hover {
47
+ border-color: var(--hi-color-primary, #58a6ff);
48
+ background: rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.04);
49
+ box-shadow: 0 2px 12px rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.1);
50
+ }
51
+
52
+ &:active {
53
+ box-shadow: 0 1px 4px rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.06);
54
+ }
55
+
56
+ &[data-selected] {
57
+ border-color: var(--hi-color-primary, #58a6ff);
58
+ background: rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.06);
59
+ box-shadow: 0 0 0 1px var(--hi-color-primary, #58a6ff);
60
+
61
+ &:hover {
62
+ background: rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.09);
63
+ box-shadow:
64
+ 0 0 0 1px var(--hi-color-primary, #58a6ff),
65
+ 0 2px 12px rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.14);
66
+ }
67
+
68
+ .hk-selection-grid-check {
69
+ opacity: 1;
70
+ transform: scale(1);
71
+ }
72
+
73
+ .hk-selection-grid-icon {
74
+ color: var(--hi-color-primary, #58a6ff);
75
+ }
76
+
77
+ .hk-selection-grid-name {
78
+ color: var(--hi-color-primary, #58a6ff);
79
+ font-weight: 600;
80
+ }
81
+ }
82
+ }
83
+
84
+ .hk-selection-grid-check {
85
+ position: absolute;
86
+ top: 0.375rem;
87
+ right: 0.375rem;
88
+ width: 1.25rem;
89
+ height: 1.25rem;
90
+ border-radius: 50%;
91
+ display: flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+ background: var(--hi-color-primary, #58a6ff);
95
+ color: #fff;
96
+ font-size: 0.625rem;
97
+ opacity: 0;
98
+ transform: scale(0.5);
99
+ transition:
100
+ opacity 200ms ease,
101
+ transform 200ms ease;
102
+ }
103
+
104
+ .hk-selection-grid-content {
105
+ flex: 1;
106
+ min-width: 0;
107
+ }
108
+
109
+ .hk-selection-grid-header {
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 0.375rem;
113
+ flex-wrap: wrap;
114
+ }
115
+
116
+ .hk-selection-grid-icon {
117
+ flex-shrink: 0;
118
+ color: var(--hi-color-text-tertiary, #8b949e);
119
+ transition: color 200ms ease;
120
+ }
121
+
122
+ .hk-selection-grid-name {
123
+ font-size: 0.8125rem;
124
+ font-weight: 500;
125
+ line-height: 1.4;
126
+ transition: color 200ms ease;
127
+ }
128
+
129
+ .hk-selection-grid-desc {
130
+ font-size: 0.6875rem;
131
+ color: var(--hi-color-text-tertiary, #8b949e);
132
+ margin: 0.25rem 0 0;
133
+ line-height: 1.4;
134
+ }
135
+
136
+ .hk-selection-grid-badge {
137
+ display: inline-flex;
138
+ align-items: center;
139
+ padding: 0 0.375rem;
140
+ height: 1rem;
141
+ font-size: 0.625rem;
142
+ font-weight: 600;
143
+ border-radius: 9999px;
144
+ background: var(--hi-color-bg-subtle, #21262d);
145
+ color: var(--hi-color-text-tertiary, #8b949e);
146
+ letter-spacing: 0.02em;
147
+
148
+ &[data-variant="primary"] {
149
+ background: rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.12);
150
+ color: var(--hi-color-primary, #58a6ff);
151
+ }
152
+
153
+ &[data-variant="success"] {
154
+ background: rgb(from var(--hi-color-success, #3fb950) r g b / 0.12);
155
+ color: var(--hi-color-success, #3fb950);
156
+ }
157
+
158
+ &[data-variant="info"] {
159
+ background: rgb(from var(--hi-color-info, #79c0ff) r g b / 0.12);
160
+ color: var(--hi-color-info, #79c0ff);
161
+ }
162
+ }
163
+
164
+ @media (max-width: 640px) {
165
+ .hk-selection-grid-grid[data-cols="3"],
166
+ .hk-selection-grid-grid[data-cols="4"] {
167
+ grid-template-columns: repeat(2, 1fr);
168
+ }
169
+ }
170
+
171
+ @media (max-width: 420px) {
172
+ .hk-selection-grid-grid[data-cols="2"],
173
+ .hk-selection-grid-grid[data-cols="3"],
174
+ .hk-selection-grid-grid[data-cols="4"] {
175
+ grid-template-columns: 1fr;
176
+ }
177
+ }
@@ -0,0 +1,99 @@
1
+ import { defineComponent, type PropType } from "vue";
2
+ import { Check } from "lucide-vue-next";
3
+ import "./HkSelectionGrid.scss";
4
+
5
+ export interface SelectionGridItem {
6
+ id: string;
7
+ title: string;
8
+ description?: string;
9
+ badge?: string;
10
+ badgeVariant?: string;
11
+ icon?: ReturnType<typeof defineComponent>;
12
+ }
13
+
14
+ export type SelectionGridCols = 2 | 3 | 4;
15
+
16
+ export default defineComponent({
17
+ name: "HkSelectionGrid",
18
+ props: {
19
+ items: { type: Array as PropType<SelectionGridItem[]>, required: true },
20
+ selectedId: { type: String, default: undefined },
21
+ columns: { type: Number as PropType<SelectionGridCols>, default: 2 },
22
+ groupTitle: { type: String, default: undefined },
23
+ dense: { type: Boolean, default: false },
24
+ },
25
+ emits: {
26
+ select: (_item: SelectionGridItem) => true,
27
+ },
28
+ setup(props, { emit }) {
29
+ return () => {
30
+ if (!props.items.length) return null;
31
+
32
+ return (
33
+ <div class="hk-selection-grid">
34
+ {props.groupTitle && (
35
+ <h4 class="hk-selection-grid-title">{props.groupTitle}</h4>
36
+ )}
37
+ <div
38
+ class="hk-selection-grid-grid"
39
+ data-cols={props.columns}
40
+ data-dense={props.dense || undefined}
41
+ >
42
+ {props.items.map((item) => {
43
+ const isSelected = props.selectedId === item.id;
44
+ const ItemIcon = item.icon;
45
+
46
+ return (
47
+ <div
48
+ key={item.id}
49
+ role="button"
50
+ tabindex={0}
51
+ class="hk-selection-grid-item"
52
+ data-selected={isSelected || undefined}
53
+ aria-selected={isSelected}
54
+ onClick={() => emit("select", item)}
55
+ onKeydown={(e) => {
56
+ if (e.key === "Enter" || e.key === " ") {
57
+ e.preventDefault();
58
+ emit("select", item);
59
+ }
60
+ }}
61
+ >
62
+ <div class="hk-selection-grid-check">
63
+ {isSelected && <Check size={14} />}
64
+ </div>
65
+ <div class="hk-selection-grid-content">
66
+ <div class="hk-selection-grid-header">
67
+ {ItemIcon && (
68
+ <ItemIcon
69
+ size={16}
70
+ class="hk-selection-grid-icon"
71
+ />
72
+ )}
73
+ <span class="hk-selection-grid-name">
74
+ {item.title}
75
+ </span>
76
+ {item.badge && (
77
+ <span
78
+ class="hk-selection-grid-badge"
79
+ data-variant={item.badgeVariant || undefined}
80
+ >
81
+ {item.badge}
82
+ </span>
83
+ )}
84
+ </div>
85
+ {item.description && (
86
+ <p class="hk-selection-grid-desc">
87
+ {item.description}
88
+ </p>
89
+ )}
90
+ </div>
91
+ </div>
92
+ );
93
+ })}
94
+ </div>
95
+ </div>
96
+ );
97
+ };
98
+ },
99
+ });
@@ -0,0 +1,93 @@
1
+ .hk-selection-waterfall {
2
+ margin-bottom: 1rem;
3
+ }
4
+
5
+ .hk-selection-waterfall-title {
6
+ font-size: 0.6875rem;
7
+ font-weight: 600;
8
+ text-transform: uppercase;
9
+ letter-spacing: 0.05em;
10
+ color: var(--hi-color-text-tertiary, #8b949e);
11
+ margin: 0 0 0.5rem;
12
+ }
13
+
14
+ .hk-selection-waterfall-flow {
15
+ display: flex;
16
+ flex-wrap: wrap;
17
+ gap: 0.375rem;
18
+ }
19
+
20
+ .hk-selection-waterfall-item {
21
+ position: relative;
22
+ display: inline-flex;
23
+ align-items: center;
24
+ gap: 0.25rem;
25
+ padding: 0.25rem 0.625rem;
26
+ border-radius: 9999px;
27
+ border: 1px solid var(--hi-color-border, #30363d);
28
+ font-size: 0.6875rem;
29
+ cursor: pointer;
30
+ transition:
31
+ border-color 200ms ease,
32
+ background 200ms ease,
33
+ box-shadow 200ms ease;
34
+ user-select: none;
35
+ white-space: nowrap;
36
+
37
+ &:hover {
38
+ border-color: var(--hi-color-primary, #58a6ff);
39
+ background: rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.04);
40
+ box-shadow: 0 2px 8px rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.1);
41
+ }
42
+
43
+ &[data-selected] {
44
+ border-color: var(--hi-color-primary, #58a6ff);
45
+ background: rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.1);
46
+ color: var(--hi-color-primary, #58a6ff);
47
+ font-weight: 500;
48
+ }
49
+ }
50
+
51
+ .hk-selection-waterfall-check {
52
+ display: inline-flex;
53
+ align-items: center;
54
+ justify-content: center;
55
+ width: 0.875rem;
56
+ height: 0.875rem;
57
+ border-radius: 50%;
58
+ background: var(--hi-color-primary, #58a6ff);
59
+ color: #fff;
60
+ }
61
+
62
+ .hk-selection-waterfall-name {
63
+ line-height: 1.5;
64
+ }
65
+
66
+ .hk-selection-waterfall-tag {
67
+ font-size: 0.625rem;
68
+ padding: 0 0.25rem;
69
+ border-radius: 9999px;
70
+ background: var(--hi-color-bg-subtle, #21262d);
71
+ color: var(--hi-color-text-tertiary, #8b949e);
72
+
73
+ &[data-variant="success"] {
74
+ background: rgb(from var(--hi-color-success, #3fb950) r g b / 0.12);
75
+ color: var(--hi-color-success, #3fb950);
76
+ }
77
+
78
+ &[data-variant="info"] {
79
+ background: rgb(from var(--hi-color-info, #79c0ff) r g b / 0.12);
80
+ color: var(--hi-color-info, #79c0ff);
81
+ }
82
+
83
+ &[data-variant="primary"] {
84
+ background: rgb(from var(--hi-color-primary, #58a6ff) r g b / 0.12);
85
+ color: var(--hi-color-primary, #58a6ff);
86
+ }
87
+ }
88
+
89
+ .hk-selection-waterfall-desc {
90
+ font-size: 0.625rem;
91
+ color: var(--hi-color-text-tertiary, #8b949e);
92
+ margin-left: 0.25rem;
93
+ }
@@ -0,0 +1,90 @@
1
+ import { defineComponent, type PropType } from "vue";
2
+ import { Check } from "lucide-vue-next";
3
+ import "./HkSelectionWaterfall.scss";
4
+
5
+ export interface WaterfallItem {
6
+ id: string;
7
+ title: string;
8
+ description?: string;
9
+ tag?: string;
10
+ tagVariant?: string;
11
+ image?: string;
12
+ }
13
+
14
+ export default defineComponent({
15
+ name: "HkSelectionWaterfall",
16
+ props: {
17
+ items: { type: Array as PropType<WaterfallItem[]>, required: true },
18
+ selectedIds: {
19
+ type: Array as PropType<string[]>,
20
+ default: () => [],
21
+ },
22
+ multi: { type: Boolean, default: false },
23
+ groupTitle: { type: String, default: undefined },
24
+ minColWidth: { type: Number, default: 140 },
25
+ },
26
+ emits: {
27
+ select: (_item: WaterfallItem) => true,
28
+ },
29
+ setup(props, { emit }) {
30
+ return () => {
31
+ if (!props.items.length) return null;
32
+
33
+ return (
34
+ <div class="hk-selection-waterfall">
35
+ {props.groupTitle && (
36
+ <h4 class="hk-selection-waterfall-title">{props.groupTitle}</h4>
37
+ )}
38
+ <div class="hk-selection-waterfall-flow">
39
+ {props.items.map((item) => {
40
+ const isSelected = props.multi
41
+ ? props.selectedIds.includes(item.id)
42
+ : props.selectedIds.length === 1 &&
43
+ props.selectedIds[0] === item.id;
44
+
45
+ return (
46
+ <div
47
+ key={item.id}
48
+ role="button"
49
+ tabindex={0}
50
+ class="hk-selection-waterfall-item"
51
+ data-selected={isSelected || undefined}
52
+ aria-selected={isSelected}
53
+ onClick={() => emit("select", item)}
54
+ onKeydown={(e) => {
55
+ if (e.key === "Enter" || e.key === " ") {
56
+ e.preventDefault();
57
+ emit("select", item);
58
+ }
59
+ }}
60
+ >
61
+ {isSelected && (
62
+ <span class="hk-selection-waterfall-check">
63
+ <Check size={10} />
64
+ </span>
65
+ )}
66
+ <span class="hk-selection-waterfall-name">
67
+ {item.title}
68
+ </span>
69
+ {item.tag && (
70
+ <span
71
+ class="hk-selection-waterfall-tag"
72
+ data-variant={item.tagVariant || undefined}
73
+ >
74
+ {item.tag}
75
+ </span>
76
+ )}
77
+ {item.description && (
78
+ <span class="hk-selection-waterfall-desc">
79
+ {item.description}
80
+ </span>
81
+ )}
82
+ </div>
83
+ );
84
+ })}
85
+ </div>
86
+ </div>
87
+ );
88
+ };
89
+ },
90
+ });
@@ -0,0 +1,81 @@
1
+ .hk-sidebar {
2
+ flex-shrink: 0;
3
+ height: 100%;
4
+ transition: width var(--hi-duration-normal, 0.3s) ease;
5
+ }
6
+
7
+ .hk-sidebar-panel {
8
+ display: flex;
9
+ flex-direction: column;
10
+ height: 100%;
11
+ background: var(--hk-sidebar-bg, var(--hi-color-surface, rgba(255, 255, 255, 0.95)));
12
+ backdrop-filter: blur(8px);
13
+ border-right: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.08));
14
+ transition: width var(--hi-duration-normal, 0.3s) ease;
15
+ overflow: hidden;
16
+ }
17
+
18
+ .hk-sidebar-header {
19
+ padding: 16px 12px;
20
+ border-bottom: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.06));
21
+ flex-shrink: 0;
22
+ min-height: 56px;
23
+ display: flex;
24
+ align-items: center;
25
+ }
26
+
27
+ .hk-sidebar-body {
28
+ flex: 1;
29
+ min-height: 0;
30
+ overflow-y: auto;
31
+ padding-block: 20px 4px;
32
+ padding-inline: var(--space-12);
33
+
34
+ &::-webkit-scrollbar {
35
+ width: 4px;
36
+ }
37
+
38
+ &::-webkit-scrollbar-track {
39
+ background: transparent;
40
+ }
41
+
42
+ &::-webkit-scrollbar-thumb {
43
+ background: var(--hi-color-border, rgba(0, 0, 0, 0.1));
44
+ border-radius: 2px;
45
+ }
46
+ }
47
+
48
+ .hk-sidebar-footer {
49
+ padding: 16px 10px;
50
+ border-top: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.06));
51
+ flex-shrink: 0;
52
+ }
53
+
54
+ .hk-sidebar[data-collapsed] {
55
+ .hk-sidebar-header {
56
+ justify-content: center;
57
+ padding: 12px 8px;
58
+ }
59
+
60
+ .hk-sidebar-body {
61
+ padding-block: 12px 4px;
62
+ }
63
+
64
+ .hk-sidebar-footer {
65
+ padding: 12px 8px;
66
+ }
67
+ }
68
+
69
+ .hk-sidebar[data-mobile] {
70
+ position: fixed;
71
+ inset: 0;
72
+ z-index: var(--hk-sidebar-z, 900);
73
+ background-color: var(--hk-drawer-overlay-bg, rgba(0, 0, 0, 0.45));
74
+ width: 100%;
75
+
76
+ .hk-sidebar-panel {
77
+ width: 280px;
78
+ box-shadow: 4px 0 24px rgba(0, 0, 0, 0.2);
79
+ border-right: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.08));
80
+ }
81
+ }
@@ -0,0 +1,45 @@
1
+ import { computed, defineComponent } from "vue";
2
+
3
+ import "./HkSidebar.scss";
4
+
5
+ export default defineComponent({
6
+ name: "HkSidebar",
7
+ props: {
8
+ collapsed: { type: Boolean, default: false },
9
+ mobile: { type: Boolean, default: false },
10
+ width: { type: String, default: "280px" },
11
+ },
12
+ emits: {
13
+ close: () => true,
14
+ },
15
+ setup(props, { emit, slots }) {
16
+ const panelWidth = computed(() => (props.collapsed ? "56px" : props.width));
17
+
18
+ return () => (
19
+ <aside
20
+ class="hk-sidebar"
21
+ data-mobile={props.mobile || undefined}
22
+ data-collapsed={props.collapsed || undefined}
23
+ style={props.mobile ? undefined : { width: panelWidth.value }}
24
+ onClick={(e) => {
25
+ if (e.target === e.currentTarget && props.mobile) {
26
+ emit("close");
27
+ }
28
+ }}
29
+ >
30
+ <nav
31
+ class="hk-sidebar-panel"
32
+ style={{ width: props.mobile ? undefined : panelWidth.value }}
33
+ >
34
+ {slots.header ? (
35
+ <header class="hk-sidebar-header">{slots.header()}</header>
36
+ ) : null}
37
+ <div class="hk-sidebar-body">{slots.default?.()}</div>
38
+ {slots.footer ? (
39
+ <footer class="hk-sidebar-footer">{slots.footer()}</footer>
40
+ ) : null}
41
+ </nav>
42
+ </aside>
43
+ );
44
+ },
45
+ });
@@ -0,0 +1,44 @@
1
+ .hk-skeleton {
2
+ display: inline-block;
3
+ background: color-mix(in srgb, var(--hi-color-primary) 8%, transparent);
4
+ }
5
+
6
+ .hk-skeleton[data-tone="muted"] {
7
+ background: color-mix(in srgb, var(--hi-color-text-primary) 12%, transparent);
8
+ }
9
+
10
+ .hk-skeleton[data-animated] {
11
+ background: linear-gradient(
12
+ 90deg,
13
+ color-mix(in srgb, var(--hi-color-primary) 8%, transparent) 25%,
14
+ color-mix(in srgb, var(--hi-color-primary) 18%, transparent) 50%,
15
+ color-mix(in srgb, var(--hi-color-primary) 8%, transparent) 75%
16
+ );
17
+ background-size: 200% 100%;
18
+ animation: hk-skeleton-shimmer 1.5s ease-in-out infinite;
19
+ }
20
+
21
+ .hk-skeleton[data-tone="muted"][data-animated] {
22
+ background: linear-gradient(
23
+ 90deg,
24
+ color-mix(in srgb, var(--hi-color-text-primary) 10%, transparent) 25%,
25
+ color-mix(in srgb, var(--hi-color-text-primary) 22%, transparent) 50%,
26
+ color-mix(in srgb, var(--hi-color-text-primary) 10%, transparent) 75%
27
+ );
28
+ background-size: 200% 100%;
29
+ animation: hk-skeleton-shimmer 1.5s ease-in-out infinite;
30
+ }
31
+
32
+ @keyframes hk-skeleton-shimmer {
33
+ 0% {
34
+ background-position: 200% 0;
35
+ }
36
+ 100% {
37
+ background-position: -200% 0;
38
+ }
39
+ }
40
+
41
+ .hk-skeleton-list {
42
+ display: flex;
43
+ flex-direction: column;
44
+ }
@@ -0,0 +1,30 @@
1
+ import { defineComponent, type PropType } from "vue";
2
+ import "./HkSkeleton.scss";
3
+
4
+ export type SkeletonTone = "primary" | "muted";
5
+
6
+ export default defineComponent({
7
+ name: "HkSkeleton",
8
+ props: {
9
+ width: { type: String, default: "100%" },
10
+ height: { type: String, default: "16px" },
11
+ radius: { type: String, default: "var(--hk-radius-sm, 4px)" },
12
+ animated: { type: Boolean, default: true },
13
+ tone: { type: String as PropType<SkeletonTone>, default: "primary" },
14
+ },
15
+ setup(props) {
16
+ return () => (
17
+ <div
18
+ class="hk-skeleton"
19
+ data-tone={props.tone}
20
+ data-animated={props.animated || undefined}
21
+ style={{
22
+ width: props.width,
23
+ height: props.height,
24
+ borderRadius: props.radius,
25
+ }}
26
+ aria-hidden="true"
27
+ />
28
+ );
29
+ },
30
+ });
@@ -0,0 +1,35 @@
1
+ import { defineComponent, type PropType } from "vue";
2
+ import HSkeleton from "./HkSkeleton";
3
+ import type { SkeletonTone } from "./HkSkeleton";
4
+ import "./HkSkeleton.scss";
5
+
6
+ export default defineComponent({
7
+ name: "HkSkeletonList",
8
+ props: {
9
+ count: { type: Number, default: 3 },
10
+ itemHeight: { type: String, default: "40px" },
11
+ gap: { type: String, default: "8px" },
12
+ tone: { type: String as PropType<SkeletonTone>, default: "primary" },
13
+ },
14
+ setup(props) {
15
+ const items = Array.from({ length: props.count }, (_, i) => i);
16
+
17
+ return () => (
18
+ <div
19
+ class="hk-skeleton-list"
20
+ style={{ gap: props.gap }}
21
+ aria-hidden="true"
22
+ >
23
+ {items.map((i) => (
24
+ <HSkeleton
25
+ key={i}
26
+ width="100%"
27
+ height={props.itemHeight}
28
+ tone={props.tone}
29
+ radius={`var(--hk-radius-sm, 4px)`}
30
+ />
31
+ ))}
32
+ </div>
33
+ );
34
+ },
35
+ });