@dolanske/vui 0.5.0 → 1.0.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 (178) hide show
  1. package/README.md +5 -4
  2. package/dist/components/Accordion/Accordion.vue.d.ts +3 -2
  3. package/dist/components/Accordion/AccordionGroup.vue.d.ts +5 -2
  4. package/dist/components/Alert/Alert.vue.d.ts +10 -3
  5. package/dist/components/Avatar/Avatar.vue.d.ts +16 -1
  6. package/dist/components/Badge/Badge.vue.d.ts +4 -3
  7. package/dist/components/Breadcrumbs/BreadcrumbItem.vue.d.ts +4 -3
  8. package/dist/components/Breadcrumbs/Breadcrumbs.vue.d.ts +3 -2
  9. package/dist/components/Button/Button.vue.d.ts +8 -17
  10. package/dist/components/ButtonGroup/ButtonGroup.vue.d.ts +5 -2
  11. package/dist/components/Calendar/Calendar.vue.d.ts +7 -7
  12. package/dist/components/Card/Card.vue.d.ts +4 -3
  13. package/dist/components/Checkbox/Checkbox.vue.d.ts +8 -6
  14. package/dist/components/CopyClipboard/CopyClipboard.vue.d.ts +2 -1
  15. package/dist/components/Divider/Divider.vue.d.ts +3 -2
  16. package/dist/components/Drawer/Drawer.vue.d.ts +6 -5
  17. package/dist/components/Dropdown/Dropdown.vue.d.ts +84 -6
  18. package/dist/components/Dropdown/DropdownItem.vue.d.ts +3 -2
  19. package/dist/components/Dropdown/DropdownTitle.vue.d.ts +6 -2
  20. package/dist/components/Flex/Flex.vue.d.ts +3 -2
  21. package/dist/components/Grid/Grid.vue.d.ts +7 -3
  22. package/dist/components/Input/Color.vue.d.ts +11 -0
  23. package/dist/components/Input/Counter.vue.d.ts +5 -5
  24. package/dist/components/Input/Dropzone.vue.d.ts +96 -11
  25. package/dist/components/Input/File.vue.d.ts +4 -3
  26. package/dist/components/Input/Input.vue.d.ts +9 -8
  27. package/dist/components/Input/Password.vue.d.ts +1 -1
  28. package/dist/components/Input/Textarea.vue.d.ts +7 -6
  29. package/dist/components/Kbd/Kbd.vue.d.ts +1 -1
  30. package/dist/components/Kbd/KbdGroup.vue.d.ts +5 -12
  31. package/dist/components/Modal/Confirm.vue.d.ts +7 -6
  32. package/dist/components/Modal/Modal.vue.d.ts +7 -6
  33. package/dist/components/OTP/OTP.vue.d.ts +7 -6
  34. package/dist/components/OTP/OTPItem.vue.d.ts +1 -1
  35. package/dist/components/Pagination/Pagination.vue.d.ts +6 -2
  36. package/dist/components/Popout/Popout.vue.d.ts +11 -3
  37. package/dist/components/Progress/Progress.vue.d.ts +7 -5
  38. package/dist/components/Radio/Radio.vue.d.ts +8 -6
  39. package/dist/components/Radio/RadioGroup.vue.d.ts +7 -6
  40. package/dist/components/Select/Select.vue.d.ts +6 -8
  41. package/dist/components/Sheet/Sheet.vue.d.ts +9 -5
  42. package/dist/components/Sidebar/Sidebar.vue.d.ts +70 -0
  43. package/dist/components/Skeleton/Skeleton.vue.d.ts +1 -1
  44. package/dist/components/Spinner/Spinner.vue.d.ts +1 -1
  45. package/dist/components/Switch/Switch.vue.d.ts +8 -6
  46. package/dist/components/Table/Cell.vue.d.ts +5 -2
  47. package/dist/components/Table/{Header.vue.d.ts → Head.vue.d.ts} +3 -2
  48. package/dist/components/Table/{Table.vue.d.ts → Root.vue.d.ts} +3 -2
  49. package/dist/components/Table/index.d.ts +6 -0
  50. package/dist/components/Table/table.d.ts +3 -3
  51. package/dist/components/Tabs/Tab.vue.d.ts +17 -3
  52. package/dist/components/Tabs/Tabs.vue.d.ts +8 -7
  53. package/dist/components/Toast/toast.d.ts +245 -0
  54. package/dist/components/Tooltip/Tooltip.vue.d.ts +2 -1
  55. package/dist/index.d.ts +5 -7
  56. package/dist/internal/Backdrop/Backdrop.vue.d.ts +3 -2
  57. package/dist/shared/helpers.d.ts +9 -0
  58. package/dist/shared/theme.d.ts +3 -0
  59. package/dist/vui.css +1 -0
  60. package/dist/vui.js +7160 -6355
  61. package/package.json +22 -18
  62. package/src/App.vue +90 -171
  63. package/src/components/Accordion/accordion.scss +1 -0
  64. package/src/components/Alert/Alert.vue +11 -5
  65. package/src/components/Alert/alert.scss +104 -23
  66. package/src/components/Avatar/Avatar.vue +4 -1
  67. package/src/components/Avatar/avatar.scss +1 -1
  68. package/src/components/Badge/Badge.vue +1 -1
  69. package/src/components/Badge/badge.scss +134 -17
  70. package/src/components/Breadcrumbs/BreadcrumbItem.vue +2 -2
  71. package/src/components/Breadcrumbs/Breadcrumbs.vue +1 -2
  72. package/src/components/Breadcrumbs/breadcrumbs.scss +2 -1
  73. package/src/components/Button/Button.vue +15 -20
  74. package/src/components/Button/button.scss +156 -55
  75. package/src/components/ButtonGroup/ButtonGroup.vue +4 -1
  76. package/src/components/ButtonGroup/button-group.scss +2 -2
  77. package/src/components/Calendar/Calendar.vue +4 -1
  78. package/src/components/Calendar/calendar.scss +25 -2
  79. package/src/components/Card/Card.vue +2 -2
  80. package/src/components/Card/card.scss +4 -4
  81. package/src/components/Checkbox/Checkbox.vue +4 -1
  82. package/src/components/Checkbox/checkbox.scss +17 -12
  83. package/src/components/CopyClipboard/CopyClipboard.vue +15 -6
  84. package/src/components/CopyClipboard/copy-clipboard.scss +10 -2
  85. package/src/components/Drawer/Drawer.vue +4 -4
  86. package/src/components/Drawer/drawer.scss +1 -0
  87. package/src/components/Dropdown/Dropdown.vue +44 -20
  88. package/src/components/Dropdown/DropdownItem.vue +4 -4
  89. package/src/components/Dropdown/DropdownTitle.vue +7 -1
  90. package/src/components/Dropdown/dropdown-item.scss +9 -2
  91. package/src/components/Dropdown/dropdown.scss +21 -7
  92. package/src/components/Grid/Grid.vue +21 -1
  93. package/src/components/Input/Color.vue +26 -0
  94. package/src/components/Input/Counter.vue +12 -16
  95. package/src/components/Input/Dropzone.vue +1 -1
  96. package/src/components/Input/File.vue +1 -1
  97. package/src/components/Input/Input.vue +8 -6
  98. package/src/components/Input/Password.vue +1 -13
  99. package/src/components/Input/Textarea.vue +4 -2
  100. package/src/components/Input/input.scss +110 -16
  101. package/src/components/Kbd/KbdGroup.vue +2 -6
  102. package/src/components/Kbd/kbd.scss +6 -5
  103. package/src/components/Modal/Confirm.vue +1 -1
  104. package/src/components/Modal/Modal.vue +23 -15
  105. package/src/components/Modal/modal.scss +11 -6
  106. package/src/components/OTP/otp.scss +8 -7
  107. package/src/components/Pagination/Pagination.vue +6 -3
  108. package/src/components/Popout/Popout.vue +15 -5
  109. package/src/components/Popout/popout.scss +8 -1
  110. package/src/components/Progress/Progress.vue +18 -5
  111. package/src/components/Progress/progress.scss +7 -1
  112. package/src/components/Radio/Radio.vue +4 -2
  113. package/src/components/Radio/radio.scss +18 -8
  114. package/src/components/Select/Select.vue +49 -18
  115. package/src/components/Select/select.scss +35 -2
  116. package/src/components/Sheet/Sheet.vue +8 -2
  117. package/src/components/Sheet/sheet.scss +9 -0
  118. package/src/components/Sidebar/Sidebar.vue +24 -11
  119. package/src/components/Sidebar/sidebar.scss +6 -5
  120. package/src/components/Spinner/spinner.scss +2 -1
  121. package/src/components/Switch/Switch.vue +4 -3
  122. package/src/components/Switch/switch.scss +39 -6
  123. package/src/components/Table/{Header.vue → Head.vue} +5 -5
  124. package/src/components/Table/{Table.vue → Root.vue} +2 -2
  125. package/src/components/Table/SelectRow.vue +2 -1
  126. package/src/components/Table/index.ts +7 -0
  127. package/src/components/Table/table.scss +25 -5
  128. package/src/components/Table/table.ts +7 -3
  129. package/src/components/Tabs/Tab.vue +7 -9
  130. package/src/components/Tabs/Tabs.vue +5 -4
  131. package/src/components/Tabs/tabs.scss +10 -3
  132. package/src/components/Toast/Toasts.vue +5 -0
  133. package/src/components/Toast/toast.scss +6 -2
  134. package/src/components/Toast/toast.ts +7 -0
  135. package/src/components/Tooltip/Tooltip.vue +9 -9
  136. package/src/components/Tooltip/tooltip.scss +4 -0
  137. package/src/examples/ExampleAccordions.vue +58 -0
  138. package/src/examples/ExampleAlerts.vue +78 -0
  139. package/src/examples/ExampleAvatars.vue +44 -0
  140. package/src/examples/ExampleBadges.vue +48 -0
  141. package/src/examples/ExampleBreadcrumbs.vue +46 -0
  142. package/src/examples/ExampleButtons.vue +140 -0
  143. package/src/examples/ExampleCalendars.vue +40 -0
  144. package/src/examples/ExampleCards.vue +94 -0
  145. package/src/examples/ExampleCheckboxes.vue +123 -0
  146. package/src/examples/ExampleCopyClipboard.vue +47 -0
  147. package/src/examples/ExampleDividers.vue +39 -0
  148. package/src/examples/ExampleDrawers.vue +67 -0
  149. package/src/examples/ExampleDropdowns.vue +114 -0
  150. package/src/examples/ExampleFlexGrid.vue +122 -0
  151. package/src/examples/ExampleInputs.vue +234 -0
  152. package/src/examples/ExampleKBD.vue +65 -0
  153. package/src/examples/ExampleModals.vue +143 -0
  154. package/src/examples/ExamplePalette.vue +159 -0
  155. package/src/examples/ExamplePopouts.vue +41 -0
  156. package/src/examples/ExampleSheets.vue +77 -0
  157. package/src/examples/ExampleSidebars.vue +270 -0
  158. package/src/examples/ExampleSkeletons.vue +26 -0
  159. package/src/examples/ExampleSpinners.vue +78 -0
  160. package/src/examples/ExampleTables.vue +195 -0
  161. package/src/examples/ExampleTabs.vue +119 -0
  162. package/src/examples/ExampleToasts.vue +96 -0
  163. package/src/examples/ExampleTooltips.vue +70 -0
  164. package/src/examples/shared/ExampleColor.vue +28 -0
  165. package/src/index.ts +8 -11
  166. package/src/internal/Backdrop/backdrop.scss +7 -1
  167. package/src/shared/helpers.ts +43 -0
  168. package/src/shared/theme.ts +22 -0
  169. package/src/style/animation.scss +1 -0
  170. package/src/style/core.scss +30 -55
  171. package/src/style/layout.scss +74 -9
  172. package/src/style/text.scss +18 -0
  173. package/src/style/theme.scss +195 -0
  174. package/src/style/tooltip.scss +22 -4
  175. package/src/style/typography.scss +95 -18
  176. package/dist/components/Table/Row.vue.d.ts +0 -16
  177. package/dist/style.css +0 -1
  178. package/src/components/Table/Row.vue +0 -9
