@skewedaspect/sleekspace-ui 0.8.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/dist/components/Dropdown/SkDropdown.vue.d.ts +9 -1
  2. package/dist/components/Dropdown/types.d.ts +2 -1
  3. package/dist/components/NavBar/SkNavBar.vue.d.ts +9 -1
  4. package/dist/components/NavBar/context.d.ts +2 -0
  5. package/dist/components/NavBar/types.d.ts +5 -1
  6. package/dist/components/NumberInput/SkNumberInput.vue.d.ts +8 -0
  7. package/dist/components/Page/SkPage.vue.d.ts +9 -0
  8. package/dist/components/ScrollArea/SkScrollArea.vue.d.ts +105 -4
  9. package/dist/composables/useCustomColors.d.ts +18 -56
  10. package/{src → dist}/global.d.ts +6 -2
  11. package/dist/sleekspace-ui.css +4257 -1253
  12. package/dist/sleekspace-ui.es.js +300 -170
  13. package/dist/sleekspace-ui.umd.js +299 -169
  14. package/dist/static/classes.d.ts +18 -0
  15. package/dist/static/components/alert.d.ts +12 -0
  16. package/dist/static/components/avatar.d.ts +9 -0
  17. package/dist/static/components/breadcrumbs.d.ts +6 -0
  18. package/dist/static/components/button.d.ts +13 -0
  19. package/dist/static/components/card.d.ts +5 -0
  20. package/dist/static/components/checkbox.d.ts +10 -0
  21. package/dist/static/components/colorPicker.d.ts +8 -0
  22. package/dist/static/components/divider.d.ts +8 -0
  23. package/dist/static/components/dropdown.d.ts +8 -0
  24. package/dist/static/components/field.d.ts +15 -0
  25. package/dist/static/components/group.d.ts +5 -0
  26. package/dist/static/components/input.d.ts +14 -0
  27. package/dist/static/components/navBar.d.ts +16 -0
  28. package/dist/static/components/numberInput.d.ts +15 -0
  29. package/dist/static/components/page.d.ts +9 -0
  30. package/dist/static/components/pagination.d.ts +5 -0
  31. package/dist/static/components/panel.d.ts +11 -0
  32. package/dist/static/components/progress.d.ts +9 -0
  33. package/dist/static/components/radio.d.ts +11 -0
  34. package/dist/static/components/select.d.ts +10 -0
  35. package/dist/static/components/sidebar.d.ts +9 -0
  36. package/dist/static/components/skeleton.d.ts +11 -0
  37. package/dist/static/components/slider.d.ts +12 -0
  38. package/dist/static/components/spinner.d.ts +12 -0
  39. package/dist/static/components/switchInput.d.ts +10 -0
  40. package/dist/static/components/table.d.ts +12 -0
  41. package/dist/static/components/tag.d.ts +8 -0
  42. package/dist/static/components/tagsInput.d.ts +7 -0
  43. package/dist/static/components/textarea.d.ts +12 -0
  44. package/dist/static/components/toolbar.d.ts +12 -0
  45. package/dist/static/components/tooltip.d.ts +7 -0
  46. package/dist/static/escape.d.ts +2 -0
  47. package/dist/static/index.cjs.js +1 -0
  48. package/dist/static/index.d.ts +68 -0
  49. package/dist/static/index.es.js +732 -0
  50. package/dist/static/render.d.ts +12 -0
  51. package/dist/static/specs.d.ts +2 -0
  52. package/dist/static/types.d.ts +43 -0
  53. package/dist/tokens.css +322 -0
  54. package/dist/types/index.d.ts +36 -0
  55. package/dist/utils/slots.d.ts +6 -0
  56. package/docs/guides/installation.md +8 -2
  57. package/docs/guides/pure-css/_meta.yaml +8 -0
  58. package/docs/guides/pure-css/class-api.md +1070 -0
  59. package/docs/guides/pure-css/custom-elements.md +574 -0
  60. package/docs/guides/pure-css/index.md +86 -0
  61. package/docs/guides/pure-css/limitations.md +152 -0
  62. package/docs/guides/pure-css/static-helpers.md +1203 -0
  63. package/llms-full.txt +3739 -261
  64. package/package.json +19 -5
  65. package/src/components/Alert/SkAlert.vue +4 -2
  66. package/src/components/Breadcrumbs/SkBreadcrumbs.vue +6 -12
  67. package/src/components/Button/SkButton.vue +8 -5
  68. package/src/components/Card/SkCard.vue +13 -5
  69. package/src/components/Checkbox/SkCheckbox.vue +9 -2
  70. package/src/components/ContextMenu/SkContextMenuRadioGroup.vue +4 -1
  71. package/src/components/Dropdown/SkDropdown.vue +20 -3
  72. package/src/components/Dropdown/SkDropdownRadioGroup.vue +4 -1
  73. package/src/components/Dropdown/types.ts +2 -1
  74. package/src/components/Modal/SkModal.vue +11 -4
  75. package/src/components/NavBar/SkNavBar.vue +19 -8
  76. package/src/components/NavBar/context.ts +4 -2
  77. package/src/components/NavBar/types.ts +6 -1
  78. package/src/components/NumberInput/SkNumberInput.vue +10 -1
  79. package/src/components/Page/SkPage.vue +29 -15
  80. package/src/components/Panel/SkPanel.vue +2 -1
  81. package/src/components/Popover/SkPopover.vue +11 -4
  82. package/src/components/Radio/SkRadio.vue +9 -2
  83. package/src/components/ScrollArea/SkScrollArea.vue +78 -5
  84. package/src/components/Switch/SkSwitch.vue +14 -13
  85. package/src/components/Tabs/SkTab.vue +7 -2
  86. package/src/components/TreeView/SkTreeItem.vue +10 -2
  87. package/src/components/TreeView/SkTreeView.vue +7 -2
  88. package/src/composables/useCustomColors.ts +86 -77
  89. package/src/composables/usePortalContext.test.ts +0 -2
  90. package/src/shims.d.ts +10 -0
  91. package/src/static/__tests__/parity.test.ts +717 -0
  92. package/src/static/__tests__/parityHarness.test.ts +98 -0
  93. package/src/static/__tests__/parityHarness.ts +260 -0
  94. package/src/static/classes.test.ts +82 -0
  95. package/src/static/classes.ts +111 -0
  96. package/src/static/components/__tests__/helpers.test.ts +837 -0
  97. package/src/static/components/alert.ts +117 -0
  98. package/src/static/components/avatar.ts +86 -0
  99. package/src/static/components/breadcrumbs.ts +28 -0
  100. package/src/static/components/button.ts +75 -0
  101. package/src/static/components/card.ts +27 -0
  102. package/src/static/components/checkbox.ts +48 -0
  103. package/src/static/components/colorPicker.ts +45 -0
  104. package/src/static/components/divider.ts +39 -0
  105. package/src/static/components/dropdown.ts +36 -0
  106. package/src/static/components/field.ts +86 -0
  107. package/src/static/components/group.ts +27 -0
  108. package/src/static/components/input.ts +55 -0
  109. package/src/static/components/navBar.ts +94 -0
  110. package/src/static/components/numberInput.ts +64 -0
  111. package/src/static/components/page.ts +31 -0
  112. package/src/static/components/pagination.ts +27 -0
  113. package/src/static/components/panel.ts +33 -0
  114. package/src/static/components/progress.ts +31 -0
  115. package/src/static/components/radio.ts +53 -0
  116. package/src/static/components/select.ts +51 -0
  117. package/src/static/components/sidebar.ts +85 -0
  118. package/src/static/components/skeleton.ts +66 -0
  119. package/src/static/components/slider.ts +50 -0
  120. package/src/static/components/spinner.ts +94 -0
  121. package/src/static/components/switchInput.ts +49 -0
  122. package/src/static/components/table.ts +88 -0
  123. package/src/static/components/tag.ts +76 -0
  124. package/src/static/components/tagsInput.ts +35 -0
  125. package/src/static/components/textarea.ts +53 -0
  126. package/src/static/components/toolbar.ts +74 -0
  127. package/src/static/components/tooltip.ts +29 -0
  128. package/src/static/escape.test.ts +53 -0
  129. package/src/static/escape.ts +28 -0
  130. package/src/static/generated/defaults.ts +379 -0
  131. package/src/static/generated/propTypes.ts +426 -0
  132. package/src/static/index.ts +116 -0
  133. package/src/static/render.test.ts +83 -0
  134. package/src/static/render.ts +76 -0
  135. package/src/static/specs.test.ts +58 -0
  136. package/src/static/specs.ts +230 -0
  137. package/src/static/types.ts +176 -0
  138. package/src/styles/__tests__/testHelpers.ts +97 -0
  139. package/src/styles/base/_custom-elements.scss +51 -0
  140. package/src/styles/base/_index.scss +4 -0
  141. package/src/styles/components/__tests__/componentSelectors.test.ts +2575 -0
  142. package/src/styles/components/_alert.scss +82 -39
  143. package/src/styles/components/_avatar.scss +102 -47
  144. package/src/styles/components/_breadcrumbs.scss +39 -37
  145. package/src/styles/components/_button.scss +58 -5
  146. package/src/styles/components/_card.scss +64 -2
  147. package/src/styles/components/_checkbox.scss +35 -5
  148. package/src/styles/components/_color-picker.scss +48 -13
  149. package/src/styles/components/_divider.scss +86 -52
  150. package/src/styles/components/_dropdown.scss +214 -0
  151. package/src/styles/components/_field.scss +76 -23
  152. package/src/styles/components/_group.scss +190 -79
  153. package/src/styles/components/_index.scss +1 -0
  154. package/src/styles/components/_input.scss +81 -5
  155. package/src/styles/components/_menu.scss +1 -1
  156. package/src/styles/components/_navbar.scss +76 -45
  157. package/src/styles/components/_number-input.scss +98 -85
  158. package/src/styles/components/_page.scss +82 -23
  159. package/src/styles/components/_pagination.scss +240 -212
  160. package/src/styles/components/_panel.scss +268 -122
  161. package/src/styles/components/_progress.scss +120 -70
  162. package/src/styles/components/_radio.scss +35 -5
  163. package/src/styles/components/_scroll-area.scss +50 -22
  164. package/src/styles/components/_select.scss +40 -9
  165. package/src/styles/components/_sidebar.scss +59 -34
  166. package/src/styles/components/_skeleton.scss +111 -65
  167. package/src/styles/components/_slider.scss +34 -10
  168. package/src/styles/components/_spinner.scss +107 -56
  169. package/src/styles/components/_switch.scss +36 -5
  170. package/src/styles/components/_table.scss +150 -166
  171. package/src/styles/components/_tag.scss +244 -154
  172. package/src/styles/components/_tags-input.scss +46 -12
  173. package/src/styles/components/_textarea.scss +36 -5
  174. package/src/styles/components/_toolbar.scss +85 -31
  175. package/src/styles/components/_tooltip.scss +172 -3
  176. package/src/styles/mixins/_cut-border.scss +18 -4
  177. package/src/styles/mixins/_dual-selector.scss +192 -0
  178. package/src/styles/mixins/_index.scss +1 -0
  179. package/src/styles/mixins/dualSelector.test.ts +151 -0
  180. package/src/styles/themes/_colorful.scss +25 -0
  181. package/src/styles/themes/_greyscale.scss +25 -0
  182. package/src/styles/themes/_shade-scale.scss +39 -0
  183. package/src/styles/tokens/_semantic-color-kinds.scss +66 -0
  184. package/src/{types.ts → types/index.ts} +19 -11
  185. package/src/utils/slots.ts +75 -0
  186. package/web-types.json +980 -137
  187. package/dist/composables/useCustomColors.test.d.ts +0 -1
  188. package/dist/composables/useFocusTrap.test.d.ts +0 -1
  189. package/dist/composables/usePortalContext.test.d.ts +0 -1
  190. package/dist/styles/mixins/fluidSize.test.d.ts +0 -1
  191. package/dist/types.d.ts +0 -29
