@dolanske/vui 0.1.5 → 0.3.0

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 (67) hide show
  1. package/README.md +29 -98
  2. package/dist/components/Accordion/Accordion.vue.d.ts +1 -0
  3. package/dist/components/Avatar/Avatar.vue.d.ts +1 -1
  4. package/dist/components/Calendar/Calendar.vue.d.ts +1 -1
  5. package/dist/components/Divider/Divider.vue.d.ts +2 -4
  6. package/dist/components/Dropdown/Dropdown.vue.d.ts +105 -0
  7. package/dist/components/Flex/Flex.vue.d.ts +2 -2
  8. package/dist/components/Grid/Grid.vue.d.ts +2 -2
  9. package/dist/components/Input/Dropzone.vue.d.ts +1 -0
  10. package/dist/components/Input/Input.vue.d.ts +1 -0
  11. package/dist/components/Popout/Popout.vue.d.ts +3 -3
  12. package/dist/components/Radio/Radio.vue.d.ts +1 -1
  13. package/dist/components/Radio/RadioGroup.vue.d.ts +3 -12
  14. package/dist/components/Tooltip/Tooltip.vue.d.ts +1 -1
  15. package/dist/index.d.ts +3 -1
  16. package/dist/shared/helpers.d.ts +6 -0
  17. package/dist/shared/types.d.ts +14 -0
  18. package/dist/style.css +1 -1
  19. package/dist/vui.js +4617 -4470
  20. package/package.json +6 -2
  21. package/src/App.vue +24 -16
  22. package/src/components/Accordion/Accordion.vue +8 -4
  23. package/src/components/Accordion/accordion.scss +38 -2
  24. package/src/components/Alert/alert.scss +1 -1
  25. package/src/components/Avatar/Avatar.vue +10 -3
  26. package/src/components/Avatar/avatar.scss +5 -5
  27. package/src/components/Button/Button.vue +6 -9
  28. package/src/components/Calendar/Calendar.vue +10 -8
  29. package/src/components/Card/Card.vue +2 -2
  30. package/src/components/Checkbox/Checkbox.vue +4 -1
  31. package/src/components/Checkbox/checkbox.scss +12 -6
  32. package/src/components/Divider/Divider.vue +18 -8
  33. package/src/components/Drawer/Drawer.vue +14 -10
  34. package/src/components/Drawer/drawer.scss +1 -14
  35. package/src/components/Dropdown/Dropdown.vue +14 -9
  36. package/src/components/Dropdown/dropdown.scss +4 -0
  37. package/src/components/Flex/Flex.vue +14 -17
  38. package/src/components/Grid/Grid.vue +9 -14
  39. package/src/components/Input/Input.vue +4 -1
  40. package/src/components/Input/Textarea.vue +7 -4
  41. package/src/components/Input/input.scss +13 -4
  42. package/src/components/OTP/otp.scss +1 -2
  43. package/src/components/Popout/Popout.vue +3 -3
  44. package/src/components/Progress/Progress.vue +13 -7
  45. package/src/components/Progress/progress.scss +1 -1
  46. package/src/components/Radio/Radio.vue +1 -1
  47. package/src/components/Radio/RadioGroup.vue +10 -5
  48. package/src/components/Sheet/Sheet.vue +16 -15
  49. package/src/components/Sheet/sheet.scss +4 -0
  50. package/src/components/Skeleton/Skeleton.vue +13 -16
  51. package/src/components/Spinner/Spinner.vue +9 -11
  52. package/src/components/Table/table.scss +1 -1
  53. package/src/components/Table/table.ts +2 -1
  54. package/src/components/Tabs/Tab.vue +1 -1
  55. package/src/components/Tabs/Tabs.vue +1 -0
  56. package/src/components/Toast/toast.ts +0 -24
  57. package/src/components/Tooltip/Tooltip.vue +6 -3
  58. package/src/index.ts +4 -0
  59. package/src/shared/helpers.ts +15 -0
  60. package/src/shared/types.ts +18 -0
  61. package/src/style/core.scss +28 -15
  62. package/src/style/fonts.scss +23 -0
  63. package/src/style/layout.scss +133 -1
  64. package/src/style/typography.scss +9 -9
  65. package/src/style/utils.scss +13 -0
  66. package/dist/shared/composables.d.ts +0 -3
  67. package/src/shared/composables.ts +0 -18