@@ -1,6 +1,7 @@
1
1
  <script setup lang='ts'>
2
- import { computed, onMounted, useTemplateRef, watchEffect } from 'vue'
3
- import { delay, formatUnitValue, isNil, randomMinMax } from '../../shared/helpers'
2
+ import { whenever } from '@vueuse/core'
3
+ import { computed, onMounted, useTemplateRef, watch, watchEffect } from 'vue'
4
+ import { clamp, delay, formatUnitValue, isNil, randomMinMax } from '../../shared/helpers'
4
5
  import './progress.scss'
5
6
 
6
7
  interface Props {
@@ -30,10 +31,14 @@ const {
30
31
  height,
31
32
  } = defineProps<Props>()
32
33
 
34
+ const emit = defineEmits<{
35
+ done: []
36
+ }>()
37
+
33
38
  const progressAmount = defineModel<number>({
34
39
  default: 0,
35
40
  set(value) {
36
- return Math.min(value, 100)
41
+ return clamp(0, 100, value)
37
42
  },
38
43
  })
39
44
 
@@ -49,6 +54,8 @@ watchEffect(() => {
49
54
  }
50
55
  })
51
56
 
57
+ whenever(() => fake, fakeIncrement)
58
+
52
59
  // Automatically / randomly increment but never reach 100% until