@@ -4,11 +4,11 @@
4
4
 
5
5
  <template>
6
6
  <div ref="rootRef" :class="classes" :style="customStyles" :data-scheme="theme">
7
- <header v-if="$slots.header" class="sk-page-header">
7
+ <header v-if="hasSlotContent(slots.header)" class="sk-page-header">
8
8
  <slot name="header" />
9
9
  </header>
10
10
 
11
- <div v-if="$slots.subheader" class="sk-page-subheader">
11
+ <div v-if="hasSlotContent(slots.subheader)" class="sk-page-subheader">
12
12
  <slot name="subheader" />
13
13
  </div>
14
14
 
@@ -18,25 +18,25 @@
18
18
  v-if="hasSidebar && !isSidebarDrawerActive"
19
19
  class="sk-page-sidebar"
20
20
  >
21
- <div v-if="$slots['sidebar-header']" class="sk-page-sidebar-header">
21
+ <div v-if="hasSlotContent(slots['sidebar-header'])" class="sk-page-sidebar-header">
22
22
  <slot name="sidebar-header" :is-drawer="false" />
23
23
  </div>
24
24
  <div class="sk-page-sidebar-body">
25
25
  <slot name="sidebar" :is-drawer="false" />
26
26
  </div>
27
- <div v-if="$slots['sidebar-footer']" class="sk-page-sidebar-footer">
27
+ <div v-if="hasSlotContent(slots['sidebar-footer'])" class="sk-page-sidebar-footer">
28
28
  <slot name="sidebar-footer" :is-drawer="false" />