@@ -1,7 +1,6 @@
1
1
  <script setup lang="ts">
2
- import type { Sizes } from '../../shared/types'
2
+ import type { Space } from '../../shared/types'
3
3
  import { computed } from 'vue'
4
- import { useActualGap } from '../../shared/composables'
5
4
 
6
5
  export interface FlexProps {
7
6
  inline?: boolean
@@ -13,7 +12,7 @@ export interface FlexProps {
13
12
  rowReverse?: boolean
14
13
  columnReverse?: boolean
15
14
 
16
- gap?: Sizes | number
15
+ gap?: Space | number
17
16
 
18
17
  // NOTE: Add more if needed
19
18
  justifyStart?: boolean
@@ -35,7 +34,7 @@ export interface FlexProps {
35
34
  const props = defineProps<FlexProps>()
36
35
 
37
36
  // Flex gap
38
- const ag = useActualGap(props.gap)
37
+ const ag = computed(() => `var(--space-${props.gap})`)
39
38
 
40
39
  // Flex direction
41
40
  const ad = computed(() => {
@@ -91,19 +90,17 @@ const aH = computed(() => props.expand ? '100%' : 'auto')
91
90
  </script>
92
91
 
93
92
  <template>
94
- <div class="vui-flex">
93
+ <div
94
+ class="vui-flex" :style="{
95
+ display: aY,
96
+ flexWrap: aW,
97
+ flexDirection: ad,
98
+ justifyContent: aj,
99
+ gap: ag,
100
+ alignItems: aA,
101
+ width: aH,
102
+ }"
103
+ >
95
104
  <slot />
96
105
  </div>
97
106
  </template>
98
-
99
- <style scoped lang="scss">
100
- .vui-flex {
101
- display: v-bind(aY);
102
- flex-wrap: v-bind(aW);
103
- flex-direction: v-bind(ad);
104
- justify-content: v-bind(aj);
105
- gap: v-bind(ag);
106
- align-items: v-bind(aA);
107
- width: v-bind(aH);
108
- }
109
- </style>
@@ -1,7 +1,6 @@
1
1
  <script setup lang="ts">
2
- import type { Sizes } from '../../shared/types'
2
+ import type { Space } from '../../shared/types'
3
3
  import { computed } from 'vue'
4
- import { useActualGap } from '../../shared/composables'
5
4
  import { createArray } from '../../shared/helpers'
6
5
 
7
6
  /**
@@ -10,7 +9,7 @@ import { createArray } from '../../shared/helpers'
10
9
 
11
10
  interface Props {
12
11
  inline?: boolean
13
- gap?: Sizes | number
12
+ gap?: Space | number
14
13
  rows?: number | string
15
14
  columns?: number | string
16
15
  areas?: string[]
@@ -18,7 +17,7 @@ interface Props {
18
17
 
19
18
  const props = defineProps<Props>()
20
19
 
21
- const aG = useActualGap(props.gap)
20
+ const ag = computed(() => `var(--space-${props.gap})`)
22
21
 
23
22
  const aTC = computed(() => {
24
23
  if (typeof props.columns === 'number') {
@@ -43,17 +42,13 @@ const aD = computed(() => props.inline ? 'inline-grid' : 'grid')
43
42
 
44
43
  <template>
45
44
  <div
46
- class="vui-grid"
45
+ class="vui-grid" :style="{
46
+ display: aD,
47
+ gap: ag,
48
+ gridTemplateColumns: aTC,
49
+ gridTemplateRows: aTR,
50
+ }"
47
51
  >
48
52
  <slot />
49
53
  </div>
50
54
  </template>
51
-
52
- <style scoped lang="scss">
53
- .vui-grid {
54
- display: v-bind(aD);
55
- gap: v-bind(aG);
56
- grid-template-columns: v-bind(aTC);
57
- grid-template-rows: v-bind(aTR);
58
- }
59
- </style>
@@ -23,6 +23,7 @@ export interface InputProps {
23
23
  multiple?: boolean
24
24
  min?: number
25
25
  max?: number
26
+ disabled?: boolean
26
27
  }
27
28
 
28
29
  const {
@@ -40,6 +41,7 @@ const {
40
41
  min,
41
42
  max,
42
43
  errors = [] as string[],
44
+ disabled,
43
45
  } = defineProps<InputProps>()
44
46
 
45
47
  const model = defineModel<string | number>({
@@ -76,7 +78,7 @@ const renderLimit = computed(() => {
76
78
  </script>
77
79
 
78
80
  <template>
79
- <div class="vui-input-container" :class="{ expand, required, readonly, 'has-errors': errors.length > 0 }">
81
+ <div class="vui-input-container" :class="{ expand, disabled, required, readonly, 'has-errors': errors.length > 0 }">
80
82
  <slot name="before" />
81
83
  <div class="vui-input">
82
84
  <label v-if="label" for="id">{{ label }}</label>
@@ -100,6 +102,7 @@ const renderLimit = computed(() => {
100
102
  :accept
101
103
  :multiple
102
104
  :min
105
+ :disabled
103
106
  >
104
107
  <slot name="end" />
105
108
  </Flex>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { InputProps } from './Input.vue'
3
- import { useId } from 'vue'
3
+ import { computed, useId } from 'vue'
4
4
  import '../Input/input.scss'
5
5
 
6
6
  type Props = Omit<InputProps, 'type'> & {
@@ -35,6 +35,9 @@ const model = defineModel<string>({
35
35
  },
36
36
  })
37
37
  const id = useId()
38
+
39
+ const r = computed(() => resize === true ? 'both' : (resize || 'initial'))
40
+ const fS = computed(() => autoResize ? 'content' : 'auto')
38
41
  </script>
39
42
 
40
43
  <template>
@@ -54,9 +57,9 @@ const id = useId()
54
57
  :required
55
58
  :max="limit"
56
59
  :style="{
57
- 'resize': resize === true ? 'both' : (resize || 'initial'),
58
- // @ts-expect-error This is only supported in chrome for now
59
- 'field-sizing': autoResize ? 'content' : 'auto',
60
+ resize: r,
61
+ // @ts-expect-error Early-adoption CSS attribute
62
+ fieldSizing: fS,
60
63
  }"
61
64
  />
62
65
  </div>
@@ -17,6 +17,11 @@
17
17
 
18
18
  width: 224px;
19
19
 
20
+ &.disabled input,
21
+ &.disabled textarea {
22
+ cursor: not-allowed;
23
+ }
24
+
20
25
  &.expand {
21
26
  width: 100%;
22
27
  }
@@ -26,10 +31,14 @@
26
31
  color: var(--input-color-text-red);
27
32
  }
28
33
 
29
- &.readonly .vui-input input {
30
- pointer-events: none;
31
- color: var(--input-color-text-light);
32
- border-color: var(--input-color-border-weak);
34
+ &.readonly .vui-input {
35
+ input,
36
+ textarea,
37
+ .vui-input-style {
38
+ pointer-events: none;
39
+ color: var(--input-color-text-light);
40
+ border-color: var(--input-color-border-weak);
41
+ }
33
42
  }
34
43
 
35
44
  &.has-errors {
@@ -15,7 +15,7 @@
15
15
  border: 1px solid var(--color-border-strong);
16
16
  color: var(--color-text);
17
17
  z-index: 1;
18
- font-size: var(--font-size-l);
18
+ font-size: var(--font-size-m);
19
19
  outline: 0 solid var(--color-text-light);
20
20
  transition: var(--transition);
21
21
 
@@ -47,7 +47,6 @@
47
47
  }
48
48
  }
49
49
 
50
- // TODO: animate blinking cursor
51
50
  &.active {
52
51
  z-index: 2;
53
52
  outline-width: 2px;
@@ -1,11 +1,11 @@
1
1
  <script setup lang='ts'>
2
- import type { MaybeElement, Placement } from '@floating-ui/vue'
2
+ import type { Placement, PopoutMaybeElement } from '../../shared/types'
3
3
  import { autoPlacement, offset, useFloating } from '@floating-ui/vue'
4
4
  import { toRef, useTemplateRef } from 'vue'
5
5
  import './popout.scss'
6
6
 
7
- interface Props {
8
- anchor: MaybeElement<HTMLElement>
7
+ export interface Props {
8
+ anchor: PopoutMaybeElement<HTMLElement>
9
9
  /**
10
10
  * Override the autoPlacement option
11
11
  */
@@ -1,6 +1,6 @@
1
1
  <script setup lang='ts'>
2
- import { onMounted, useTemplateRef, watchEffect } from 'vue'
3
- import { delay, isNil, randomMinMax } from '../../shared/helpers'
2
+ import { computed, onMounted, useTemplateRef, watchEffect } from 'vue'
3
+ import { delay, formatUnitValue, isNil, randomMinMax } from '../../shared/helpers'
4
4
  import './progress.scss'
5
5
 
6
6
  interface Props {
@@ -42,16 +42,19 @@ const progressRef = useTemplateRef('progress')
42
42
 
43
43
  watchEffect(() => {
44
44
  if (progressRef.value && !isNil(height)) {
45
- progressRef.value.style.setProperty('--vui-progress-height', `${height}px`)
45
+ progressRef.value.style.setProperty(
46
+ '--vui-progress-height',
47
+ formatUnitValue(height),
48
+ )
46
49
  }
47
50
  })
48
51
 
49
52
  // Automatically / randomly increment but never reach 100% until
50
53
  async function fakeIncrement() {
51
54
  if (fake && progressAmount.value < 100) {
52
- if (progressAmount.value > 95) {
55
+ if (progressAmount.value > 90) {
53
56
  // Only in crement by the fraction of the remaining amount
54
- progressAmount.value += (100 - progressAmount.value) * 0.075
57
+ progressAmount.value += (100 - progressAmount.value) * 0.05
55
58
  await delay(randomMinMax(500, 3000))
56
59
  }
57
60
  else {
@@ -63,6 +66,9 @@ async function fakeIncrement() {
63
66
  }
64
67
 
65
68
  onMounted(fakeIncrement)
69
+
70
+ const w = computed(() => `${progressAmount.value}%`)
71
+ const bC = computed(() => color)
66
72
  </script>
67
73
 
68
74
  <template>
@@ -76,8 +82,8 @@ onMounted(fakeIncrement)
76
82
  >
77
83
  <div
78
84
  class="vui-progress-indicator" :style="{
79
- width: `${progressAmount}%`,
80
- backgroundColor: color,
85
+ width: w,
86
+ backgroundColor: bC,
81
87
  }"
82
88
  />
83
89
  </div>
@@ -1,5 +1,6 @@
1
1
  .vui-progress {
2
2
  --vui-progress-height: 3px;
3
+
3
4
  display: block;
4
5
  width: 100%;
5
6
  position: relative;
@@ -18,7 +19,6 @@
18
19
  border-radius: none !important;
19
20
 
20
21
  &.fixed-active {
21
- // Some arbitrary value which should never happen to people with
22
22
  height: var(--vui-progress-height);
23
23
  }
24
24
 
@@ -6,7 +6,7 @@ import './radio.scss'
6
6
  export interface RadioProps {
7
7
  label?: string
8
8
  disabled?: boolean
9
- value: string
9
+ value: any
10
10
  }
11
11
 
12
12
  const {
@@ -1,7 +1,7 @@
1
1
  <script setup lang='ts'>
2
- import type { VNode } from 'vue'
3
2
  import type { FlexProps } from '../Flex/Flex.vue'
4
- import type { RadioProps } from './Radio.vue'
3
+ import type Radio from './Radio.vue'
4
+ import { watchEffect } from 'vue'
5
5
  import Flex from '../Flex/Flex.vue'
6
6
 
7
7
  interface Props extends FlexProps {
@@ -14,22 +14,27 @@ const {
14
14
  } = defineProps<Props>()
15
15
 
16
16
  const slots = defineSlots<{
17
- default: () => { children: Array<VNode & { props: RadioProps }> }[]
17
+ default: () => Array<typeof Radio>
18
18
  }>()
19
19
 
20
20
  const checked = defineModel()
21
+
22
+ watchEffect(() => {
23
+ if (slots.default().some(s => s.type.__name !== 'Radio')) {
24
+ console.error('You can only pass `<Radio />` components as children.')
25
+ }
26
+ })
21
27
  </script>
22
28
 
23
29
  <template>
24
30
  <Flex v-bind="flexProps">
25
31
  <Component
26
32
  :is="vnode"
27
- v-for="vnode of slots.default()[0].children"
33
+ v-for="vnode of slots.default()"
28
34
  :key="vnode.props.value"
29
35
  v-bind="vnode.props"
30
36
  v-model="checked"
31
37
  :class="{ disabled: disabled || vnode.props.disabled }"
32
38
  />
33
39
  </Flex>
34
- <!-- <div class="vui-radio-group" /> -->
35
40
  </template>
@@ -1,6 +1,7 @@
1
1
  <script setup lang='ts'>
2
2
  import { computed } from 'vue'
3
3
  import Backdrop from '../../internal/Backdrop/Backdrop.vue'
4
+ import { formatUnitValue } from '../../shared/helpers'
4
5
  import Button from '../Button/Button.vue'
5
6
  import Divider from '../Divider/Divider.vue'
6
7
  import './sheet.scss'
@@ -17,6 +18,8 @@ const {
17
18
  separator,
18
19
  } = defineProps<Props>()
19
20
 
21
+ const TRANSITION_OFFSET = 16
22
+
20
23
  const open = defineModel<boolean>()
21
24
 
22
25
  function close() {
@@ -24,27 +27,21 @@ function close() {
24
27
  }
25
28
 
26
29
  const style = computed(() => {
27
- const formattedSizeValue = typeof size === 'number' ? `${size}px` : size
28
- let style
29
-
30
30
  if (position === 'left' || position === 'right') {
31
- style = { width: formattedSizeValue }
32
- }
33
- else {
34
- style = { minHeight: formattedSizeValue }
31
+ return { width: formatUnitValue(size) }
35
32
  }
36
33
 
37
- return style
34
+ return undefined
38
35
  })
39
36
 
40
37
  // Used to compute base location so that sheet appears to animate form the edge of the screen
41
38
  const baseTransform = computed(() => {
42
39
  switch (position) {
43
- case 'left': return `translate(-16px, 0)`
44
- case 'top': return `translate(0, -16px)`
45
- case 'bottom': return `translate(0, 16px)`
40
+ case 'left': return `translate(-${TRANSITION_OFFSET}px, 0)`
41
+ case 'top': return `translate(0, -${TRANSITION_OFFSET}px)`
42
+ case 'bottom': return `translate(0, ${TRANSITION_OFFSET}px)`
46
43
  case 'right':
47
- default: return `translate(16px, 0)`
44
+ default: return `translate(${TRANSITION_OFFSET}px, 0)`
48
45
  }
49
46
  })
50
47
  </script>
@@ -52,16 +49,16 @@ const baseTransform = computed(() => {
52
49
  <template>
53
50
  <Teleport to="body">
54
51
  <Transition appear name="sheet">
55
- <Backdrop v-if="open" :style="{ padding: 0 }" @close="open = false">
52
+ <Backdrop v-if="open" @close="open = false">
56
53
  <div v-if="open" class="vui-sheet" :class="[`vui-sheet-position-${position}`]" :style>
57
54
  <div class="vui-sheet-header">
58
- <div :style="{ flex: 1 }">
55
+ <div class="flex-1">
59
56
  <slot name="header" :close />
60
57
  </div>
61
58
  <Button square icon="ph:x" @click="open = false" />
62
59
  </div>
63
60
 
64
- <Divider v-if="separator && $slots.header" :size="1" />
61
+ <Divider v-if="separator && $slots.header" :space="1" />
65
62
 
66
63
  <div v-if="$slots.default" class="vui-sheet-content">
67
64
  <slot :close />
@@ -73,6 +70,10 @@ const baseTransform = computed(() => {
73
70
  </template>
74
71
 
75
72
  <style scoped lang="scss">
73
+ .vui-backdrop {
74
+ padding: 0;
75
+ }
76
+
76
77
  .sheet-enter-active,
77
78
  .sheet-leave-active {
78
79
  transition: var(--transition);
@@ -9,20 +9,24 @@
9
9
  &.vui-sheet-position-top {
10
10
  top: 0;
11
11
  border-bottom: 1px solid var(--color-border);
12
+ height: auto;
12
13
  }
13
14
 
14
15
  &.vui-sheet-position-bottom {
15
16
  bottom: 0;
16
17
  border-top: 1px solid var(--color-border);
18
+ height: auto;
17
19
  }
18
20
 
19
21
  &.vui-sheet-position-right {
20
22
  right: 0;
23
+ top: 0;
21
24
  border-left: 1px solid var(--color-border);
22
25
  }
23
26
 
24
27
  &.vui-sheet-position-left {
25
28
  left: 0;
29
+ top: 0;
26
30
  border-right: 1px solid var(--color-border);
27
31
  }
28
32
 
@@ -1,5 +1,7 @@
1
1
  <!-- eslint-disable ts/no-use-before-define -->
2
2
  <script setup lang='ts'>
3
+ import { computed } from 'vue'
4
+ import { formatUnitValue } from '../../shared/helpers'
3
5
  import './skeleton.scss'
4
6
 
5
7
  interface Props {
@@ -20,27 +22,22 @@ const {
20
22
 
21
23
  const DEFAULT_RADIUS = 'var(--border-radius-s)'
22
24
 
23
- function valueToPixels(value: string | number) {
24
- return typeof value === 'number'
25
- ? `${value}px`
26
- // If last value of string is NOT a number, do not add "px" at the end
27
- // eslint-disable-next-line unicorn/prefer-number-properties
28
- : isNaN(Number(value[value.length - 1]))
29
- ? value
30
- : `${value}px`
31
- }
25
+ // Give priority to radius, if it is NOT set to default
26
+ const bR = computed(() => formatUnitValue(circle && radius === DEFAULT_RADIUS ? 9999 : radius))
27
+
28
+ // When using `circle` prop, we want to use the same
29
+ // value for both height and width, but we can't
30
+ // know which one will be defined
31
+ const w = computed(() => formatUnitValue(circle ? (width || height) : width))
32
+ const h = computed(() => formatUnitValue(circle ? (width || height) : height))
32
33
  </script>
33
34
 
34
35
  <template>
35
36
  <div
36
37
  class="vui-skeleton" :style="{
37
- // Give priority to radius, if it is NOT set to default
38
- borderRadius: valueToPixels(circle && radius === DEFAULT_RADIUS ? 9999 : radius),
39
- // When using `circle` prop, we want to use the same
40
- // value for both height and width, but we can't
41
- // know which one will be defined
42
- width: valueToPixels(circle ? (width || height) : width),
43
- height: valueToPixels(circle ? (width || height) : height),
38
+ borderRadius: bR,
39
+ width: w,
40
+ height: h,
44
41
  }"
45
42
  />
46
43
  </template>
@@ -12,7 +12,7 @@ const {
12
12
  size = 's',
13
13
  } = defineProps<Props>()
14
14
 
15
- const actualSize = computed(() => {
15
+ const w = computed(() => {
16
16
  switch (size) {
17
17
  case Size.s: return '16px'
18
18
  case Size.l: return '38px'
@@ -21,7 +21,7 @@ const actualSize = computed(() => {
21
21
  }
22
22
  })
23
23
 
24
- const actualBorderWidth = computed(() => {
24
+ const bW = computed(() => {
25
25
  switch (size) {
26
26
  case Size.s: return '3px'
27
27
  case Size.l: return '5px'
@@ -32,13 +32,11 @@ const actualBorderWidth = computed(() => {
32
32
  </script>
33
33
 
34
34
  <template>
35
- <div :class="{ size }" class="vui-spinner" />
35
+ <div
36
+ :class="{ size }" class="vui-spinner" :style="{
37
+ width: w,
38
+ height: w,
39
+ borderWidth: bW,
40
+ }"
41
+ />
36
42
  </template>
37
-
38
- <style lang="scss">
39
- .vui-spinner {
40
- width: v-bind(actualSize);
41
- height: v-bind(actualSize);
42
- border-width: v-bind(actualBorderWidth);
43
- }
44
- </style>
@@ -59,7 +59,7 @@
59
59
  font-size: var(--font-size-ms);
60
60
  border: none;
61
61
  border-left: none !important;
62
- transition: var(--transition-quick);
62
+ transition: var(--transition-fast);
63
63
  position: relative;
64
64
  z-index: 1;
65
65
 
@@ -125,7 +125,8 @@ export function defineTable<const Dataset extends Array<BaseRow>>(
125
125
  const key = sorting.value.key
126
126
 
127
127
  if (key) {
128
- final = final.toSorted((a: Dataset[number], b: Dataset[number]) => {
128
+ // FIXME: change to `toSorted` when typescript is ok
129
+ final = [...final].sort((a: Dataset[number], b: Dataset[number]) => {
129
130
  const aValue = a[key]
130
131
  const bValue = b[key]
131
132
  return sorting.value.type === 'asc'
@@ -14,7 +14,7 @@ const id = computed(() => props.id ?? props.label)
14
14
  </script>
15
15
 
16
16
  <template>
17
- <button class="vui-tab" :data-tab-id="id" :class="{ disabled: props.disabled }">
17
+ <button class="vui-tab" :data-tab-id="id" :class="{ disabled: props.disabled }" role="tab">
18
18
  <Icon v-if="props.icon" :icon="props.icon" />
19
19
  {{ props.label }}
20
20
  </button>
@@ -56,6 +56,7 @@ useEventListener(window, 'resize', computeUnderlinePosition)
56
56
  <div
57
57
  ref="tabs"
58
58
  class="vui-tabs"
59
+ role="tablist"
59
60
  :class="[
60
61
  { expand, disabled },
61
62
  variant === 'default'
@@ -1,5 +1,3 @@
1
- // share some tiny global state
2
-
3
1
  import { ref } from 'vue'
4
2
 
5
3
  interface ToastAction {
@@ -68,25 +66,3 @@ export function pushToast(title: string, options?: ToastOptions): Toast {
68
66
  export function removeToast(id: number): void {
69
67
  toasts.value.delete(id)
70
68
  }
71
-
72
- //////
73
-
74
- // export const toastError: NewToastFn = (title, options) => {
75
- // return pushToast('error', title, options)
76
- // }
77
-
78
- // export const toastSuccess: NewToastFn = (title, options) => {
79
- // return pushToast('success', title, options)
80
- // }
81
-
82
- // export const toastInfo: NewToastFn = (title, options) => {
83
- // return pushToast('info', title, options)
84
- // }
85
-
86
- // export const toastNeutral: NewToastFn = (title, options) => {
87
- // return pushToast('neutral', title, options)
88
- // }
89
-
90
- // export const toastWarning: NewToastFn = (title, options) => {
91
- // return pushToast('warning', title, options)
92
- // }
@@ -1,6 +1,6 @@
1
1
  <script setup lang='ts'>
2
- import type { Placement } from '@floating-ui/vue'
3
- import { ref, useTemplateRef, watch } from 'vue'
2
+ import type { Placement } from '../../shared/types'
3
+ import { ref, useId, useTemplateRef, watch } from 'vue'
4
4
  import Popout from '../Popout/Popout.vue'
5
5
  import './tooltip.scss'
6
6
 
@@ -50,18 +50,21 @@ watch(hoverAnchor, (isHovering) => {
50
50
  showTooltip.value = false
51
51
  }
52
52
  })
53
+
54
+ const id = useId()
53
55
  </script>
54
56
 
55
57
  <template>
56
58
  <div
57
59
  ref="popoutAnchor"
60
+ :aria-describedby="id"
58
61
  @mouseenter="hoverAnchor = true"
59
62
  @mouseleave="hoverAnchor = false"
60
63
  >
61
64
  <slot />
62
65
  </div>
63
66
  <Transition appear name="tooltip">
64
- <Popout v-if="showTooltip" :anchor="popoutAnchorRef" class="vui-tooltip" :placement>
67
+ <Popout v-if="showTooltip" :id :anchor="popoutAnchorRef" class="vui-tooltip" :placement name="tooltip">
65
68
  <slot name="tooltip" />
66
69
  </Popout>
67
70
  </Transition>