53
60
  async function fakeIncrement() {
54
61
  if (fake && progressAmount.value < 100) {
@@ -59,15 +66,21 @@ async function fakeIncrement() {
59
66
  }
60
67
  else {
61
68
  progressAmount.value += randomMinMax(1, 10)
62
- await delay(randomMinMax(200, 1000))
69
+ await delay(randomMinMax(200, 12000))
63
70
  }
64
71
  fakeIncrement()
65
72
  }
66
73
  }
67
74
 
75
+ watch(progressAmount, (v) => {
76
+ if (v >= 100) {
77
+ emit('done')
78
+ }
79
+ })
80
+
68
81
  onMounted(fakeIncrement)
69
82
 
70
- const w = computed(() => `${progressAmount.value}%`)
83
+ const w = computed(() => `${clamp(0, 100, progressAmount.value)}%`)
71
84
  const bC = computed(() => color)
72
85
  </script>
73
86
 
@@ -5,7 +5,7 @@
5
5
  width: 100%;
6
6
  position: relative;
7
7
  border-radius: 999px;
8
- background-color: var(--color-bg-lowered);
8
+ background-color: var(--color-bg-raised);
9
9
  overflow: hidden;
10
10
  height: var(--vui-progress-height);
11
11
 
@@ -39,3 +39,9 @@
39
39
  transition: var(--transition-slow);