29
29
  </div>
30
30
  </aside>
31
31
 
32
32
  <div class="sk-page-center">
33
- <div v-if="$slots['main-header']" class="sk-page-main-header">
33
+ <div v-if="hasSlotContent(slots['main-header'])" class="sk-page-main-header">
34
34
  <slot name="main-header" />
35
35
  </div>
36
36
  <main class="sk-page-content">
37
37
  <slot />
38
38
  </main>
39
- <div v-if="$slots['main-footer']" class="sk-page-main-footer">
39
+ <div v-if="hasSlotContent(slots['main-footer'])" class="sk-page-main-footer">
40
40
  <slot name="main-footer" />
41
41
  </div>
42
42
  </div>
@@ -46,19 +46,19 @@
46
46
  v-if="hasAside && !isAsideDrawerActive"
47
47
  class="sk-page-aside"
48
48
  >
49
- <div v-if="$slots['aside-header']" class="sk-page-aside-header">
49
+ <div v-if="hasSlotContent(slots['aside-header'])" class="sk-page-aside-header">
50
50
  <slot name="aside-header" :is-drawer="false" />
51
51
  </div>
52
52
  <div class="sk-page-aside-body">
53
53
  <slot name="aside" :is-drawer="false" />
54
54
  </div>
55
- <div v-if="$slots['aside-footer']" class="sk-page-aside-footer">
55
+ <div v-if="hasSlotContent(slots['aside-footer'])" class="sk-page-aside-footer">
56
56
  <slot name="aside-footer" :is-drawer="false" />
