pimelon-ui 0.1.0-alpha.9 → 0.1.5
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 +6 -13
- package/src/components/Autocomplete.story.vue +25 -0
- package/src/components/Autocomplete.vue +38 -22
- package/src/components/Avatar.story.vue +28 -0
- package/src/components/Badge.story.vue +26 -0
- package/src/components/Button.story.vue +31 -0
- package/src/components/Checkbox.story.vue +22 -0
- package/src/components/Checkbox.vue +4 -1
- package/src/components/CommandPalette/CommandPalette.vue +110 -0
- package/src/components/CommandPalette/CommandPaletteItem.vue +28 -0
- package/src/components/Dialog.story.vue +39 -0
- package/src/components/Dropdown.story.vue +98 -0
- package/src/components/ErrorMessage.story.vue +28 -0
- package/src/components/FileUploader.story.vue +34 -0
- package/src/components/FormControl.story.vue +101 -0
- package/src/components/ListView/ListRow.vue +29 -0
- package/src/components/ListView/ListRowItem.vue +10 -0
- package/src/components/ListView/ListView.vue +116 -0
- package/src/components/Popover.story.vue +29 -0
- package/src/components/Progress.story.vue +34 -0
- package/src/components/Select.story.vue +21 -0
- package/src/components/Select.vue +1 -0
- package/src/components/Spinner.story.vue +14 -0
- package/src/components/Switch.story.vue +28 -0
- package/src/components/Switch.vue +11 -15
- package/src/components/TabButtons.vue +54 -0
- package/src/components/TextInput.story.vue +81 -0
- package/src/components/TextInput.vue +8 -2
- package/src/components/Textarea.vue +1 -0
- package/src/components/Tooltip.story.vue +11 -0
- package/src/env.d.ts +2 -0
- package/src/index.js +6 -0
- package/src/resources/documentResource.js +35 -2
- package/src/resources/listResource.js +5 -1
- package/src/style.css +6 -0
- package/src/utils/fileUploadHandler.ts +62 -57
- package/src/utils/{melonRequest.js → frappeRequest.js} +2 -9
- package/src/components/Avatar.stories.ts +0 -110
- package/src/components/Badge.stories.js +0 -149
- package/src/components/Button.stories.js +0 -173
- package/src/components/Divider.stories.ts +0 -110
- package/src/components/Dropdown.stories.ts +0 -73
- package/src/components/Progress.stories.js +0 -80
- package/src/components/Spinner.stories.ts +0 -13
- package/src/components/Switch.stories.js +0 -52
- package/src/components/TextInput.stories.ts +0 -143
- package/src/tokens/Color.vue +0 -194
- package/src/utils/tailwind.config.stories.js +0 -8
- package/src/vite-env.d.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pimelon-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A set of components and utilities for rapid UI development",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
"dev": "vite",
|
|
15
15
|
"build": "vite build",
|
|
16
16
|
"preview": "vite preview",
|
|
17
|
-
"
|
|
18
|
-
"build
|
|
17
|
+
"story:dev": "histoire dev",
|
|
18
|
+
"story:build": "histoire build",
|
|
19
|
+
"story:preview": "histoire preview"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
22
|
"src",
|
|
@@ -60,24 +61,16 @@
|
|
|
60
61
|
"vue-router": "^4.1.6"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
|
-
"@
|
|
64
|
-
"@storybook/addon-interactions": "^7.0.0-beta.61",
|
|
65
|
-
"@storybook/addon-links": "^7.0.0-beta.61",
|
|
66
|
-
"@storybook/blocks": "^7.0.0-alpha.8",
|
|
67
|
-
"@storybook/testing-library": "^0.0.14-next.1",
|
|
68
|
-
"@storybook/vue3": "^7.0.0-beta.61",
|
|
69
|
-
"@storybook/vue3-vite": "^7.0.0-beta.61",
|
|
64
|
+
"@histoire/plugin-vue": "^0.16.1",
|
|
70
65
|
"@vitejs/plugin-vue": "^4.0.0",
|
|
71
66
|
"autoprefixer": "^10.4.13",
|
|
72
67
|
"cross-fetch": "^3.1.5",
|
|
68
|
+
"histoire": "^0.16.2",
|
|
73
69
|
"husky": "^8.0.3",
|
|
74
70
|
"lint-staged": ">=10",
|
|
75
71
|
"postcss": "^8.4.21",
|
|
76
72
|
"prettier": "2.7.1",
|
|
77
73
|
"prettier-plugin-tailwindcss": "^0.1.13",
|
|
78
|
-
"react": "^18.2.0",
|
|
79
|
-
"react-dom": "^18.2.0",
|
|
80
|
-
"storybook": "^7.0.0-beta.61",
|
|
81
74
|
"tailwindcss": "^3.2.7",
|
|
82
75
|
"typescript": "^5.0.2",
|
|
83
76
|
"vite": "^4.1.0",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import Autocomplete from './Autocomplete.vue'
|
|
4
|
+
|
|
5
|
+
const value = ref('')
|
|
6
|
+
const options = [
|
|
7
|
+
{ label: 'John Doe', value: 'john-doe' },
|
|
8
|
+
{ label: 'Jane Doe', value: 'jane-doe' },
|
|
9
|
+
{ label: 'John Smith', value: 'john-smith' },
|
|
10
|
+
{ label: 'Jane Smith', value: 'jane-smith' },
|
|
11
|
+
{ label: 'John Wayne', value: 'john-wayne' },
|
|
12
|
+
{ label: 'Jane Wayne', value: 'jane-wayne' },
|
|
13
|
+
]
|
|
14
|
+
</script>
|
|
15
|
+
<template>
|
|
16
|
+
<Story :layout="{ width: 500, type: 'grid' }" autoPropsDisabled>
|
|
17
|
+
<div class="p-2">
|
|
18
|
+
<Autocomplete
|
|
19
|
+
:options="options"
|
|
20
|
+
v-model="value"
|
|
21
|
+
placeholder="Select person"
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
</Story>
|
|
25
|
+
</template>
|
|
@@ -2,28 +2,33 @@
|
|
|
2
2
|
<Combobox v-model="selectedValue" nullable v-slot="{ open: isComboboxOpen }">
|
|
3
3
|
<Popover class="w-full" v-model:show="showOptions">
|
|
4
4
|
<template #target="{ open: openPopover, togglePopover }">
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<span
|
|
12
|
-
class="overflow-hidden text-ellipsis whitespace-nowrap text-base leading-5"
|
|
13
|
-
v-if="selectedValue"
|
|
5
|
+
<slot name="target" v-bind="{ open: openPopover, togglePopover }">
|
|
6
|
+
<div class="w-full">
|
|
7
|
+
<button
|
|
8
|
+
class="flex h-7 w-full items-center justify-between gap-2 rounded bg-gray-100 px-2 py-1 transition-colors hover:bg-gray-200 focus:ring-2 focus:ring-gray-400"
|
|
9
|
+
:class="{ 'bg-gray-200': isComboboxOpen }"
|
|
10
|
+
@click="() => togglePopover()"
|
|
14
11
|
>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
<div class="flex items-center">
|
|
13
|
+
<slot name="prefix" />
|
|
14
|
+
<span
|
|
15
|
+
class="overflow-hidden text-ellipsis whitespace-nowrap text-base leading-5"
|
|
16
|
+
v-if="selectedValue"
|
|
17
|
+
>
|
|
18
|
+
{{ displayValue(selectedValue) }}
|
|
19
|
+
</span>
|
|
20
|
+
<span class="text-base leading-5 text-gray-500" v-else>
|
|
21
|
+
{{ placeholder || '' }}
|
|
22
|
+
</span>
|
|
23
|
+
</div>
|
|
24
|
+
<FeatherIcon
|
|
25
|
+
name="chevron-down"
|
|
26
|
+
class="h-4 w-4 text-gray-600"
|
|
27
|
+
aria-hidden="true"
|
|
28
|
+
/>
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
</slot>
|
|
27
32
|
</template>
|
|
28
33
|
<template #body="{ isOpen }">
|
|
29
34
|
<div v-show="isOpen">
|
|
@@ -36,6 +41,7 @@
|
|
|
36
41
|
>
|
|
37
42
|
<div class="relative w-full">
|
|
38
43
|
<ComboboxInput
|
|
44
|
+
ref="search"
|
|
39
45
|
class="form-input w-full"
|
|
40
46
|
type="text"
|
|
41
47
|
@change="
|
|
@@ -80,7 +86,10 @@
|
|
|
80
86
|
{ 'bg-gray-100': active },
|
|
81
87
|
]"
|
|
82
88
|
>
|
|
83
|
-
<slot
|
|
89
|
+
<slot
|
|
90
|
+
name="item-prefix"
|
|
91
|
+
v-bind="{ active, selected, option }"
|
|
92
|
+
/>
|
|
84
93
|
{{ option.label }}
|
|
85
94
|
</li>
|
|
86
95
|
</ComboboxOption>
|
|
@@ -168,6 +177,13 @@ export default {
|
|
|
168
177
|
query(q) {
|
|
169
178
|
this.$emit('update:query', q)
|
|
170
179
|
},
|
|
180
|
+
showOptions(val) {
|
|
181
|
+
if (val) {
|
|
182
|
+
this.$nextTick(() => {
|
|
183
|
+
this.$refs.search.el.focus()
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
},
|
|
171
187
|
},
|
|
172
188
|
methods: {
|
|
173
189
|
filterOptions(options) {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactive } from 'vue'
|
|
3
|
+
import Avatar from './Avatar.vue'
|
|
4
|
+
const state = reactive({
|
|
5
|
+
image: 'https://avatars.githubusercontent.com/u/499550?s=60&v=4',
|
|
6
|
+
label: 'EY',
|
|
7
|
+
size: 'md',
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const shapes = ['circle', 'square']
|
|
11
|
+
const sizes = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl']
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<Story :layout="{ type: 'grid', width: 300 }">
|
|
16
|
+
<Variant v-for="shape in shapes" :key="shape" :title="shape">
|
|
17
|
+
<Avatar :shape="shape" v-bind="state" />
|
|
18
|
+
</Variant>
|
|
19
|
+
<Variant v-for="shape in shapes" :key="shape" :title="shape">
|
|
20
|
+
<Avatar :shape="shape" v-bind="state" :image="null" />
|
|
21
|
+
</Variant>
|
|
22
|
+
|
|
23
|
+
<template #controls>
|
|
24
|
+
<HstText v-model="state.label" title="Label" />
|
|
25
|
+
<HstSelect v-model="state.size" :options="sizes" title="Size" />
|
|
26
|
+
</template>
|
|
27
|
+
</Story>
|
|
28
|
+
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactive } from 'vue'
|
|
3
|
+
import Badge from './Badge.vue'
|
|
4
|
+
const state = reactive({
|
|
5
|
+
theme: 'gray',
|
|
6
|
+
size: 'sm',
|
|
7
|
+
label: 'Badge',
|
|
8
|
+
})
|
|
9
|
+
const variants = ['solid', 'subtle', 'outline', 'ghost']
|
|
10
|
+
const themes = ['gray', 'blue', 'green', 'orange', 'red']
|
|
11
|
+
const sizes = ['sm', 'md', 'lg']
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<Story :layout="{ type: 'grid', width: 300 }">
|
|
16
|
+
<Variant v-for="variant in variants" :key="variant" :title="variant">
|
|
17
|
+
<Badge :variant="variant" v-bind="state">{{ state.label }}</Badge>
|
|
18
|
+
</Variant>
|
|
19
|
+
|
|
20
|
+
<template #controls>
|
|
21
|
+
<HstText v-model="state.label" title="Content" />
|
|
22
|
+
<HstSelect v-model="state.theme" :options="themes" title="Theme" />
|
|
23
|
+
<HstSelect v-model="state.size" :options="sizes" title="Size" />
|
|
24
|
+
</template>
|
|
25
|
+
</Story>
|
|
26
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactive } from 'vue'
|
|
3
|
+
import Button from './Button.vue'
|
|
4
|
+
const state = reactive({
|
|
5
|
+
theme: 'gray',
|
|
6
|
+
size: 'sm',
|
|
7
|
+
label: 'Button',
|
|
8
|
+
loading: false,
|
|
9
|
+
loadingText: null,
|
|
10
|
+
disabled: false,
|
|
11
|
+
link: null,
|
|
12
|
+
})
|
|
13
|
+
const variants = ['solid', 'subtle', 'outline', 'ghost']
|
|
14
|
+
const themes = ['gray', 'blue', 'green', 'red']
|
|
15
|
+
const sizes = ['sm', 'md', 'lg', 'xl', '2xl']
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<Story :layout="{ type: 'grid', width: 300 }">
|
|
20
|
+
<Variant v-for="variant in variants" :key="variant" :title="variant">
|
|
21
|
+
<Button :variant="variant" v-bind="state">{{ state.label }}</Button>
|
|
22
|
+
</Variant>
|
|
23
|
+
|
|
24
|
+
<template #controls>
|
|
25
|
+
<HstText v-model="state.label" title="Content" />
|
|
26
|
+
<HstCheckbox v-model="state.disabled" title="Disabled" />
|
|
27
|
+
<HstSelect v-model="state.theme" :options="themes" title="Theme" />
|
|
28
|
+
<HstSelect v-model="state.size" :options="sizes" title="Size" />
|
|
29
|
+
</template>
|
|
30
|
+
</Story>
|
|
31
|
+
</template>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactive } from 'vue'
|
|
3
|
+
import Checkbox from './Checkbox.vue'
|
|
4
|
+
|
|
5
|
+
const state = reactive({
|
|
6
|
+
size: 'sm',
|
|
7
|
+
value: false,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const sizes = ['sm', 'md']
|
|
11
|
+
</script>
|
|
12
|
+
<template>
|
|
13
|
+
<Story :layout="{ width: 500, type: 'grid' }">
|
|
14
|
+
<div class="p-2">
|
|
15
|
+
<Checkbox v-bind="state" v-model="state.value" label="Enable feature" />
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<template #controls>
|
|
19
|
+
<HstSelect v-model="state.size" :options="sizes" title="Size" />
|
|
20
|
+
</template>
|
|
21
|
+
</Story>
|
|
22
|
+
</template>
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
:id="htmlId"
|
|
17
17
|
:checked="Boolean(modelValue)"
|
|
18
18
|
@change="(e) => $emit('update:modelValue', (e.target as HTMLInputElement).checked)"
|
|
19
|
+
v-bind="attrs"
|
|
19
20
|
/>
|
|
20
21
|
<label class="block" :class="labelClasses" v-if="label" :for="htmlId">
|
|
21
22
|
{{ label }}
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
</div>
|
|
24
25
|
</template>
|
|
25
26
|
<script lang="ts" setup>
|
|
26
|
-
import { computed } from 'vue'
|
|
27
|
+
import { computed, useAttrs } from 'vue'
|
|
27
28
|
import { useId } from '../utils/useId'
|
|
28
29
|
|
|
29
30
|
interface CheckboxProps {
|
|
@@ -40,6 +41,8 @@ const props = withDefaults(defineProps<CheckboxProps>(), {
|
|
|
40
41
|
padding: false,
|
|
41
42
|
})
|
|
42
43
|
|
|
44
|
+
const attrs = useAttrs()
|
|
45
|
+
|
|
43
46
|
const htmlId = props.id ?? useId()
|
|
44
47
|
|
|
45
48
|
const labelClasses = computed(() => {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template>
|
|
3
|
+
<Dialog
|
|
4
|
+
v-model="show"
|
|
5
|
+
:options="{ size: 'xl', position: 'top' }"
|
|
6
|
+
@after-leave="searchQuery = ''"
|
|
7
|
+
>
|
|
8
|
+
<template #body>
|
|
9
|
+
<div>
|
|
10
|
+
<Combobox nullable @update:model-value="select">
|
|
11
|
+
<div class="relative">
|
|
12
|
+
<div class="absolute inset-y-0 left-0 flex items-center pl-4.5">
|
|
13
|
+
<FeatherIcon name="search" class="h-4 w-4" />
|
|
14
|
+
</div>
|
|
15
|
+
<ComboboxInput
|
|
16
|
+
placeholder="Search"
|
|
17
|
+
class="w-full border-none bg-transparent py-3 pl-11.5 pr-4.5 text-base text-gray-800 placeholder-gray-500 focus:ring-0"
|
|
18
|
+
v-model="searchQuery"
|
|
19
|
+
autocomplete="off"
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
22
|
+
<ComboboxOptions
|
|
23
|
+
class="max-h-96 overflow-auto border-t border-gray-100"
|
|
24
|
+
static
|
|
25
|
+
:hold="true"
|
|
26
|
+
>
|
|
27
|
+
<div
|
|
28
|
+
class="mb-2 mt-4.5 first:mt-3"
|
|
29
|
+
v-for="(group, index) in groups"
|
|
30
|
+
:key="group.title"
|
|
31
|
+
>
|
|
32
|
+
<div
|
|
33
|
+
class="mb-2.5 px-4.5 text-base text-gray-600"
|
|
34
|
+
v-if="!group.hideTitle"
|
|
35
|
+
>
|
|
36
|
+
{{ group.title }}
|
|
37
|
+
</div>
|
|
38
|
+
<ComboboxOption
|
|
39
|
+
v-for="item in group.items"
|
|
40
|
+
:key="item.name"
|
|
41
|
+
v-slot="{ active }"
|
|
42
|
+
:value="item"
|
|
43
|
+
class="px-2.5"
|
|
44
|
+
:disabled="item.disabled"
|
|
45
|
+
>
|
|
46
|
+
<component
|
|
47
|
+
:is="group.component"
|
|
48
|
+
:item="item"
|
|
49
|
+
:active="active"
|
|
50
|
+
/>
|
|
51
|
+
</ComboboxOption>
|
|
52
|
+
</div>
|
|
53
|
+
</ComboboxOptions>
|
|
54
|
+
</Combobox>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
</Dialog>
|
|
58
|
+
</template>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<script setup>
|
|
62
|
+
import {
|
|
63
|
+
Combobox,
|
|
64
|
+
ComboboxInput,
|
|
65
|
+
ComboboxOption,
|
|
66
|
+
ComboboxOptions,
|
|
67
|
+
} from '@headlessui/vue'
|
|
68
|
+
import { computed, onBeforeUnmount, onMounted } from 'vue'
|
|
69
|
+
|
|
70
|
+
const emit = defineEmits(['update:show', 'update:searchQuery', 'select'])
|
|
71
|
+
const props = defineProps({
|
|
72
|
+
show: { type: Boolean, default: false },
|
|
73
|
+
searchQuery: { type: String, default: '' },
|
|
74
|
+
groups: { type: Array, default: () => [] },
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const show = computed({
|
|
78
|
+
get: () => props.show,
|
|
79
|
+
set: (value) => emit('update:show', value),
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const searchQuery = computed({
|
|
83
|
+
get: () => props.searchQuery,
|
|
84
|
+
set: (value) => emit('update:searchQuery', value),
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
function select(item) {
|
|
88
|
+
emit('select', item)
|
|
89
|
+
show.value = false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function keydownWatcher(e) {
|
|
93
|
+
if (e.key === 'Escape' && show.value) {
|
|
94
|
+
show.value = false
|
|
95
|
+
e.preventDefault()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
e.key === 'k' &&
|
|
100
|
+
(e.ctrlKey || e.metaKey) &&
|
|
101
|
+
!e.target.classList.contains('ProseMirror')
|
|
102
|
+
) {
|
|
103
|
+
show.value = true
|
|
104
|
+
e.preventDefault()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
onMounted(() => window.addEventListener('keydown', keydownWatcher))
|
|
109
|
+
onBeforeUnmount(() => window.removeEventListener('keydown', keydownWatcher))
|
|
110
|
+
</script>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineProps({
|
|
3
|
+
item: { type: Object, required: true },
|
|
4
|
+
active: { type: Boolean, default: false },
|
|
5
|
+
})
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<div
|
|
10
|
+
class="flex w-full min-w-0 items-center rounded px-2 py-2 text-base font-medium text-gray-800"
|
|
11
|
+
:class="{ 'bg-gray-200': active }"
|
|
12
|
+
>
|
|
13
|
+
<component
|
|
14
|
+
:is="item.icon"
|
|
15
|
+
v-if="item.icon"
|
|
16
|
+
class="mr-3 h-4 w-4 text-gray-700"
|
|
17
|
+
/>
|
|
18
|
+
<span class="overflow-hidden text-ellipsis whitespace-nowrap">
|
|
19
|
+
{{ item.title }}
|
|
20
|
+
</span>
|
|
21
|
+
<span
|
|
22
|
+
v-if="item.description"
|
|
23
|
+
class="ml-auto whitespace-nowrap pl-2 text-gray-600"
|
|
24
|
+
>
|
|
25
|
+
{{ item.description }}
|
|
26
|
+
</span>
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import Dialog from './Dialog.vue'
|
|
4
|
+
import Button from './Button.vue'
|
|
5
|
+
|
|
6
|
+
const dialog1 = ref(false)
|
|
7
|
+
const dialog2 = ref(false)
|
|
8
|
+
</script>
|
|
9
|
+
<template>
|
|
10
|
+
<Story :layout="{ width: 500, type: 'grid' }">
|
|
11
|
+
<Variant title="With options" autoPropsDisabled>
|
|
12
|
+
<Button @click="dialog1 = true">Show Dialog</Button>
|
|
13
|
+
<Dialog
|
|
14
|
+
:options="{
|
|
15
|
+
title: 'Confirm',
|
|
16
|
+
message: 'Are you sure you want to confirm this action?',
|
|
17
|
+
size: 'xl',
|
|
18
|
+
actions: [{ label: 'Confirm', variant: 'solid', onClick: () => {} }],
|
|
19
|
+
}"
|
|
20
|
+
v-model="dialog1"
|
|
21
|
+
/>
|
|
22
|
+
</Variant>
|
|
23
|
+
<Variant title="With slots" autoPropsDisabled>
|
|
24
|
+
<Button @click="dialog2 = true">Show Dialog</Button>
|
|
25
|
+
<Dialog v-model="dialog2">
|
|
26
|
+
<template #body-title>
|
|
27
|
+
<h3>Custom Title</h3>
|
|
28
|
+
</template>
|
|
29
|
+
<template #body-content>
|
|
30
|
+
<p>Custom Body</p>
|
|
31
|
+
</template>
|
|
32
|
+
<template #actions>
|
|
33
|
+
<Button variant="solid">Confirm</Button>
|
|
34
|
+
<Button class="ml-2" @click="dialog2 = false">Close</Button>
|
|
35
|
+
</template>
|
|
36
|
+
</Dialog>
|
|
37
|
+
</Variant>
|
|
38
|
+
</Story>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { h } from 'vue'
|
|
3
|
+
import Dropdown from './Dropdown.vue'
|
|
4
|
+
import FeatherIcon from './FeatherIcon.vue'
|
|
5
|
+
import Button from './Button.vue'
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<Story :layout="{ type: 'grid', width: 300 }">
|
|
10
|
+
<Variant title="Basic">
|
|
11
|
+
<Dropdown
|
|
12
|
+
:options="[
|
|
13
|
+
{
|
|
14
|
+
label: 'Edit Title',
|
|
15
|
+
onClick: () => {},
|
|
16
|
+
icon: () => h(FeatherIcon, { name: 'edit-2' }),
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: 'Manage Members',
|
|
20
|
+
onClick: () => {},
|
|
21
|
+
icon: () => h(FeatherIcon, { name: 'users' }),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
label: 'Delete this project',
|
|
25
|
+
onClick: () => {},
|
|
26
|
+
icon: () => h(FeatherIcon, { name: 'trash' }),
|
|
27
|
+
},
|
|
28
|
+
]"
|
|
29
|
+
/>
|
|
30
|
+
</Variant>
|
|
31
|
+
|
|
32
|
+
<Variant title="Button prop">
|
|
33
|
+
<Dropdown
|
|
34
|
+
:options="[
|
|
35
|
+
{
|
|
36
|
+
label: 'Edit Title',
|
|
37
|
+
onClick: () => {},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: 'Manage Members',
|
|
41
|
+
onClick: () => {},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: 'Delete this project',
|
|
45
|
+
onClick: () => {},
|
|
46
|
+
},
|
|
47
|
+
]"
|
|
48
|
+
:button="{
|
|
49
|
+
label: 'Actions',
|
|
50
|
+
}"
|
|
51
|
+
/>
|
|
52
|
+
</Variant>
|
|
53
|
+
|
|
54
|
+
<Variant title="Custom Button and Groups">
|
|
55
|
+
<Dropdown
|
|
56
|
+
:options="[
|
|
57
|
+
{
|
|
58
|
+
group: 'Manage',
|
|
59
|
+
items: [
|
|
60
|
+
{
|
|
61
|
+
label: 'Edit Title',
|
|
62
|
+
icon: () => h(FeatherIcon, { name: 'edit' }),
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: 'Manage Members',
|
|
66
|
+
icon: () => h(FeatherIcon, { name: 'users' }),
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
group: 'Delete',
|
|
72
|
+
items: [
|
|
73
|
+
{
|
|
74
|
+
label: 'Delete users',
|
|
75
|
+
icon: () => h(FeatherIcon, { name: 'edit' }),
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
label: 'Delete this project',
|
|
79
|
+
icon: () => h(FeatherIcon, { name: 'trash' }),
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
]"
|
|
84
|
+
>
|
|
85
|
+
<Button>
|
|
86
|
+
<template #icon>
|
|
87
|
+
<FeatherIcon name="more-horizontal" class="h-4 w-4" />
|
|
88
|
+
</template>
|
|
89
|
+
</Button>
|
|
90
|
+
</Dropdown>
|
|
91
|
+
</Variant>
|
|
92
|
+
|
|
93
|
+
<template #controls>
|
|
94
|
+
<!-- <HstText v-model="state.label" title="Label" />
|
|
95
|
+
<HstSelect v-model="state.size" :options="sizes" title="Size" /> -->
|
|
96
|
+
</template>
|
|
97
|
+
</Story>
|
|
98
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import ErrorMessage from './ErrorMessage.vue'
|
|
4
|
+
|
|
5
|
+
const message = ref('Invalid value')
|
|
6
|
+
const error = new Error('There was an error')
|
|
7
|
+
</script>
|
|
8
|
+
<template>
|
|
9
|
+
<Story :layout="{ width: 500, type: 'grid' }" autoPropsDisabled>
|
|
10
|
+
<Variant title="String message">
|
|
11
|
+
<ErrorMessage :message="message" />
|
|
12
|
+
</Variant>
|
|
13
|
+
|
|
14
|
+
<Variant title="Error object">
|
|
15
|
+
<ErrorMessage :message="error" />
|
|
16
|
+
|
|
17
|
+
<template #source>
|
|
18
|
+
<textarea v-pre>
|
|
19
|
+
<ErrorMessage :message="Error('There was an error')" />
|
|
20
|
+
</textarea>
|
|
21
|
+
</template>
|
|
22
|
+
</Variant>
|
|
23
|
+
|
|
24
|
+
<Variant title="Falsy value">
|
|
25
|
+
<ErrorMessage message="" />
|
|
26
|
+
</Variant>
|
|
27
|
+
</Story>
|
|
28
|
+
</template>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import FileUploader from './FileUploader.vue'
|
|
3
|
+
import Button from './Button.vue'
|
|
4
|
+
|
|
5
|
+
const validateFileFunction = (fileObject) => {}
|
|
6
|
+
const onSuccess = (file) => {}
|
|
7
|
+
</script>
|
|
8
|
+
<template>
|
|
9
|
+
<Story :layout="{ width: 500, type: 'grid' }" autoPropsDisabled>
|
|
10
|
+
<FileUploader
|
|
11
|
+
:fileTypes="['image/*']"
|
|
12
|
+
:validateFile="validateFileFunction"
|
|
13
|
+
@success="onSuccess"
|
|
14
|
+
>
|
|
15
|
+
<template
|
|
16
|
+
v-slot="{
|
|
17
|
+
file,
|
|
18
|
+
uploading,
|
|
19
|
+
progress,
|
|
20
|
+
uploaded,
|
|
21
|
+
message,
|
|
22
|
+
error,
|
|
23
|
+
total,
|
|
24
|
+
success,
|
|
25
|
+
openFileSelector,
|
|
26
|
+
}"
|
|
27
|
+
>
|
|
28
|
+
<Button @click="openFileSelector" :loading="uploading">
|
|
29
|
+
{{ uploading ? `Uploading ${progress}%` : 'Upload Image' }}
|
|
30
|
+
</Button>
|
|
31
|
+
</template>
|
|
32
|
+
</FileUploader>
|
|
33
|
+
</Story>
|
|
34
|
+
</template>
|