40
40
  }
41
41
  }
42
+
43
+ :root.light {
44
+ .vui-progress-indicator {
45
+ background-color: var(--);
46
+ }
47
+ }
@@ -7,12 +7,14 @@ export interface RadioProps {
7
7
  label?: string
8
8
  disabled?: boolean
9
9
  value: any
10
+ accent?: boolean
10
11
  }
11
12
 
12
13
  const {
13
14
  label,
14
15
  disabled,
15
16
  value,
17
+ accent,
16
18
  } = defineProps<RadioProps>()
17
19
  const slots = defineSlots()
18
20
  const checked = defineModel()
@@ -21,8 +23,8 @@ const isChecked = computed(() => value === checked.value)
21
23
  </script>
22
24
 
23
25
  <template>
24
- <div class="vui-radio" :class="{ disabled: !!disabled, checked: isChecked }">
25
- <input :id v-model="checked" type="radio" :value :checked="isChecked">
26
+ <div class="vui-radio" :class="{ disabled: !!disabled, checked: isChecked, accent }">
27
+ <input :id v-model="checked" type="radio" :value :checked="isChecked" :disabled>
26
28
  <label :for="id">
27
29
  <span class="vui-radio-icon">
28
30
  <Icon :icon="isChecked ? 'ph:radio-button-fill' : 'ph:circle'" />
@@ -5,18 +5,21 @@
5
5
  .vui-radio-icon svg {
6
6
  color: var(--color-text);
7
7
  }
8
+
9
+ &.accent .vui-radio-icon svg {
10
+ color: var(--color-accent);
11
+ }
8
12
  }
9
13
 
10
14
  &.disabled {
11
- cursor: not-allowed;
12
- pointer-events: none;
13
-
14
- .vui-radio-icon svg path {
15
- color: var(--color-text-lighter) !important;
15
+ .vui-radio-icon {
16
+ opacity: 0.25;
17
+ cursor: not-allowed;
16
18
  }
17
19
 
18
- input + label p {
19
- color: var(--color-text-lighter);
20
+ input + label .vui-radio-content {
21
+ opacity: 0.4;
22
+ cursor: not-allowed;
20
23
  }
21
24
  }
22
25
 
@@ -38,6 +41,7 @@
38
41
  position: absolute;
39
42
  overflow: hidden;
40
43
  outline: none !important;
44
+ opacity: 0;
41
45
 
42
46
  &:focus-visible + label .vui-radio-icon {
43
47
  outline: 2px solid var(--color-text);
@@ -59,10 +63,16 @@
59
63
  display: flex;
60
64
  align-items: center;
61
65
  min-height: var(--radio-size);
62
- font-size: var(--font-size-ms);
66
+ font-size: var(--font-size-m);
63
67
  // line-height: var(--radio-size);
64
68
  }
65
69
  }
66
70
  }
67
71
  }
68
72
  }
73
+
74
+ :root.light {
75
+ .vui-radio.checked.accent .vui-radio-icon svg {
76
+ color: var(--color-accent);
77
+ }
78
+ }
@@ -1,7 +1,7 @@
1
1
  <!-- eslint-disable ts/consistent-type-definitions -->
2
2
  <script setup lang='ts' generic="T">
3
3
  import { Icon } from '@iconify/vue'
4
- import { computed, onMounted, ref, useTemplateRef } from 'vue'
4
+ import { computed, onMounted, ref, useId, useTemplateRef } from 'vue'
5
5
  import { searchString } from '../../shared/helpers'
6
6
  import Button from '../Button/Button.vue'
7
7
  import Dropdown from '../Dropdown/Dropdown.vue'
@@ -28,6 +28,8 @@ type Props = {
28
28
  search?: boolean
29
29
  maxActiveOptions?: number
30
30
  showClear?: boolean
31
+ disabled?: boolean
32
+ errors?: string[]
31
33
  }
32
34
 