57
57
  </div>
58
58
  </aside>
59
59
  </div>
60
60
 
61
- <footer v-if="$slots.footer" class="sk-page-footer">
61
+ <footer v-if="hasSlotContent(slots.footer)" class="sk-page-footer">
62
62
  <slot name="footer" />
63
63
  </footer>
64
64
 
@@ -84,13 +84,13 @@
84
84
  tabindex="-1"
85
85
  @click="onSidebarDrawerClick"
86
86
  >
87
- <div v-if="$slots['sidebar-header']" class="sk-page-sidebar-header">
87
+ <div v-if="hasSlotContent(slots['sidebar-header'])" class="sk-page-sidebar-header">
88
88
  <slot name="sidebar-header" :is-drawer="true" />
89
89
  </div>
90
90
  <div class="sk-page-sidebar-body">
91
91
  <slot name="sidebar" :is-drawer="true" />
92
92
  </div>
93
- <div v-if="$slots['sidebar-footer']" class="sk-page-sidebar-footer">
93
+ <div v-if="hasSlotContent(slots['sidebar-footer'])" class="sk-page-sidebar-footer">
94
94
  <slot name="sidebar-footer" :is-drawer="true" />
95
95
  </div>
96
96
  </aside>
@@ -119,13 +119,13 @@
119
119
  tabindex="-1"
120
120
  @click="onAsideDrawerClick"
121
121
  >
122
- <div v-if="$slots['aside-header']" class="sk-page-aside-header">
122
+ <div v-if="hasSlotContent(slots['aside-header'])" class="sk-page-aside-header">
123
123
  <slot name="aside-header" :is-drawer="true" />
124
124
  </div>
125
125
  <div class="sk-page-aside-body">
126
126
  <slot name="aside" :is-drawer="true" />
127
127
  </div>
128
- <div v-if="$slots['aside-footer']" class="sk-page-aside-footer">
128
+ <div v-if="hasSlotContent(slots['aside-footer'])" class="sk-page-aside-footer">
129
129
  <slot name="aside-footer" :is-drawer="true" />
130
130
  </div>
131
131
  </aside>
@@ -199,6 +199,9 @@
199
199
  import { useFocusTrap } from '@/composables/useFocusTrap';
200
200
  import { providePageDrawers } from '@/composables/usePageDrawer';
201
201
 
202
+ // Utils
203
+ import { hasSlotContent } from '@/utils/slots';
204
+
202
205
  //------------------------------------------------------------------------------------------------------------------
203
206
 
204
207
  export interface SkPageComponentProps
@@ -279,6 +282,15 @@
279
282
  * @default false
280
283
  */
281
284
  flush ?: boolean;
285
+
286
+ /**
287
+ * When true, disables the bouncy overscroll / rubber-band effect on the page's scroll
288
+ * containers (main content, sidebar body, aside body). Applies `overscroll-behavior: none`
289
+ * to every scrollable region owned by SkPage. Note: the document-level bounce from scrolling
290
+ * the page itself (outside SkPage's containers) can only be killed on `html`/`body`.
291
+ * @default false
292
+ */
293
+ noBounce ?: boolean;
282
294
  }
283
295
 
284
296
  //------------------------------------------------------------------------------------------------------------------
