@veritree/ui 0.0.1 → 0.1.3

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/index.js CHANGED
@@ -1,12 +1,58 @@
1
- import vtAccordion from './src/vtAccordion/vtAccordion.vue';
2
- import vtAccordionButton from './src/vtAccordion/vtAccordionButton.vue';
3
- import vtAccordionGroup from './src/vtAccordion/vtAccordionGroup.vue';
4
- import vtAccordionPanel from './src/vtAccordion/vtAccordionPanel.vue';
1
+ import VTAlert from './src/Alerts/VTAlert.vue';
2
+
3
+ // import VTSpinner from './src/Spinner/VTSpinner.vue';
4
+
5
+ import VTButton from './src/Button/VTButton.vue';
6
+ // import VTButtonSave from './src/Button/VTButtonSave.vue';
7
+
8
+ import VTInput from './src/Input/VTInput.vue';
9
+ import VTInputDate from './src/Input/VTInputDate.vue';
10
+ import VTInputFile from './src/Input/VTInputFile.vue';
11
+ import VTInputUpload from './src/Input/VTInputUpload.vue';
12
+
13
+ import VTTextarea from './src/Textarea/VTTextarea.vue';
14
+
15
+ // import VTListbox from './src/Listbox/VTListbox.vue';
16
+ // import VTListboxButton from './src/Listbox/VTListboxButton.vue';
17
+ // import VTListboxOption from './src/Listbox/VTListboxOption.vue';
18
+ // import VTListboxOptions from './src/Listbox/VTListboxOptions.vue';
19
+
20
+ import VTModal from './src/Modal/VTModal.vue';
21
+
22
+ import VTAccordion from './src/Accordion/VTAccordion.vue';
23
+ import VTAccordionButton from './src/Accordion/VTAccordionButton.vue';
24
+ import VTAccordionGroup from './src/Accordion/VTAccordionGroup.vue';
25
+ import VTAccordionPanel from './src/Accordion/VTAccordionPanel.vue';
26
+
27
+ import VTTab from './src/Tabs/VTTab.vue';
28
+ import VTTabGroup from './src/Tabs/VTTabGroup.vue';
29
+ import VTTabList from './src/Tabs/VTTabList.vue';
30
+ import VTTabPanel from './src/Tabs/VTTabPanel.vue';
31
+ import VTTabPanels from './src/Tabs/VTTabPanels.vue';
5
32
 
6
33
  export {
7
- vtAccordion,
8
- vtAccordionButton,
9
- vtAccordionGroup,
10
- vtAccordionPanel,
34
+ VTAlert,
35
+ // VTSpinner,
36
+ VTButton,
37
+ // VTButtonSave,
38
+ VTInput,
39
+ VTInputDate,
40
+ VTInputFile,
41
+ VTInputUpload,
42
+ VTTextarea,
43
+ // VTListbox,
44
+ // VTListboxButton,
45
+ // VTListboxOption,
46
+ // VTListboxOptions,
47
+ VTModal,
48
+ VTAccordion,
49
+ VTAccordionButton,
50
+ VTAccordionGroup,
51
+ VTAccordionPanel,
52
+ VTTab,
53
+ VTTabGroup,
54
+ VTTabList,
55
+ VTTabPanel,
56
+ VTTabPanels,
11
57
  }
12
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veritree/ui",
3
- "version": "0.0.1",
3
+ "version": "0.1.3",
4
4
  "description": "veritree ui library",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -4,7 +4,7 @@
4
4
 
5
5
  <script>