33
35
  const {
@@ -39,11 +41,14 @@ const {
39
41
  options,
40
42
  single = true,
41
43
  search,
42
- maxActiveOptions = 3,
44
+ maxActiveOptions,
43
45
  showClear,
46
+ disabled,
47
+ errors = [] as string[],
44
48
  } = defineProps<Props>()
45
49
 
46
50
  const selected = defineModel<SelectOption[] | undefined>()
51
+ const trigger = useTemplateRef('trigger')
47
52
 
48
53
  //
49
54
  function setValue(option: SelectOption) {
@@ -68,7 +73,7 @@ function setValue(option: SelectOption) {
68
73
  if (!selected.value) {
69
74
  selected.value = [option]
70
75
  }
71
- else {
76
+ else if (!maxActiveOptions || (selected.value.length < maxActiveOptions)) {
72
77
  selected.value?.push(option)
73
78
  }
74
79
  }
@@ -97,7 +102,7 @@ const renderPlaceholder = computed(() => {
97
102
  return selected.value[0].label
98
103
 
99
104
  // If amount of selected exceeds the active capacity
100
- if (selected.value.length > maxActiveOptions) {
105
+ if (selected.value.length > 3) {
101
106
  return `${selected.value.length} selected`
102
107
  }
103
108
 
@@ -117,42 +122,59 @@ function clearValue() {
117
122
  selected.value = undefined
118
123
  dropdownRef.value?.close()
119
124
  }
125
+
126
+ const id = useId()
120
127
  </script>
121
128
 
122
129
  <template>
123
- <div class="vui-input-container vui-select" :class="{ expand, required, readonly }">
124
- <Dropdown ref="dropdown" :expand>
130
+ <div class="vui-input-container vui-select" :class="{ expand, required, readonly, disabled, 'has-errors': errors.length > 0 }">
131
+ <Dropdown ref="dropdown" :expand @close="trigger?.focus({ preventScroll: true })">
125
132
  <template #trigger="{ toggle, isOpen }">
126
133
  <div class="vui-input vui-select-trigger-content">
127
- <label v-if="label" for="id">{{ label }}</label>
134
+ <label v-if="label" :for="id">{{ label }}</label>
128
135
  <p v-if="hint" class="vui-input-hint">
129
136
  {{ hint }}
130
137
  </p>
131
138
 
132
- <button class="vui-input-style vui-select-trigger-container" @click="toggle">
139
+ <button
140
+ :id
141
+ ref="trigger"
142
+ class="vui-input-style vui-select-trigger-container"
143
+ :class="{ 'has-value': selected && selected.length > 0 }"
144
+ :disabled
145
+ @click="toggle"
146
+ >
133
147
  <span>
134
148
  {{ renderPlaceholder }}
135
149
  </span>
136
- <Button
137
- v-if="showClear && !required && selected"
138
- plain
139
- icon="ph:x"
140
- square
141
- size="s"
142
- @click.stop="clearValue"
143
- />
150
+ <template v-if="showClear && !required && selected">
151
+ <div class="flex-1" />
152
+ <Button
153
+ plain
154
+ icon="ph:x"
155
+ square
156
+ size="s"
157
+
158
+ @click.stop="clearValue"
159
+ />
160
+ </template>
144
161
  <Icon :icon="isOpen ? 'ph:caret-up' : 'ph:caret-down'" />
145
162
  </button>
146
163
  </div>
147
164
  </template>
148
165
 
149
166
  <template #default="{ close, isOpen }">
150
- <DropdownTitle v-if="search">
167
+ <DropdownTitle v-if="search" sticky>
151
168
  <Input
152
169
  v-model="searchStr"
153
170
  placeholder="Search..."
154
171
  :focus="isOpen"
155
- />
172
+ expand
173
+ >
174
+ <template #start>
175
+ <Icon icon="ph:magnifying-glass" />
176
+ </template>
177
+ </Input>
156
178
  </DropdownTitle>
157
179
 
158
180
  <p v-if="filteredOptions.length === 0" class="vue-select-no-results">
@@ -176,5 +198,14 @@ function clearValue() {
176
198
  </DropdownItem>
177
199
  </template>
178
200
  </Dropdown>
201
+
202
+ <p v-if="maxActiveOptions && !single" class="vui-input-limit">
203
+ {{ `${selected ? selected.length : 0}/${maxActiveOptions}` }}
204
+ </p>
205
+ <ul v-if="errors.length > 0" class="vui-input-errors">
206
+ <li v-for="err in errors" :key="err">
207
+ {{ err }}
208
+ </li>
209
+ </ul>
179
210
  </div>
180
211
  </template>
@@ -1,5 +1,7 @@
1
1
  .vui-input-container {
2
2
  &.vui-select {
3
+ width: auto;
4
+
3
5
  &.expand {
4
6
  .vui-dropdown-trigger-wrap,
5
7
  .vui-dropdown-trigger-content {
@@ -7,7 +9,22 @@
7
9
  }
8
10
  }
9
11
 
12
+ &.disabled {
13
+ .vui-input-style {
14
+ cursor: not-allowed;
15
+
16
+ &:hover {
17
+ border-color: var(--color-border);
18
+ }
19
+ }
20
+ }
21
+
10
22
  .vui-select-trigger-container {
23
+ &.has-value span {
24
+ color: var(--color-text);
25
+ font-weight: var(--font-weight-medium);
26
+ }
27
+
11
28
  span {
12
29
  white-space: nowrap;
13
30
  text-overflow: ellipsis;
@@ -30,8 +47,6 @@
30
47
  margin-right: -6px;
31
48
 
32
49
  .vui-button-slot svg {
33
- width: 14px;
34
- height: 14px;
35
50
  color: var(--color-text-light);
36
51
  }
37
52
  }
@@ -42,3 +57,21 @@
42
57
  }
43
58
  }
44
59
  }
60
+
61
+ select {
62
+ display: block;
63
+ height: var(--interactive-el-height);
64
+ line-height: var(--interactive-el-height);
65
+ background-color: var(--color-bg);
66
+ border: 1px solid var(--color-border);
67
+ border-radius: var(--border-radius-s);
68
+ padding-inline: var(--space-xs);
69
+ transition: var(--transition-fast);
70
+ z-index: 1;
71
+ font-size: var(--font-size-m);
72
+ color: var(--color-text);
73
+
74
+ &:hover {
75
+ border-color: var(--color-border-strong);
76
+ }
77
+ }
@@ -55,14 +55,20 @@ const baseTransform = computed(() => {
55
55
  <div class="flex-1">
56
56
  <slot name="header" :close />
57
57
  </div>
58
- <Button square icon="ph:x" @click="open = false" />
58
+ <Button plain square icon="ph:x" @click="open = false" />
59
59
  </div>
60
60
 
61
- <Divider v-if="separator && $slots.header" :space="1" />
61
+ <Divider v-if="separator && $slots.header" :size="1" />
62
62
 
63
63
  <div v-if="$slots.default" class="vui-sheet-content">
64
64
  <slot :close />
65
65
  </div>
66
+
67
+ <Divider v-if="separator && $slots.footer" :size="1" />
68
+
69
+ <div class="vui-sheet-footer">
70
+ <slot name="footer" :close />
71
+ </div>
66
72
  </div>
67
73
  </Backdrop>
68
74
  </Transition>
@@ -10,24 +10,32 @@
10
10
  top: 0;
11
11
  border-bottom: 1px solid var(--color-border);
12
12
  height: auto;
13
+ border-bottom-right-radius: var(--border-radius-l);
14
+ border-bottom-left-radius: var(--border-radius-l);
13
15
  }
14
16
 
15
17
  &.vui-sheet-position-bottom {
16
18
  bottom: 0;
17
19
  border-top: 1px solid var(--color-border);
18
20
  height: auto;
21
+ border-top-right-radius: var(--border-radius-l);
22
+ border-top-left-radius: var(--border-radius-l);
19
23
  }
20
24
 
21
25
  &.vui-sheet-position-right {
22
26
  right: 0;
23
27
  top: 0;
24
28
  border-left: 1px solid var(--color-border);
29
+ border-top-left-radius: var(--border-radius-l);
30
+ border-bottom-left-radius: var(--border-radius-l);
25
31
  }
26
32
 
27
33
  &.vui-sheet-position-left {
28
34
  left: 0;
29
35
  top: 0;
30
36
  border-right: 1px solid var(--color-border);
37
+ border-top-right-radius: var(--border-radius-l);
38
+ border-bottom-right-radius: var(--border-radius-l);
31
39
  }
32
40
 
33
41
  &.vui-sheet-position-top,
@@ -36,6 +44,7 @@
36
44
  width: 100%;
37
45
  }
38
46
 
47
+ .vui-sheet-footer,
39
48
  .vui-sheet-header,
40
49
  .vui-sheet-content {
41
50
  width: 100%;
@@ -1,6 +1,6 @@
1
1
  <script setup lang='ts'>
2
- import { useCssVar, useMouse, useTimeoutFn, watchThrottled } from '@vueuse/core'
3
- import { computed, useSlots, useTemplateRef } from 'vue'
2
+ import { useCssVar, useMouseInElement, useTimeoutFn, watchThrottled } from '@vueuse/core'
3
+ import { computed, onMounted, useSlots, useTemplateRef } from 'vue'
4
4
  import { isNil } from '../../shared/helpers'
5
5
  import './sidebar.scss'
6
6
 
@@ -27,18 +27,21 @@ interface Props {
27
27
  }
28
28
 
29
29
  const sidebar = useTemplateRef('sidebar')
30
- const open = defineModel<boolean>()
30
+ const open = defineModel<boolean>({
31
+ default: true,
32
+ })
31
33
  const slots = useSlots()
32
34
  const offset = useCssVar('--vui-sidebar-float-offset', sidebar, {
33
35
  initialValue: '8px',
34
36
  })
35
37
 
36
38
  const width = computed(() => {
37
- if (props.mini)
38
- return `65px`
39
+ if (props.mini) {
40
+ return props.floaty ? '73px' : `65px`
41
+ }
39
42
  if (!props.floaty)
40
43
  return `${props.width}px`
41
- return `calc(${props.width}px - ${offset.value})`
44
+ return `calc(${props.width}px + ${offset.value})`
42
45
  })
43
46
 
44
47
  const slotProps = computed(() => ({
@@ -46,19 +49,29 @@ const slotProps = computed(() => ({
46
49
  floaty: props.floaty,
47
50
  width: props.width,
48
51
  open,
52
+ close: () => open.value = false,
49
53
  }))
50
54
 
51
55
  // Sidebar `appear` implementation
52
56
  const { start, stop, isPending } = useTimeoutFn(() => {
53
- open.value = true
57
+ if (props.appear) {
58
+ open.value = true
59
+ }
54
60
  }, 250)
55
61
 
56
62
  const APPEAR_OFFSET = 32
57
63
 
58
- const { x } = useMouse()
64
+ const wrap = useTemplateRef('wrap')
65
+ const { elementX } = useMouseInElement(wrap)
66
+
67
+ onMounted(() => {
68
+ if (props.appear && open.value) {
69
+ open.value = false
70
+ }
71
+ })
59
72
 
60
- watchThrottled(x, (pos) => {
61
- if (!props.appear)
73
+ watchThrottled(elementX, (pos) => {
74
+ if (!props.appear || (pos <= APPEAR_OFFSET && pos >= 0 && isPending.value))
62
75
  return
63
76
 
64
77
  if (pos <= APPEAR_OFFSET && pos >= 0 && !open.value && !isPending.value) {
@@ -83,7 +96,7 @@ watchThrottled(x, (pos) => {
83
96
  </script>
84
97
 
85
98
  <template>
86
- <div class="vui-sidebar-outer" :style="{ width }" :class="{ open }">
99
+ <div ref="wrap" class="vui-sidebar-outer" :style="{ width }" :class="{ open }">
87
100
  <aside ref="sidebar" class="vui-sidebar" :class="{ open, floaty: props.floaty, mini: props.mini }" :style="{ width: `${props.mini ? 65 : props.width}px` }">
88
101
  <div v-if="slots.header" class="vui-sidebar-header">
89
102
  <slot name="header" v-bind="slotProps" />
@@ -2,12 +2,12 @@
2
2
  .vui-sidebar-layout {
3
3
  display: flex;
4
4
  flex-wrap: nowrap;
5
- gap: 32px;
5
+ gap: 0;
6
+ height: 100vh;
6
7
  position: relative;
7
8
 
8
9
  main {
9
10
  flex: 1;
10
- padding: 2rem;
11
11
  }
12
12
 
13
13
  .vui-sidebar-outer {
@@ -22,11 +22,11 @@
22
22
  .vui-sidebar {
23
23
  --vui-sidebar-float-offset: 8px;
24
24
  display: flex;
25
+ height: 100%;
25
26
  flex-direction: column;
26
27
  gap: var(--space-sm);
27
- height: 100vh;
28
28
  width: 224px;
29
- position: fixed;
29
+ position: absolute;
30
30
  top: 0;
31
31
  z-index: 50;
32
32
  background-color: var(--color-bg);
@@ -47,7 +47,7 @@
47
47
  top: var(--vui-sidebar-float-offset);
48
48
  left: var(--vui-sidebar-float-offset);
49
49
  bottom: var(--vui-sidebar-float-offset);
50
- height: calc(100vh - calc(var(--vui-sidebar-float-offset) * 2));
50
+ height: calc(100% - calc(var(--vui-sidebar-float-offset) * 2));
51
51
  border-radius: var(--border-radius-m);
52
52
  border: 1px solid var(--color-border);
53
53
  transform: translateX(calc(-100% - calc(var(--vui-sidebar-float-offset) * 2)));
@@ -76,6 +76,7 @@
76
76
  position: absolute;
77
77
  inset: 0;
78
78
  overflow-y: auto;
79
+ overflow-x: hidden;
79
80
  }
80
81
  }
81
82
 
@@ -1,5 +1,6 @@
1
1
  .vui-spinner {
2
- --spinner-color: var(--color-text);
2
+ --spinner-color: var(--color-border-accent);
3
+
3
4
  aspect-ratio: 1;
4
5
  border-radius: 50%;
5
6
  border: 3px solid var(--spinner-color);
@@ -5,17 +5,18 @@ import './switch.scss'
5
5
  interface Props {
6
6
  label?: string
7
7
  disabled?: boolean
8
+ accent?: boolean
8
9
  }
9
10
 
10
- const { label, disabled } = defineProps<Props>()
11
+ const { label, disabled, accent } = defineProps<Props>()
11
12
  const slots = defineSlots()
12
13
  const checked = defineModel<boolean>()
13
14
  const id = useId()
14
15
  </script>
15
16
 
16
17
  <template>
17
- <div class="vui-switch" :class="{ disabled, checked }">
18
- <input :id v-model="checked" type="checkbox">
18
+ <div class="vui-switch" :class="{ disabled, checked, accent }">
19
+ <input :id v-model="checked" type="checkbox" :disabled>
19
20
  <label :for="id">
20
21
  <div class="vui-switch-icon">
21
22
  <span class="vui-switch-indicator" />
@@ -1,9 +1,27 @@
1
1
  .vui-switch {
2
2
  --switch-size: 24px;
3
3
 
4
- &.checked .vui-switch-icon .vui-switch-indicator {
5
- left: calc(100% - 24px);
6
- background-color: var(--color-text);
4
+ &.checked {
5
+ .vui-switch-icon .vui-switch-indicator {
6
+ left: calc(100% - 24px);
7
+ background-color: var(--color-text);
8
+ }
9
+
10
+ &.accent .vui-switch-icon .vui-switch-indicator {
11
+ background-color: var(--color-accent);
12
+ }
13
+ }
14
+
15
+ &.disabled {
16
+ .vui-switch-icon {
17
+ opacity: 0.5;
18
+ cursor: not-allowed;
19
+ }
20
+
21
+ input + label .vui-switch-content {
22
+ opacity: 0.5;
23
+ cursor: not-allowed;
24
+ }
7
25
  }
8
26
 
9
27
  .vui-switch-icon {
@@ -12,7 +30,7 @@
12
30
  border-radius: 22px;
13
31
  background-color: var(--color-bg-raised);
14
32
  position: relative;
15
- cursor: pointer;
33
+ cursor: default;
16
34
 
17
35
  .vui-switch-indicator {
18
36
  display: block;
@@ -22,7 +40,7 @@
22
40
  width: 20px;
23
41
  height: 20px;
24
42
  border-radius: 100%;
25
- background-color: var(--color-bg);
43
+ background-color: var(--color-text-lighter);
26
44
  transition: var(--transition);
27
45
  }
28
46
  }
@@ -33,6 +51,7 @@
33
51
  position: absolute;
34
52
  overflow: hidden;
35
53
  outline: none !important;
54
+ opacity: 0;
36
55
 
37
56
  &:focus-visible + label .vui-switch-icon {
38
57
  outline: 2px solid var(--color-text);
@@ -52,9 +71,23 @@
52
71
  &:is(p) {
53
72
  height: var(--switch-size);
54
73
  line-height: var(--switch-size);
55
- font-size: var(--font-size-ms);
74
+ font-size: var(--font-size-m);
56
75
  }
57
76
  }
58
77
  }
59
78
  }
60
79
  }
80
+
81
+ :root.light {
82
+ .vui-switch .vui-switch-icon .vui-switch-indicator {
83
+ background-color: var(--color-bg-lowered);
84
+ }
85
+
86
+ .vui-switch.checked.accent .vui-switch-icon .vui-switch-indicator {
87
+ background-color: var(--color-accent);
88
+ }
89
+
90
+ .vui-switch.checked .vui-switch-icon .vui-switch-indicator {
91
+ background-color: var(--color-text);
92
+ }
93
+ }
@@ -40,18 +40,18 @@ const sortStateBind = computed(() => {
40
40
 
41
41
  <template>
42
42
  <th>
43
- <div v-if="props.header" class="vui-table-th-content">
43
+ <div v-if="props.header || $slots.default" class="vui-table-th-content">
44
44
  <slot>
45
- {{ props.header.label }}
45
+ {{ props.header?.label }}
46
46
  </slot>
47
47
  <Button
48
- v-if="props.sort"
48
+ v-if="props.sort && props.header"
49
49
  class="vui-table-sort-button"
50
50
  v-bind="sortStateBind"
51
- :class="{ active: !!props.header.sortKey }"
52
51
  size="s"
53
- plain
52
+ :plain="!!!props.header.sortKey"
54
53
  square
54
+ variant="gray"
55
55
  @click="props.header.sortToggle"
56
56
  />
57
57
  </div>