@@ -296,6 +308,7 @@
296
308
  asideOpen: undefined,
297
309
  theme: undefined,
298
310
  flush: false,
311
+ noBounce: false,
299
312
  });
300
313
 
301
314
  const emit = defineEmits<{
@@ -322,8 +335,8 @@
322
335
  // Slot-derived state
323
336
  //------------------------------------------------------------------------------------------------------------------
324
337
 
325
- const hasSidebar = computed<boolean>(() => Boolean(slots.sidebar));
326
- const hasAside = computed<boolean>(() => Boolean(slots.aside));
338
+ const hasSidebar = computed<boolean>(() => hasSlotContent(slots.sidebar));
339
+ const hasAside = computed<boolean>(() => hasSlotContent(slots.aside));
327
340
 
328
341
  //------------------------------------------------------------------------------------------------------------------
329
342
  // Responsive mode detection — driven by SkPage's own width, not the viewport, so embedded
@@ -535,6 +548,7 @@
535
548
  'sk-sidebar-drawer-active': isSidebarDrawerActive.value,
536
549
  'sk-aside-drawer-active': isAsideDrawerActive.value,
537
550
  'sk-flush': props.flush,
551
+ 'sk-no-bounce': props.noBounce,
538
552
  }));
539
553
 
540
554
  const customStyles = computed(() =>
@@ -131,7 +131,8 @@
131
131
  const customColorStyles = useCustomColors(
132
132
  'panel',
133
133
  toRef(() => props.baseColor),
134
- toRef(() => props.textColor)
134
+ toRef(() => props.textColor),
135
+ toRef(() => props.borderColor)
135
136
  );
136
137
 
137
138
  //------------------------------------------------------------------------------------------------------------------
@@ -14,7 +14,7 @@
14
14
  :collision-padding="8"
15
15
  >
16
16
  <!-- Header -->
17
- <div v-if="title || $slots.header" class="sk-popover-header">
17
+ <div v-if="title || hasSlotContent(slots.header)" class="sk-popover-header">
18
18
  <h3 v-if="title" class="sk-popover-title">
19
19
  {{ title }}
20
20
  </h3>
@@ -42,12 +42,12 @@
42
42
  </div>
43
43
 
44
44
  <!-- Content -->
45
- <div v-if="$slots.default" class="sk-popover-body">
45
+ <div v-if="hasSlotContent(slots.default)" class="sk-popover-body">
46
46
  <slot />
47
47
  </div>
48
48
 
49
49
  <!-- Footer -->
50
- <div v-if="$slots.footer" class="sk-popover-footer">
50
+ <div v-if="hasSlotContent(slots.footer)" class="sk-popover-footer">
51
51
  <slot name="footer" />
52
52
  </div>
53
53
 
@@ -100,7 +100,7 @@
100
100
  * button pairs or other closing actions.
101
101
  */
102
102
 
103
- import { computed, toRef } from 'vue';
103
+ import { type Slots, computed, toRef, useSlots } from 'vue';
104
104
  import {
105
105
  PopoverArrow,
106
106
  PopoverClose,
@@ -118,6 +118,9 @@
118
118
  import { useCustomColors } from '@/composables/useCustomColors';
119
119
  import { usePortalContext } from '@/composables/usePortalContext';
120
120
 
121
+ // Utils
122
+ import { hasSlotContent } from '@/utils/slots';
123
+
121
124
  //------------------------------------------------------------------------------------------------------------------
122
125
 
123
126
  export interface SkPopoverComponentProps extends ComponentCustomColors
@@ -223,6 +226,10 @@
223
226
 
224
227
  //------------------------------------------------------------------------------------------------------------------
225
228
 
229
+ const slots : Slots = useSlots();
230
+
231
+ //------------------------------------------------------------------------------------------------------------------
232
+
226
233
  const contentClasses = computed(() => ({
227
234
  'sk-popover-content': true,
228
235
  [`sk-${ props.kind }`]: true,
@@ -12,7 +12,7 @@
12
12
  >
13
13
  <RadioGroupIndicator class="sk-radio-indicator" />
14
14
  </RadioGroupItem>
15
- <span v-if="label || $slots.default" class="sk-radio-label">
15
+ <span v-if="label || hasSlotContent(slots.default)" class="sk-radio-label">
16
16
  <slot>{{ label }}</slot>
17
17
  </span>
18
18
  </label>
@@ -46,7 +46,7 @@
46
46
  * formatting, icons, or complex layouts alongside the radio button.
47
47
  */
48
48
 
49
- import { type ComputedRef, computed, inject, toRef } from 'vue';
49
+ import { type ComputedRef, computed, inject, toRef, useSlots } from 'vue';
50
50
  import { RadioGroupIndicator, RadioGroupItem } from 'reka-ui';
51
51
 
52
52
  // Types
@@ -56,6 +56,9 @@
56
56
  // Composables
57
57
  import { useCustomColors } from '@/composables/useCustomColors';
58
58
 
59
+ // Utils
60
+ import { hasSlotContent } from '@/utils/slots';
61
+
59
62
  //------------------------------------------------------------------------------------------------------------------
60
63
 
61
64
  export interface SkRadioComponentProps extends ComponentCustomColors
@@ -110,6 +113,10 @@
110
113
 
111
114
  //------------------------------------------------------------------------------------------------------------------
112
115
 
116
+ const slots = useSlots();
117
+
118
+ //------------------------------------------------------------------------------------------------------------------
119
+
113
120
  // Inject parent kind and size
114
121
  const parentKind = inject<ComputedRef<SkRadioKind | undefined>>('radio-kind', computed(() => undefined));
115
122
  const parentSize = inject<ComputedRef<SkRadioSize | undefined>>('radio-size', computed(() => undefined));
@@ -3,8 +3,8 @@
3
3
  --------------------------------------------------------------------------------------------------------------------->
4
4
 
5
5
  <template>
6
- <ScrollAreaRoot :type="type" :class="classes" :style="customColorStyles">
7
- <ScrollAreaViewport class="sk-scroll-area-viewport">
6
+ <ScrollAreaRoot :type="type" :class="classes" :style="rootStyles">
7
+ <ScrollAreaViewport ref="viewportRef" class="sk-scroll-area-viewport">
8
8
  <slot />
9
9
  </ScrollAreaViewport>
10
10
  <ScrollAreaScrollbar
@@ -34,7 +34,7 @@
34
34
  <!--------------------------------------------------------------------------------------------------------------------->
35
35
 
36
36
  <script setup lang="ts">
37
- import { computed, toRef } from 'vue';
37
+ import { computed, onBeforeUnmount, onMounted, ref, toRef } from 'vue';
38
38
  import {
39
39
  ScrollAreaCorner,
40
40
  ScrollAreaRoot,
@@ -61,9 +61,10 @@
61
61
 
62
62
  /**
63
63
  * When true, fade the scrollable edges with a CSS mask so content visibly tapers
64
- * into/out of the viewport. Applies to whichever axis (or both) is scrollable.
64
+ * into/out of the viewport. Only edges with more content to scroll toward get the
65
+ * fade -- the top fade disappears at scrollTop=0, bottom fade at scroll end, etc.
65
66
  * Override the fade distance via the `--sk-scroll-area-fade` custom property
66
- * (default 1.5rem).
67
+ * (default 1rem).
67
68
  * @default false
68
69
  */
69
70
  fade ?: boolean;
@@ -94,6 +95,78 @@
94
95
  [`sk-${ props.orientation }`]: true,
95
96
  'sk-fade': props.fade,
96
97
  }));
98
+
99
+ //------------------------------------------------------------------------------------------------------------------
100
+ // Scroll-position-aware fade tracking
101
+ //------------------------------------------------------------------------------------------------------------------
102
+
103
+ const viewportRef = ref<InstanceType<typeof ScrollAreaViewport> | null>(null);
104
+
105
+ const fadeTop = ref(0);
106
+ const fadeBottom = ref(0);
107
+ const fadeLeft = ref(0);
108
+ const fadeRight = ref(0);
109
+
110
+ let scrollEl : HTMLElement | null = null;
111
+ let resizeObs : ResizeObserver | null = null;
112
+ let rafID : number | null = null;
113
+
114
+ function updateFades() : void
115
+ {
116
+ if(!scrollEl) { return; }
117
+ const { scrollTop, scrollLeft, scrollHeight, scrollWidth, clientHeight, clientWidth } = scrollEl;
118
+ const threshold = 1;
119
+ fadeTop.value = scrollTop > threshold ? 1 : 0;
120
+ fadeBottom.value = scrollHeight - scrollTop - clientHeight > threshold ? 1 : 0;
121
+ fadeLeft.value = scrollLeft > threshold ? 1 : 0;
122
+ fadeRight.value = scrollWidth - scrollLeft - clientWidth > threshold ? 1 : 0;
123
+ }
124
+
125
+ function scheduleUpdate() : void
126
+ {
127
+ if(rafID !== null) { return; }
128
+ rafID = requestAnimationFrame(() =>
129
+ {
130
+ rafID = null;
131
+ updateFades();
132
+ });
133
+ }
134
+
135
+ onMounted(() =>
136
+ {
137
+ const exposed = viewportRef.value as unknown as { viewportElement ?: HTMLElement } | null;
138
+ scrollEl = exposed?.viewportElement ?? null;
139
+ if(!scrollEl) { return; }
140
+
141
+ scrollEl.addEventListener('scroll', scheduleUpdate, { passive: true });
142
+ resizeObs = new ResizeObserver(scheduleUpdate);
143
+ resizeObs.observe(scrollEl);
144
+ for(const child of Array.from(scrollEl.children))
145
+ {
146
+ resizeObs.observe(child);
147
+ }
148
+ updateFades();
149
+ });
150
+
151
+ onBeforeUnmount(() =>
152
+ {
153
+ if(scrollEl) { scrollEl.removeEventListener('scroll', scheduleUpdate); }
154
+ resizeObs?.disconnect();
155
+ if(rafID !== null) { cancelAnimationFrame(rafID); }
156
+ });
157
+
158
+ const rootStyles = computed(() =>
159
+ {
160
+ const styles : Record<string, string> = { ...customColorStyles.value };
161
+ if(props.fade)
162
+ {
163
+ styles['--sk-scroll-fade-top'] = String(fadeTop.value);
164
+ styles['--sk-scroll-fade-bottom'] = String(fadeBottom.value);
165
+ styles['--sk-scroll-fade-left'] = String(fadeLeft.value);
166
+ styles['--sk-scroll-fade-right'] = String(fadeRight.value);
167
+ }
168
+ return styles;
169
+ });
97
170
  </script>
98
171
 
99
172
  <!--------------------------------------------------------------------------------------------------------------------->
@@ -18,17 +18,17 @@
18
18
  </SwitchRoot>
19
19
  <label v-if="showLabel" class="sk-switch-label">
20
20
  <!-- Default slot overrides everything -->
21
- <slot v-if="slots.default" />
21
+ <slot v-if="hasSlotContent(slots.default)" />
22
22
  <!-- If using on/off labels with animation -->
23
23
  <Transition v-else-if="useOnOffLabels && !disableLabelAnimation" name="sk-switch-label-fade" mode="out-in">
24
24
  <span v-if="modelValue" :key="'on'">
25
- <slot v-if="slots['label-on']" name="label-on" />
25
+ <slot v-if="hasSlotContent(slots['label-on'])" name="label-on" />
26
26
  <template v-else>
27
27
  {{ labelOn }}
28
28
  </template>
29
29
  </span>
30
30
  <span v-else :key="'off'">
31
- <slot v-if="slots['label-off']" name="label-off" />
31
+ <slot v-if="hasSlotContent(slots['label-off'])" name="label-off" />
32
32
  <template v-else>
33
33
  {{ labelOff }}
34
34
  </template>
@@ -37,13 +37,13 @@
37
37
  <!-- If using on/off labels without animation -->
38
38
  <template v-else-if="useOnOffLabels && disableLabelAnimation">
39
39
  <span v-if="modelValue">
40
- <slot v-if="slots['label-on']" name="label-on" />
40
+ <slot v-if="hasSlotContent(slots['label-on'])" name="label-on" />
41
41
  <template v-else>
42
42
  {{ labelOn }}
43
43
  </template>
44
44
  </span>
45
45
  <span v-else>
46
- <slot v-if="slots['label-off']" name="label-off" />
46
+ <slot v-if="hasSlotContent(slots['label-off'])" name="label-off" />
47
47
  <template v-else>
48
48
  {{ labelOff }}
49
49
  </template>
@@ -88,6 +88,9 @@
88
88
  // Composables
89
89
  import { useCustomColors } from '@/composables/useCustomColors';
90
90
 
91
+ // Utils
92
+ import { hasSlotContent } from '@/utils/slots';
93
+
91
94
  //------------------------------------------------------------------------------------------------------------------
92
95
 
93
96
  export interface SkSwitchComponentProps extends ComponentCustomColors
@@ -232,14 +235,12 @@
232
235
  // - labelOn/labelOff props are provided, OR
233
236
  // - label-on/label-off slots are provided, OR
234
237
  // - fallback label prop is provided
235
- return Boolean(
236
- slots.default
237
- || props.labelOn
238
- || props.labelOff
239
- || slots['label-on']
240
- || slots['label-off']
241
- || props.label
242
- );
238
+ return hasSlotContent(slots.default)
239
+ || Boolean(props.labelOn)
240
+ || Boolean(props.labelOff)
241
+ || hasSlotContent(slots['label-on'])
242
+ || hasSlotContent(slots['label-off'])
243
+ || Boolean(props.label);
243
244
  });
244
245
 
245
246
  const useOnOffLabels = computed(() =>
@@ -8,7 +8,7 @@
8
8
  :disabled="disabled"
9
9
  :class="classes"
10
10
  >
11
- <span v-if="$slots.icon" class="sk-tab-icon">
11
+ <span v-if="hasSlotContent(slots.icon)" class="sk-tab-icon">
12
12
  <slot name="icon" />
13
13
  </span>
14
14
  <span class="sk-tab-label">
@@ -47,9 +47,10 @@
47
47
  * @slot icon - Optional icon displayed before the tab label. Useful for visual indicators.
48
48
  */
49
49
 
50
- import { type ComputedRef, computed, inject } from 'vue';
50
+ import { type ComputedRef, type Slots, computed, inject, useSlots } from 'vue';
51
51
  import { TabsTrigger } from 'reka-ui';
52
52
  import type { ComponentKind } from '@/types';
53
+ import { hasSlotContent } from '@/utils/slots';
53
54
 
54
55
  //------------------------------------------------------------------------------------------------------------------
55
56
 
@@ -93,6 +94,10 @@
93
94
 
94
95
  //------------------------------------------------------------------------------------------------------------------
95
96
 
97
+ const slots : Slots = useSlots();
98
+
99
+ //------------------------------------------------------------------------------------------------------------------
100
+
96
101
  // Inject parent kind (Sleekspace-specific for theming)
97
102
  const parentKind = inject<ComputedRef<ComponentKind | undefined>>('tabs-kind', computed(() => undefined));
98
103
 
@@ -32,7 +32,7 @@
32
32
  <div v-else-if="showChevron" class="sk-tree-item-chevron-spacer" />
33
33
 
34
34
  <!-- Leading slot (icons, checkboxes, etc.) -->
35
- <div v-if="$slots.leading" class="sk-tree-item-leading">
35
+ <div v-if="hasSlotContent(slots.leading)" class="sk-tree-item-leading">
36
36
  <slot name="leading" :is-expanded="isExpanded" :is-selected="isSelected" />
37
37
  </div>
38
38
 
@@ -47,7 +47,7 @@
47
47
  </div>
48
48
 
49
49
  <!-- Trailing slot -->
50
- <div v-if="$slots.trailing" class="sk-tree-item-trailing">
50
+ <div v-if="hasSlotContent(slots.trailing)" class="sk-tree-item-trailing">
51
51
  <slot name="trailing" :is-expanded="isExpanded" :is-selected="isSelected" />
52
52
  </div>
53
53
  </TreeItem>
@@ -62,8 +62,12 @@
62
62
  <!--------------------------------------------------------------------------------------------------------------------->
63
63
 
64
64
  <script setup lang="ts">
65
+ import { type Slots, useSlots } from 'vue';
65
66
  import { type FlattenedItem, TreeItem } from 'reka-ui';
66
67
 
68
+ // Utils
69
+ import { hasSlotContent } from '@/utils/slots';
70
+
67
71
  //------------------------------------------------------------------------------------------------------------------
68
72
 
69
73
  export interface SkTreeItemComponentProps
@@ -79,6 +83,10 @@
79
83
  disabled: false,
80
84
  showChevron: true,
81
85
  });
