@dolanske/vui 0.4.0 → 0.5.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dolanske/vui",
3
3
  "type": "module",
4
- "version": "0.4.0",
4
+ "version": "0.5.0",
5
5
  "private": false,
6
6
  "description": "Brother in Christ there's a new UI library ",
7
7
  "author": "dolanske",
@@ -48,7 +48,7 @@
48
48
  "dependencies": {
49
49
  "@floating-ui/vue": "^1.1.5",
50
50
  "@types/node": "^22.9.0",
51
- "@vuepic/vue-datepicker": "^10.0.0",
51
+ "@vuepic/vue-datepicker": "^11.0.1",
52
52
  "@vueuse/core": "^11.2.0",
53
53
  "sass": "^1.80.6",
54
54
  "vaul-vue": "^0.2.0",
package/src/App.vue CHANGED
@@ -1,20 +1,17 @@
1
1
  <script setup lang="ts">
2
2
  import { ref } from 'vue'
3
- import Avatar from './components/Avatar/Avatar.vue'
4
- import Button from './components/Button/Button.vue'
5
3
 
6
- import Divider from './components/Divider/Divider.vue'
7
- import Dropdown from './components/Dropdown/Dropdown.vue'
4
+ import Button from './components/Button/Button.vue'
5
+ import Calendar from './components/Calendar/Calendar.vue'
8
6
  import DropdownItem from './components/Dropdown/DropdownItem.vue'
9
- import Flex from './components/Flex/Flex.vue'
10
7
  import Sidebar from './components/Sidebar/Sidebar.vue'
11
8
  import Tab from './components/Tabs/Tab.vue'
12
- import Tabs from './components/Tabs/Tabs.vue'
13
9
 
14
- import Tooltip from './components/Tooltip/Tooltip.vue'
10
+ import Tabs from './components/Tabs/Tabs.vue'
15
11
 
16
12
  const tab = ref('components')
17
13
  const sidebarOpen = ref(false)
14
+ const date = ref(new Date())
18
15
  </script>
19
16
 
20
17
  <template>
@@ -37,48 +34,29 @@ const sidebarOpen = ref(false)
37
34
  </Sidebar>
38
35
 
39
36
  <main vaul-drawer-wrapper>
40
- <Button icon="ph:sidebar-simple-bold" square @click="sidebarOpen = !sidebarOpen" />
41
37
  <Tabs v-model="tab" expand>
38
+ <template #start>
39
+ <Button icon="ph:sidebar-simple-bold" square @click="sidebarOpen = !sidebarOpen" />
40
+ </template>
41
+
42
42
  <Tab id="home" label="Home" icon="ph:house" />
43
43
  <Tab id="components" label="Components" />
44
44
  <Tab id="typography" label="Typography" />
45
45
  </Tabs>
46
46
  <div style="margin-bottom: 64px;" />
47
47
  <div v-if="tab === 'home'">
48
- home
48
+ <div class="container container-xs">
49
+ <p class="text-overflow mb-m">
50
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Fugiat deleniti alias a, non incidunt temporibus aperiam ducimus enim ut necessitatibus, id ab quod ex architecto, accusamus esse sed nostrum quae?
51
+ </p>
52
+ <p v-for="item in 10" :key="item" :class="`text-overflow-${item} mb-m`">
53
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Fugiat deleniti alias a, non incidunt temporibus aperiam ducimus enim ut necessitatibus, id ab quod ex architecto, accusamus esse sed nostrum quae? Lorem ipsum dolor sit amet consectetur, adipisicing elit. Mollitia ducimus perferendis, soluta molestias saepe incidunt officiis doloribus alias omnis id praesentium impedit possimus quibusdam? Laudantium porro fugiat eius. Voluptatum, id!
54
+ </p>
55
+ </div>
49
56
  </div>
50
57
 
51
58
  <div v-if="tab === 'components'">
52
- <h1 class="mb-xl">
53
- Hii
54
- <span class="counter text-color-accent">129</span>
55
- </h1>
56
- <p>I am down</p>
57
-
58
- <Flex>
59
- <Avatar size="s" />
60
- <Avatar size="m" />
61
- <Avatar size="l" />
62
- <Avatar :size="80" />
63
- </Flex>
64
-
65
- <Divider />
66
-
67
- <Tooltip>
68
- <span class="block mb-xl">Hello world</span>
69
- <template #tooltip>
70
- <p>
71
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro, animi! Nobis maxime neque cumque, in a amet voluptatibus tenetur dicta eos delectus illo soluta aliquam voluptatum nulla? In, incidunt asperiores?
72
- </p>
73
- </template>
74
- </Tooltip>
75
-
76
- <Dropdown>
77
- <template #trigger="{ toggle }">
78
- <Button aria-label="Dropdown menu trigger" square icon="ph:x" @click="toggle" />
79
- </template>
80
- <DropdownItem>Hiiii</DropdownItem>
81
- </Dropdown>
59
+ <Calendar v-model="date" time />
82
60
  </div>
83
61
  <div v-else-if="tab === 'typography'" class="typeset" :style="{ maxWidth: '688px', margin: 'auto' }">
84
62
  <h1>The Joke Tax Chronicles</h1>
@@ -58,6 +58,7 @@
58
58
  font-size: var(--font-size-m);
59
59
  font-weight: 500;
60
60
  color: var(--color-text);
61
+ border-radius: var(--border-radius-m);
61
62
 
62
63
  &:hover {
63
64
  text-decoration: underline;
@@ -50,7 +50,7 @@ const height = computed(() => {
50
50
  case Size.s: return '24px'
51
51
  case Size.l: return '40px'
52
52
  case Size.m:
53
- default: return '34px'
53
+ default: return 'var(--interactive-el-height)'
54
54
  }
55
55
  })
56
56
 
@@ -1,5 +1,5 @@
1
1
  .vui-button {
2
- --button-height: 34px;
2
+ --button-height: var(--interactive-el-height);
3
3
  --button-padding: 8px;
4
4
 
5
5
  &.disabled {
@@ -41,6 +41,8 @@
41
41
 
42
42
  &.square {
43
43
  width: var(--button-height);
44
+ min-width: var(--button-height);
45
+ min-height: var(--button-height);
44
46
  padding: 0;
45
47
  }
46
48
 
@@ -2,6 +2,7 @@
2
2
  import type { VueDatePickerProps } from '@vuepic/vue-datepicker'
3
3
  import { Icon } from '@iconify/vue'
4
4
  import VueDatePicker from '@vuepic/vue-datepicker'
5
+ import { useAttrs } from 'vue'
5
6
  import '@vuepic/vue-datepicker/dist/main.css'
6
7
  import './calendar.scss'
7
8
 
@@ -16,12 +17,14 @@ const props = withDefaults(defineProps<VueDatePickerProps & {
16
17
  })
17
18
 
18
19
  const ICON_SIZE = 18
20
+ const attrs = useAttrs()
19
21
  </script>
20
22
 
21
23
  <template>
22
24
  <VueDatePicker
23
- v-bind="props"
24
- :style="props.expand || !$slots.trigger
25
+ v-bind="{ ...props, ...attrs }"
26
+ class="vui-calendar"
27
+ :style="props.expand ?? !$slots.trigger
25
28
  ? undefined
26
29
  : { display: 'inline-block', width: 'auto' }
27
30
  "
@@ -54,3 +54,7 @@
54
54
  .dp--clear-btn {
55
55
  inset-inline-end: 8px;
56
56
  }
57
+
58
+ .dp__input {
59
+ height: var(--interactive-el-height);
60
+ }
@@ -43,10 +43,9 @@ const id = useId()
43
43
  <span class="vui-checkbox-icon">
44
44
  <Icon :icon="checked ? iconOn : iconOff" />
45
45
  </span>
46
- <p v-if="label" class="vui-checkbox-content">{{ label }}</p>
47
- <div v-else-if="slots.default" class="vui-checkbox-content">
48
- <slot />
49
- </div>
46
+ <p v-if="label || slots.default" class="vui-checkbox-content">
47
+ <slot>{{ label }}</slot>
48
+ </p>
50
49
  </label>
51
50
  </div>
52
51
  </template>
@@ -38,7 +38,16 @@
38
38
  }
39
39
 
40
40
  input {
41
- display: none;
41
+ width: 1px;
42
+ height: 1px;
43
+ position: absolute;
44
+ overflow: hidden;
45
+ outline: none !important;
46
+
47
+ &:focus-visible + label .vui-checkbox-icon {
48
+ outline: 2px solid var(--color-text);
49
+ border-radius: var(--border-radius-s);
50
+ }
42
51
 
43
52
  & + label {
44
53
  display: grid;
@@ -1,5 +1,6 @@
1
1
  <script setup lang='ts'>
2
2
  import { Icon } from '@iconify/vue'
3
+ import './dropdown-item.scss'
3
4
 
4
5
  interface Props {
5
6
  disabled?: boolean
@@ -0,0 +1,77 @@
1
+ .vui-dropdown-item {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: flex-start;
5
+ width: fit-content;
6
+ gap: var(--space-xs);
7
+ width: 100%;
8
+ height: var(--interactive-el-height);
9
+ border-radius: var(--border-radius-m);
10
+ font-size: var(--font-size-s);
11
+ cursor: default;
12
+ transition: var(--transition);
13
+ padding-inline: var(--space-s);
14
+ white-space: nowrap;
15
+ margin-bottom: 4px;
16
+
17
+ &:last-child {
18
+ margin-bottom: 0;
19
+ }
20
+
21
+ &.disabled {
22
+ pointer-events: none;
23
+
24
+ &:hover {
25
+ background-color: transparent;
26
+ }
27
+
28
+ .vui-dropdown-item-slot,
29
+ .vui-dropdown-item-icon svg path {
30
+ color: var(--color-text-lighter);
31
+ }
32
+
33
+ .vui-dropdown-item-hint {
34
+ visibility: hidden;
35
+ opacity: 0;
36
+ z-index: -1;
37
+ }
38
+ }
39
+
40
+ .vui-dropdown-item-icon {
41
+ width: 16px;
42
+ height: 16px;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+
47
+ svg {
48
+ width: 16px;
49
+ height: 16px;
50
+
51
+ path {
52
+ color: var(--color-text-light);
53
+ }
54
+ }
55
+ }
56
+
57
+ .vui-dropdown-item-hint {
58
+ margin-left: 8px;
59
+ font-size: 0.9rem;
60
+ // white-space: nowrap;
61
+ color: var(--color-text-lighter);
62
+ }
63
+
64
+ .vui-dropdown-item-slot {
65
+ flex: 1;
66
+ display: flex;
67
+ align-items: center;
68
+ justify-content: flex-start;
69
+ color: var(--color-text);
70
+ flex-wrap: nowrap;
71
+ }
72
+
73
+ &.selected,
74
+ &:hover {
75
+ background-color: var(--color-button-gray-hover);
76
+ }
77
+ }
@@ -10,85 +10,6 @@
10
10
  padding: 4px;
11
11
  }
12
12
 
13
- .vui-dropdown-item {
14
- display: flex;
15
- align-items: center;
16
- justify-content: flex-start;
17
- width: fit-content;
18
- gap: var(--space-xs);
19
- width: 100%;
20
- // TODO: make button-height variable global and use it here and everywhere else
21
- height: 34px;
22
- border-radius: var(--border-radius-m);
23
- font-size: var(--font-size-s);
24
- cursor: default;
25
- transition: var(--transition);
26
- padding-inline: var(--space-s);
27
- white-space: nowrap;
28
- margin-bottom: 4px;
29
-
30
- &:last-child {
31
- margin-bottom: 0;
32
- }
33
-
34
- &.disabled {
35
- pointer-events: none;
36
-
37
- &:hover {
38
- background-color: transparent;
39
- }
40
-
41
- .vui-dropdown-item-slot,
42
- .vui-dropdown-item-icon svg path {
43
- color: var(--color-text-lighter);
44
- }
45
-
46
- .vui-dropdown-item-hint {
47
- visibility: hidden;
48
- opacity: 0;
49
- z-index: -1;
50
- }
51
- }
52
-
53
- .vui-dropdown-item-icon {
54
- width: 16px;
55
- height: 16px;
56
- display: flex;
57
- align-items: center;
58
- justify-content: center;
59
-
60
- svg {
61
- width: 16px;
62
- height: 16px;
63
-
64
- path {
65
- color: var(--color-text-light);
66
- }
67
- }
68
- }
69
-
70
- .vui-dropdown-item-hint {
71
- margin-left: 8px;
72
- font-size: 0.9rem;
73
- // white-space: nowrap;
74
- color: var(--color-text-lighter);
75
- }
76
-
77
- .vui-dropdown-item-slot {
78
- flex: 1;
79
- display: flex;
80
- align-items: center;
81
- justify-content: flex-start;
82
- color: var(--color-text);
83
- flex-wrap: nowrap;
84
- }
85
-
86
- &.selected,
87
- &:hover {
88
- background-color: var(--color-button-gray-hover);
89
- }
90
- }
91
-
92
13
  .vui-dropdown-title {
93
14
  position: relative;
94
15
  display: flex;
@@ -32,7 +32,10 @@ export interface FlexProps {
32
32
  expand?: boolean
33
33
  }
34
34
 
35
- const props = defineProps<FlexProps>()
35
+ const props = withDefaults(defineProps<FlexProps>(), {
36
+ // eslint-disable-next-line vue/require-valid-default-prop
37
+ gap: 's',
38
+ })
36
39
 
37
40
  // Flex gap
38
41
  const ag = computed(() => typeof props.gap === 'number'
@@ -15,7 +15,10 @@ interface Props {
15
15
  areas?: string[]
16
16
  }
17
17
 
18
- const props = defineProps<Props>()
18
+ const props = withDefaults(defineProps<Props>(), {
19
+ // eslint-disable-next-line vue/require-valid-default-prop
20
+ gap: 's',
21
+ })
19
22
 
20
23
  const ag = computed(() => typeof props.gap === 'number'
21
24
  ? formatUnitValue(props.gap)
@@ -9,7 +9,7 @@
9
9
  --input-color-border-strong: var(--color-border-strong);
10
10
  --input-color-border-weak: var(--color-border-weak);
11
11
 
12
- --input-height: 34px;
12
+ --input-height: var(--interactive-el-height);
13
13
  --input-padding: 8px;
14
14
  --textarea-padding: 8px;
15
15
 
@@ -83,7 +83,7 @@
83
83
  height: var(--input-height);
84
84
  line-height: var(--input-height);
85
85
  color: var(--color-text);
86
- outline: none !important;
86
+ outline: none;
87
87
  padding-inline: var(--input-padding);
88
88
  font-size: var(--font-size-ms);
89
89
  width: 100%;
@@ -91,7 +91,7 @@
91
91
 
92
92
  &:has(input:focus-visible),
93
93
  &:focus-visible {
94
- border-color: var(--input-color-border-strong);
94
+ outline: 2px solid var(--color-text);
95
95
  }
96
96
  }
97
97
 
@@ -142,7 +142,7 @@
142
142
  textarea {
143
143
  line-height: 1.3em;
144
144
  height: auto;
145
- min-height: 4lh;
145
+ min-height: 5lh;
146
146
  field-sizing: content;
147
147
  padding: var(--textarea-padding);
148
148
  transition: none;
@@ -1,5 +1,5 @@
1
1
  .vui-kbd {
2
- display: flex;
2
+ display: inline-flex;
3
3
  align-items: center;
4
4
  justify-content: center;
5
5
  height: 24px;
@@ -33,7 +33,16 @@
33
33
  }
34
34
 
35
35
  input {
36
- display: none;
36
+ width: 1px;
37
+ height: 1px;
38
+ position: absolute;
39
+ overflow: hidden;
40
+ outline: none !important;
41
+
42
+ &:focus-visible + label .vui-radio-icon {
43
+ outline: 2px solid var(--color-text);
44
+ border-radius: 999px;
45
+ }
37
46
 
38
47
  & + label {
39
48
  display: grid;
@@ -1,6 +1,7 @@
1
1
  <script setup lang='ts'>
2
- import { useCssVar, useMouse } from '@vueuse/core'
3
- import { computed, useSlots, useTemplateRef, watch } from 'vue'
2
+ import { useCssVar, useMouse, useTimeoutFn, watchThrottled } from '@vueuse/core'
3
+ import { computed, useSlots, useTemplateRef } from 'vue'
4
+ import { isNil } from '../../shared/helpers'
4
5
  import './sidebar.scss'
5
6
 
6
7
  const props = withDefaults(defineProps<Props>(), {
@@ -47,21 +48,37 @@ const slotProps = computed(() => ({
47
48
  open,
48
49
  }))
49
50
 
51
+ // Sidebar `appear` implementation
52
+ const { start, stop, isPending } = useTimeoutFn(() => {
53
+ open.value = true
54
+ }, 250)
55
+
56
+ const APPEAR_OFFSET = 32
57
+
50
58
  const { x } = useMouse()
51
59
 
52
- let appearActive = true
53
- watch(x, (pos) => {
60
+ watchThrottled(x, (pos) => {
54
61
  if (!props.appear)
55
62
  return
56
63
 
57
- if (pos < 20 && !open.value) {
58
- open.value = true
59
- appearActive = true
64
+ if (pos <= APPEAR_OFFSET && pos >= 0 && !open.value && !isPending.value) {
65
+ start()
66
+ }
67
+ else if (isPending.value) {
68
+ stop()
60
69
  }
61
- else if (appearActive && pos > props.width) {
70
+
71
+ const openWidth = props.mini
72
+ ? 65
73
+ : props.floaty
74
+ ? props.width
75
+ : props.width - (isNil(offset.value) ? 0 : Number(offset.value?.replace('px', '')))
76
+
77
+ if ((pos > APPEAR_OFFSET + openWidth || pos < 0) && open.value) {
62
78
  open.value = false
63
- appearActive = false
64
79
  }
80
+ }, {
81
+ throttle: 100,
65
82
  })
66
83
  </script>
67
84
 
@@ -28,7 +28,15 @@
28
28
  }
29
29
 
30
30
  input {
31
- display: none;
31
+ width: 1px;
32
+ height: 1px;
33
+ position: absolute;
34
+ overflow: hidden;
35
+ outline: none !important;
36
+
37
+ &:focus-visible + label .vui-switch-icon {
38
+ outline: 2px solid var(--color-text);
39
+ }
32
40
 
33
41
  & + label {
34
42
  display: grid;
@@ -18,6 +18,8 @@ const {
18
18
 
19
19
  const slots = defineSlots<{
20
20
  default: () => Array<VNode & { props: TabProps }>
21
+ start: unknown
22
+ end: unknown
21
23
  }>()
22
24
 
23
25
  const active = defineModel()
@@ -67,6 +69,7 @@ onMounted(() => {
67
69
  : `vui-tabs-variant-${variant}`,
68
70
  ]"
69
71
  >
72
+ <slot name="start" />
70
73
  <Component
71
74
  :is="vnode"
72
75
  v-for="vnode of slots.default()"
@@ -74,6 +77,10 @@ onMounted(() => {
74
77
  :class="{ active: vnode.props.id === active }"
75
78
  @click="active = vnode.props.id"
76
79
  />
80
+ <template v-if="slots.end">
81
+ <div v-if="!!!expand" class="flex-1" />
82
+ <slot name="end" />
83
+ </template>
77
84
 
78
85
  <Transition name="fade" appear>
79
86
  <div ref="underline" class="vui-tab-underline" />
@@ -2,6 +2,7 @@
2
2
  display: flex;
3
3
  width: 100%;
4
4
  gap: 4px;
5
+ align-items: center;
5
6
  border-bottom: 1px solid var(--color-border);
6
7
  position: relative;
7
8
 
@@ -5,7 +5,7 @@
5
5
  // To overwrite any of these styles, simply create a style file and import it
6
6
  // after this one
7
7
  @use './reset.scss';
8
- @use './fonts.scss';
8
+ // @use './text.scss';
9
9
 
10
10
  :root {
11
11
  color-scheme: dark;
@@ -80,27 +80,29 @@
80
80
  --color-bg-blue-raised: rgb(26, 59, 119);
81
81
  --color-border-blue: rgb(20, 45, 112);
82
82
 
83
- --color-border: rgb(38, 38, 38);
83
+ --color-border: rgb(40, 40, 40);
84
84
  --color-border-strong: rgb(54, 54, 54);
85
85
  --color-border-weak: rgb(36, 36, 36);
86
86
 
87
- --color-accent: rgb(56, 214, 219);
87
+ --color-accent: rgb(238, 120, 17);
88
88
  --color-accent-disabled: hsl(from var(--color-accent) calc(h) calc(s * 0.4) calc(l));
89
89
  --color-bg-accent-lowered: hsl(from var(--color-accent) calc(h) s calc(l * 0.2));
90
- --color-bg-accent-raised: hsl(from var(--color-accent) calc(h) s calc(l * 0.5));
91
- --color-border-accent: hsl(from var(--color-accent) calc(h) s calc(l * 0.4));
90
+ --color-bg-accent-raised: hsl(from var(--color-accent) calc(h) s calc(l * 0.45));
91
+ --color-border-accent: hsl(from var(--color-accent) calc(h) s calc(l * 0.3));
92
92
 
93
93
  --box-shadow: 0 2px 12px rgba(8, 8, 8, 0.2);
94
94
  --box-shadow-strong: 0 4px 15px rgba(8, 8, 8, 0.4);
95
95
 
96
96
  --global-font: Geist, sans-serif;
97
97
  --global-font-mono: 'Geist Mono', 'Courier New', Courier, monospace;
98
+
99
+ --interactive-el-height: 34px;
98
100
  }
99
101
 
100
102
  :root {
101
103
  font-family: var(--global-font);
102
104
  font-size: 63.5%;
103
- // TODO remove
105
+ // TODO remove - should remove?
104
106
  position: relative;
105
107
  background-color: var(--color-bg);
106
108
 
@@ -143,6 +145,6 @@ body {
143
145
  @include meta.load-css('animation.scss');
144
146
  @include meta.load-css('tooltip.scss');
145
147
  @include meta.load-css('utils.scss');
146
- @include meta.load-css('fonts.scss');
148
+ @include meta.load-css('text.scss');
147
149
 
148
150
  @include meta.load-css('media-query.scss');
@@ -24,6 +24,10 @@ $containers: 'xs', 's', 'm', 'l', 'xl', 'xxl';
24
24
  display: flex;
25
25
  }
26
26
 
27
+ .flex-1 {
28
+ flex: 1;
29
+ }
30
+
27
31
  .inline-flex {
28
32
  display: inline-flex;
29
33
  }
@@ -36,6 +40,34 @@ $containers: 'xs', 's', 'm', 'l', 'xl', 'xxl';
36
40
  display: inline-block;
37
41
  }
38
42
 
43
+ .inline {
44
+ display: inline;
45
+ }
46
+
47
+ .x-center {
48
+ justify-content: center;
49
+ }
50
+
51
+ .x-start {
52
+ justify-content: flex-start;
53
+ }
54
+
55
+ .x-end {
56
+ justify-content: flex-end;
57
+ }
58
+
59
+ .y-start {
60
+ align-items: center;
61
+ }
62
+
63
+ .y-end {
64
+ align-self: flex-end;
65
+ }
66
+
67
+ .y-center {
68
+ align-items: center;
69
+ }
70
+
39
71
  // Sizes
40
72
  // Paddings & margins
41
73
  .mx-auto {