6
6
  export default {
7
- name: 'vtAccordion',
7
+ name: 'VTAccordion',
8
8
  props: {
9
9
  multiple: {
10
10
  type: Boolean,
@@ -22,7 +22,7 @@ import IconChevronDown from '../icons/IconChevronDown.vue';
22
22
  import IconChevronUp from '../icons/IconChevronUp.vue';
23
23
 
24
24
  export default {
25
- name: 'vtAccordionButton',
25
+ name: 'VTAccordionButton',
26
26
  components: { IconChevronDown, IconChevronUp },
27
27
  inject: ['api'],
28
28
 
@@ -11,7 +11,7 @@
11
11
  import { genId } from '../utils/genId';
12
12
 
13
13
  export default {
14
- name: 'vtAccordionGroup',
14
+ name: 'VTAccordionGroup',
15
15
  inject: ['api'],
16
16
 
17
17
  data() {
@@ -11,10 +11,8 @@
11
11
  </template>
12
12
 
13
13
  <script>
14
- // import { keys } from '@/utils/keyboard';
15
-
16
14
  export default {
17
- name: 'vtAccordionPanel',
15
+ name: 'VTAccordionPanel',
18
16
  inject: ['api'],
19
17
 
20
18
  data() {
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <div v-if="isVisible" class="Alert" :class="classes" role="alert">
3
+ <slot></slot>
4
+ <button v-if="dismissable" class="Alert-close" @click="dismiss">
5
+ <IconClose />
6
+ </button>
7
+ </div>
8
+ </template>
9
+
10
+ <script>
11
+ import { IconClose } from '@veritree/icons';
12
+
13
+ export default {
14
+ name: 'VTAlert',
15
+
16
+ components: { IconClose },
17
+
18
+ model: {
19
+ prop: 'value',
20
+ },
21
+
22
+ props: {
23
+ dismissable: {
24
+ type: Boolean,
25
+ default: false,
26
+ },
27
+ variant: {
28
+ type: String,
29
+ default: '',
30
+ validate(value) {
31
+ if (value === '') return true;
32
+ return ['success', 'warning', 'error'].includes(value);
33
+ },
34
+ },
35
+ value: {
36
+ type: Boolean,
37
+ default: false,
38
+ },
39
+ },
40
+
41
+ data() {
42
+ return {
43
+ destroy: false,
44
+ };
45
+ },
46
+
47
+ computed: {
48
+ classes() {
49
+ return {
50
+ [`Alert--${this.variant}`]: this.variant,
51
+ };
52
+ },
53
+
54
+ isVisible() {
55
+ return this.value && !this.destroy;
56
+ },
57
+ },
58
+
59
+ watch: {
60
+ isVisible(newVal) {
61
+ if (newVal === false) this.destroy = false;
62
+ },
63
+ },
64
+
65
+ methods: {
66
+ dismiss() {
67
+ this.destroy = true;
68
+ this.$emit('input', false);
69
+ },
70
+ },
71
+ };
72
+ </script>
@@ -0,0 +1,86 @@
1
+ <template>
2
+ <component
3
+ :is="tag"
4
+ :class="classes"
5
+ :data-theme="theme"
6
+ :to="to"
7
+ class="Button"
8
+ type="button"
9
+ v-on="$listeners"
10
+ >
11
+ <slot></slot>
12
+ </component>
13
+ </template>
14
+
15
+ <script>
16
+ export default {
17
+ name: 'VTButton',
18
+
19
+ props: {
20
+ variant: {
21
+ type: String,
22
+ default: 'primary',
23
+ validator(value) {
24
+ return [
25
+ 'primary',
26
+ 'secondary',
27
+ 'tertiary',
28
+ 'danger',
29
+ 'custom',
30
+ 'icon',
31
+ ].includes(value);
32
+ },
33
+ },
34
+ size: {
35
+ type: String,
36
+ default: 'large',
37
+ validator(value) {
38
+ return ['large', 'small'].includes(value);
39
+ },
40
+ },
41
+ pill: {
42
+ type: Boolean,
43
+ default: false,
44
+ },
45
+ theme: {
46
+ type: String,
47
+ default: null,
48
+ validator(value) {
49
+ return ['dark'].includes(value);
50
+ },
51
+ },
52
+ to: {
53
+ type: [String, Object],
54
+ default: null,
55
+ },
56
+ },
57
+
58
+ computed: {
59
+ classes() {
60
+ const classes = {};
61
+
62
+ if (this.variant) {
63
+ classes[`Button--${this.variant}`] = true;
64
+ }
65
+
66
+ if (this.size) {
67
+ classes[`Button--${this.size}`] = true;
68
+ }
69
+
70
+ if (this.pill) {
71
+ classes['Button--pill'] = true;
72
+ }
73
+
74
+ return classes;
75
+ },
76
+
77
+ tag() {
78
+ if (this.to) {
79
+ return 'NuxtLink';
80
+ }
81
+
82
+ return 'button';
83
+ },
84
+ },
85
+ };
86
+ </script>
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <VTButton
3
+ class="relative"
4
+ :disabled="saving"
5
+ v-bind="$attrs"
6
+ v-on="$listeners"
7
+ >
8
+ <VTSpinner v-if="saving" class="absolute m-auto" />
9
+ <span :class="{ invisible: saving }"><slot></slot></span>
10
+ </VTButton>
11
+ </template>
12
+
13
+ <script>
14
+ import VTButton from './VTButton.vue';
15
+ import VTSpinner from '../Spinner/VTSpinner.vue';
16
+
17
+ export default {
18
+ components: { VTButton, VTSpinner },
19
+
20
+ props: {
21
+ saving: {
22
+ type: Boolean,
23
+ default: false,
24
+ },
25
+ },
26
+ };
27
+ </script>
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <input
3
+ :class="classes"
4
+ class="form-control"
5
+ :data-theme="theme"
6
+ :type="type"
7
+ :value="value"
8
+ v-on="listeners"
9
+ />
10
+ </template>
11
+
12
+ <script>
13
+ export default {
14
+ name: 'VTInput',
15
+
16
+ props: {
17
+ lazy: {
18
+ type: Boolean,
19
+ default: false,
20
+ },
21
+ type: {
22
+ type: String,
23
+ default: 'text',
24
+ },
25
+ theme: {
26
+ type: String,
27
+ default: null,
28
+ validator(value) {
29
+ return ['dark'].includes(value);
30
+ },
31
+ },
32
+ variant: {
33
+ type: [String, Object],
34
+ default: '',
35
+ validator(value) {
36
+ if (value === '' || typeof value === 'object') {
37
+ return true;
38
+ }
39
+
40
+ return ['success', 'warning', 'error'].includes(value);
41
+ },
42
+ },
43
+ value: {
44
+ type: [String, Number, Object, Array],
45
+ default: null,
46
+ },
47
+ },
48
+
49
+ computed: {
50
+ classes() {
51
+ const classes = {};
52
+
53
+ if (this.variant) {
54
+ classes[`form-control--${this.variant}`] = true;
55
+ }
56
+
57
+ return classes;
58
+ },
59
+
60
+ listeners() {
61
+ // `Object.assign` merges objects together to form a new object
62
+ return Object.assign(
63
+ {},
64
+ // We add all the listeners from the parent
65
+ this.$listeners,
66
+ // Then we can add custom listeners or override the
67
+ // behavior of some listeners.
68
+ {
69
+ // This ensures that the component works with v-model
70
+ input: (event) => {
71
+ if (this.lazy) return;
72
+ this.$emit('input', event.target.value);
73
+ },
74
+ blur: (event) => {
75
+ this.$emit('blur', event);
76
+ },
77
+ }
78
+ );
79
+ },
80
+ },
81
+ };
82
+ </script>
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <VTInput v-model="date" type="date" />
3
+ </template>
4
+
5
+ <script>
6
+ import VTInput from './VTInput.vue';
7
+
8
+ export default {
9
+ name: 'VTInputDate',
10
+
11
+ components: { VTInput },
12
+
13
+ model: {
14
+ prop: 'value',
15
+ event: 'input',
16
+ },
17
+
18
+ props: {
19
+ value: {
20
+ type: String,
21
+ default: '',
22
+ },
23
+ },
24
+
25
+ computed: {
26
+ date: {
27
+ get() {
28
+ return this.$date.format(this.value, 'YYYY-MM-DD');
29
+ },
30
+ set(newDate) {
31
+ this.$emit('input', newDate);
32
+ },
33
+ },
34
+ },
35
+ };
36
+ </script>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <div class="flex items-stretch gap-2">
3
+ <VTInput
4
+ ref="input"
5
+ type="file"
6
+ :value="value"
7
+ :theme="theme"
8
+ v-bind="$attrs"
9
+ @change="onChange"
10
+ />
11
+ <VTButton :theme="theme" @click.stop="onButtonClick">Browse</VTButton>
12
+ </div>
13
+ </template>
14
+
15
+ <script>
16
+ import VTButton from '../Button/VTButton.vue';
17
+ import VTInput from './VTInput.vue';
18
+
19
+ export default {
20
+ name: 'VTInputFile',
21
+
22
+ components: {
23
+ VTInput,
24
+ VTButton,
25
+ },
26
+
27
+ inheritAttrs: false,
28
+
29
+ props: {
30
+ theme: {
31
+ type: String,
32
+ default: null,
33
+ validator(value) {
34
+ return ['dark'].includes(value);
35
+ },
36
+ },
37
+ multiple: {
38
+ type: Boolean,
39
+ default: false,
40
+ },
41
+ },
42
+
43
+ data() {
44
+ return {
45
+ value: null,
46
+ };
47
+ },
48
+
49
+ methods: {
50
+ onChange(event) {
51
+ this.value = this.$refs.input.$el.value;
52
+ this.$emit('change', event);
53
+ },
54
+
55
+ onButtonClick() {
56
+ this.$refs.input.$el.click();
57
+ },
58
+ },
59
+ };
60
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <label
3
+ class="flex h-full w-full flex-col items-center justify-center rounded border-2 border-dotted border-white p-4 text-center hover:border-fl-500 hover:bg-fd-500"
4
+ :class="{ 'border-fl-500 bg-fd-500': isDraggingOver }"
5
+ @drop.prevent="onDrop"
6
+ @dragover.prevent="onDragOver"
7
+ @dragleave.prevent="onDragLeave"
8
+ >
9
+ <IconImagePlaceholder class="mb-3" />
10
+ <span>Drop your images here, or click to browse</span>
11
+ <VTInput type="file" class="sr-only" v-bind="$attrs" @change="onChange" />
12
+ </label>
13
+ </template>
14
+
15
+ <script>
16
+ import { IconImagePlaceholder } from '@veritree/icons';
17
+ import VTInput from './VTInput.vue';
18
+
19
+ export default {
20
+ name: 'VTInputFile',
21
+
22
+ components: {
23
+ VTInput,
24
+ IconImagePlaceholder,
25
+ },
26
+
27
+ inheritAttrs: false,
28
+
29
+ data() {
30
+ return {
31
+ isDraggingOver: false,
32
+ };
33
+ },
34
+
35
+ methods: {
36
+ onDrop(event) {
37
+ this.isDraggingOver = false;
38
+ this.$emit('drop', event);
39
+ },
40
+
41
+ onDragOver() {
42
+ this.isDraggingOver = true;
43
+ },
44
+
45
+ onDragLeave() {
46
+ this.isDraggingOver = false;
47
+ },
48
+
49
+ onChange(event) {
50
+ this.$emit('change', event);
51
+ },
52
+ },
53
+ };
54
+ </script>
@@ -0,0 +1,168 @@
1
+ <template>
2
+ <div class="Listbox"><slot></slot></div>
3
+ </template>
4
+
5
+ <script>
6
+ export default {
7
+ name: 'VTListbox',
8
+
9
+ provide() {
10
+ return {
11
+ api: () => {
12
+ // Get children components
13
+ const getListbox = () => this.listbox;
14
+ const getListboxValue = () => this.value;
15
+ const getListboxButton = () => this.listboxButton;
16
+
17
+ // Handle registering and unregistering
18
+ const registerListboxButton = (button) => {
19
+ if (!button) return;
20
+ this.listboxButton = button;
21
+ };
22
+
23
+ const registerListbox = (listbox) => {
24
+ if (!listbox) return;
25
+ this.listbox = listbox;
26
+ };
27
+
28
+ const registerOption = (option) => {
29
+ if (!option) return;
30
+ _register(this.listboxOptions, option);
31
+ };
32
+
33
+ const unregisterOption = (id) => {
34
+ _unregister(this.listboxOptions, id);
35
+ };
36
+
37
+ // Register helper functions
38
+ const _register = (arr, item) => {
39
+ arr.push(item);
40
+ };
41
+
42
+ const _unregister = (arr, id) => {
43
+ const index = _getIndex(arr, id);
44
+ arr.splice(index, 1);
45
+ };
46
+
47
+ // Handle focus and unfocus
48
+ const getFocusedIndex = () => {
49
+ let index = 0;
50
+
51
+ this.listboxOptions.forEach((option, i) => {
52
+ if (option.focused) index = i;
53
+ });
54
+
55
+ return index;
56
+ };
57
+
58
+ const focusOptionByFilter = (filter) => {
59
+ const optionIndex = this.listboxOptions.findIndex((option) => {
60
+ const text = option.$el.innerText;
61
+ return text.toLowerCase().indexOf(filter.toLowerCase()) === 0;
62
+ });
63
+
64
+ if (optionIndex >= 0) _focusOption(optionIndex);
65
+ };
66
+
67
+ const focusFirstOption = () => {
68
+ _focusOption(0);
69
+ };
70
+
71
+ const focusLastOption = () => {
72
+ _focusOption(this.listboxOptions.length - 1);
73
+ };
74
+
75
+ const focusPreviousOption = () => {
76
+ const focusedIndex = getFocusedIndex();
77
+ const previousIndex = focusedIndex - 1;
78
+
79
+ if (previousIndex >= 0) _focusOption(previousIndex);
80
+ };
81
+
82
+ const focusNextOption = () => {
83
+ const focusedIndex = getFocusedIndex();
84
+ const nextIndex = focusedIndex + 1;
85
+
86
+ if (nextIndex < this.listboxOptions.length) _focusOption(nextIndex);
87
+ };
88
+
89
+ const unfocusOptions = () => {
90
+ this.listboxOptions.forEach((option) => {
91
+ option.unfocus();
92
+ });
93
+ };
94
+
95
+ const _focusOption = (index) => {
96
+ unfocusOptions();
97
+ this.listboxOptions[index].focus();
98
+ };
99
+
100
+ const _getFocusedOption = () => {
101
+ return this.listboxOptions.filter((option) => option.focused)[0];
102
+ };
103
+
104
+ // handle selecting and unselecting
105
+ const selectOption = () => {
106
+ const focusedOption = _getFocusedOption();
107
+
108
+ // do nothing if option is already selected
109
+ if (focusedOption.selected) return;
110
+
111
+ // unselect all options
112
+ this.listboxOptions.forEach((option) => option.unselect());
113
+
114
+ // select focused option
115
+ if (focusedOption) {
116
+ focusedOption.select();
117
+ _onSelectedOption(focusedOption.value);
118
+ }
119
+ };
120
+
121
+ const _onSelectedOption = (value) => {
122
+ this.listbox.hide();
123
+ this.$emit('input', value);
124
+ this.$emit('change');
125
+ };
126
+
127
+ // Index helper functions
128
+ const _getIndex = (arr, id) => {
129
+ return arr.findIndex((item) => item.id === id);
130
+ };
131
+
132
+ return {
133
+ getListbox,
134
+ getListboxButton,
135
+ registerListboxButton,
136
+ registerListbox,
137
+ registerOption,
138
+ unregisterOption,
139
+ focusOptionByFilter,
140
+ focusFirstOption,
141
+ focusLastOption,
142
+ focusPreviousOption,
143
+ focusNextOption,
144
+ unfocusOptions,
145
+ selectOption,
146
+ getFocusedIndex,
147
+ getListboxValue,
148
+ };
149
+ },
150
+ };
151
+ },
152
+
153
+ props: {
154
+ value: {
155
+ type: [String, Number, Object],
156
+ default: null,
157
+ },
158
+ },
159
+
160
+ data() {
161
+ return {
162
+ listbox: null,
163
+ listboxButton: null,
164
+ listboxOptions: [],
165
+ };
166
+ },
167
+ };
168
+ </script>