86
+
87
+ //------------------------------------------------------------------------------------------------------------------
88
+
89
+ const slots : Slots = useSlots();
82
90
  </script>
83
91
 
84
92
  <!--------------------------------------------------------------------------------------------------------------------->
@@ -6,13 +6,13 @@
6
6
  <TreeRoot
7
7
  :items="items"
8
8
  :get-key="getKey"
9
- :model-value="modelValue"
9
+ :model-value="rekaModelValue"
10
10
  :multiple="multiple"
11
11
  :propagate-select="propagateSelect"
12
12
  :default-expanded="expandedKeys"
13
13
  :class="classes"
14
14
  :style="customColorStyles"
15
- @update:model-value="$emit('update:modelValue', $event as T | T[])"
15
+ @update:model-value="$emit('update:modelValue', $event as unknown as T | T[])"
16
16
  >
17
17
  <template #default="{ flattenItems }">
18
18
  <slot :flatten-items="flattenItems" />
@@ -73,6 +73,11 @@
73
73
 
74
74
  //------------------------------------------------------------------------------------------------------------------
75
75
 
76
+ type RekaTreeValue = Record<string, unknown> | Record<string, unknown>[];
77
+ const rekaModelValue = computed(() => props.modelValue as unknown as RekaTreeValue);
78
+
79
+ //------------------------------------------------------------------------------------------------------------------
80
+
76
81
  function getAllKeys(items : T[]) : string[]
77
82
  {
78
83
  const keys : string[] = [];