@poveste/controls 0.1.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/LICENSE +22 -0
- package/README.md +5 -0
- package/dist/components/HstCopyIcon.story.vue.d.ts +2 -0
- package/dist/components/HstCopyIcon.vue.d.ts +7 -0
- package/dist/components/HstWrapper.vue.d.ts +20 -0
- package/dist/components/button/HstButton.story.vue.d.ts +2 -0
- package/dist/components/button/HstButton.vue.d.ts +13 -0
- package/dist/components/button/HstButtonGroup.story.vue.d.ts +2 -0
- package/dist/components/button/HstButtonGroup.vue.d.ts +22 -0
- package/dist/components/checkbox/HstCheckbox.story.vue.d.ts +2 -0
- package/dist/components/checkbox/HstCheckbox.vue.d.ts +19 -0
- package/dist/components/checkbox/HstCheckboxList.story.vue.d.ts +2 -0
- package/dist/components/checkbox/HstCheckboxList.vue.d.ts +22 -0
- package/dist/components/checkbox/HstSimpleCheckbox.story.vue.d.ts +2 -0
- package/dist/components/checkbox/HstSimpleCheckbox.vue.d.ts +12 -0
- package/dist/components/colorselect/HstColorSelect.story.vue.d.ts +2 -0
- package/dist/components/colorselect/HstColorSelect.vue.d.ts +19 -0
- package/dist/components/design-tokens/HstColorShades.story.vue.d.ts +2 -0
- package/dist/components/design-tokens/HstColorShades.vue.d.ts +19 -0
- package/dist/components/design-tokens/HstTokenGrid.story.vue.d.ts +2 -0
- package/dist/components/design-tokens/HstTokenGrid.vue.d.ts +26 -0
- package/dist/components/design-tokens/HstTokenList.story.vue.d.ts +2 -0
- package/dist/components/design-tokens/HstTokenList.vue.d.ts +21 -0
- package/dist/components/json/HstJson.story.vue.d.ts +2 -0
- package/dist/components/json/HstJson.vue.d.ts +19 -0
- package/dist/components/number/HstNumber.story.vue.d.ts +2 -0
- package/dist/components/number/HstNumber.vue.d.ts +19 -0
- package/dist/components/radio/HstRadio.story.vue.d.ts +2 -0
- package/dist/components/radio/HstRadio.vue.d.ts +22 -0
- package/dist/components/select/CustomSelect.vue.d.ts +22 -0
- package/dist/components/select/HstSelect.story.vue.d.ts +2 -0
- package/dist/components/select/HstSelect.vue.d.ts +22 -0
- package/dist/components/slider/HstSlider.story.vue.d.ts +2 -0
- package/dist/components/slider/HstSlider.vue.d.ts +16 -0
- package/dist/components/text/HstText.story.vue.d.ts +2 -0
- package/dist/components/text/HstText.vue.d.ts +19 -0
- package/dist/components/textarea/HstTextarea.story.vue.d.ts +2 -0
- package/dist/components/textarea/HstTextarea.vue.d.ts +19 -0
- package/dist/index.d.ts +1047 -0
- package/dist/index.es.css +1 -0
- package/dist/index.es.js +16392 -0
- package/dist/style-standalone.css +973 -0
- package/dist/types.d.ts +4 -0
- package/dist/utils.d.ts +1 -0
- package/package.json +72 -0
- package/postcss.config.cjs +8 -0
- package/src/components/HstCopyIcon.story.vue +26 -0
- package/src/components/HstCopyIcon.vue +38 -0
- package/src/components/HstWrapper.vue +41 -0
- package/src/components/button/HstButton.story.vue +30 -0
- package/src/components/button/HstButton.vue +26 -0
- package/src/components/button/HstButtonGroup.story.vue +79 -0
- package/src/components/button/HstButtonGroup.vue +71 -0
- package/src/components/checkbox/HstCheckbox.spec.ts +28 -0
- package/src/components/checkbox/HstCheckbox.story.vue +47 -0
- package/src/components/checkbox/HstCheckbox.vue +56 -0
- package/src/components/checkbox/HstCheckboxList.story.vue +49 -0
- package/src/components/checkbox/HstCheckboxList.vue +82 -0
- package/src/components/checkbox/HstSimpleCheckbox.story.vue +28 -0
- package/src/components/checkbox/HstSimpleCheckbox.vue +82 -0
- package/src/components/checkbox/__snapshots__/HstCheckbox.spec.ts.snap +5 -0
- package/src/components/colorselect/HstColorSelect.story.vue +28 -0
- package/src/components/colorselect/HstColorSelect.vue +89 -0
- package/src/components/design-tokens/HstColorShades.story.vue +392 -0
- package/src/components/design-tokens/HstColorShades.vue +101 -0
- package/src/components/design-tokens/HstTokenGrid.story.vue +36 -0
- package/src/components/design-tokens/HstTokenGrid.vue +88 -0
- package/src/components/design-tokens/HstTokenList.story.vue +81 -0
- package/src/components/design-tokens/HstTokenList.vue +62 -0
- package/src/components/json/HstJson.story.vue +41 -0
- package/src/components/json/HstJson.vue +151 -0
- package/src/components/number/HstNumber.story.vue +42 -0
- package/src/components/number/HstNumber.vue +95 -0
- package/src/components/radio/HstRadio.story.vue +51 -0
- package/src/components/radio/HstRadio.vue +109 -0
- package/src/components/select/CustomSelect.vue +119 -0
- package/src/components/select/HstSelect.story.vue +161 -0
- package/src/components/select/HstSelect.vue +40 -0
- package/src/components/slider/HstSlider.story.vue +37 -0
- package/src/components/slider/HstSlider.vue +103 -0
- package/src/components/text/HstText.story.vue +63 -0
- package/src/components/text/HstText.vue +44 -0
- package/src/components/textarea/HstTextarea.story.vue +35 -0
- package/src/components/textarea/HstTextarea.vue +44 -0
- package/src/end.d.ts +8 -0
- package/src/index.ts +54 -0
- package/src/style/main.css +17 -0
- package/src/types.ts +4 -0
- package/src/utils.ts +12 -0
- package/tailwind.config.cjs +13 -0
- package/tsconfig.json +53 -0
- package/vite.config.ts +46 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default {
|
|
3
|
+
name: 'HstColorShades',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import type { CSSProperties } from 'vue'
|
|
9
|
+
import { VTooltip as vTooltip } from 'floating-vue'
|
|
10
|
+
import { computed, ref } from 'vue'
|
|
11
|
+
import HstCopyIcon from '../HstCopyIcon.vue'
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
shades: Record<string, any>
|
|
15
|
+
getName?: (key: string, color: string) => string
|
|
16
|
+
search?: string
|
|
17
|
+
}>()
|
|
18
|
+
|
|
19
|
+
function flattenShades(shades: Record<string, any>, path = ''): Record<string, string> {
|
|
20
|
+
return Object.entries(shades).reduce((acc, [key, color]) => {
|
|
21
|
+
const nextPath = path ? key === 'DEFAULT' ? path : `${path}-${key}` : key
|
|
22
|
+
const obj = typeof color === 'object' ? flattenShades(color, nextPath) : { [nextPath]: color }
|
|
23
|
+
return { ...acc, ...obj }
|
|
24
|
+
}, {})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const shadesWithName = computed(() => {
|
|
28
|
+
const shades = props.shades
|
|
29
|
+
const getName = props.getName
|
|
30
|
+
const flatShades = flattenShades(shades)
|
|
31
|
+
return Object.entries(flatShades).map(([key, color]) => {
|
|
32
|
+
const name = getName ? getName(key, color) : key
|
|
33
|
+
return {
|
|
34
|
+
key,
|
|
35
|
+
color,
|
|
36
|
+
name,
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const displayedShades = computed(() => {
|
|
42
|
+
let list = shadesWithName.value
|
|
43
|
+
if (props.search) {
|
|
44
|
+
const reg = new RegExp(props.search, 'i')
|
|
45
|
+
list = list.filter(({ name }) => reg.test(name))
|
|
46
|
+
}
|
|
47
|
+
return list
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const hover = ref<string>(null)
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<template>
|
|
54
|
+
<div
|
|
55
|
+
v-if="displayedShades.length"
|
|
56
|
+
class="histoire-color-shades htw-grid htw-gap-4 htw-grid-cols-[repeat(auto-fill,minmax(200px,1fr))] htw-m-4"
|
|
57
|
+
>
|
|
58
|
+
<div
|
|
59
|
+
v-for="shade of displayedShades"
|
|
60
|
+
:key="shade.key"
|
|
61
|
+
class="htw-flex htw-flex-col htw-gap-2"
|
|
62
|
+
@mouseenter="hover = shade.key"
|
|
63
|
+
@mouseleave="hover = null"
|
|
64
|
+
>
|
|
65
|
+
<slot
|
|
66
|
+
:color="shade.color"
|
|
67
|
+
>
|
|
68
|
+
<div
|
|
69
|
+
class="htw-rounded-full htw-w-16 htw-h-16"
|
|
70
|
+
:style="{
|
|
71
|
+
backgroundColor: shade.color,
|
|
72
|
+
} as CSSProperties"
|
|
73
|
+
/>
|
|
74
|
+
</slot>
|
|
75
|
+
<div>
|
|
76
|
+
<div class="htw-flex htw-gap-1">
|
|
77
|
+
<pre
|
|
78
|
+
v-tooltip="shade.name.length > 23 ? shade.name : ''"
|
|
79
|
+
class="htw-my-0 htw-truncate htw-shrink"
|
|
80
|
+
>{{ shade.name }}</pre>
|
|
81
|
+
<HstCopyIcon
|
|
82
|
+
v-if="hover === shade.key"
|
|
83
|
+
:content="shade.name"
|
|
84
|
+
class="htw-flex-none"
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="htw-flex htw-gap-1">
|
|
88
|
+
<pre
|
|
89
|
+
v-tooltip="shade.color.length > 23 ? shade.color : ''"
|
|
90
|
+
class="htw-my-0 htw-opacity-50 htw-truncate htw-shrink"
|
|
91
|
+
>{{ shade.color }}</pre>
|
|
92
|
+
<HstCopyIcon
|
|
93
|
+
v-if="hover === shade.key"
|
|
94
|
+
:content="shade.color"
|
|
95
|
+
class="htw-flex-none"
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</template>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import HstTokenGrid from './HstTokenGrid.vue'
|
|
3
|
+
|
|
4
|
+
const tokens = {
|
|
5
|
+
'sm': 'filter: drop-shadow(0 1px 1px rgb(0 0 0 / 0.05))',
|
|
6
|
+
'DEFAULT': 'filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06))',
|
|
7
|
+
'md': 'filter: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06))',
|
|
8
|
+
'lg': 'filter: drop-shadow(0 10px 8px rgb(0 0 0 / 0.04)) drop-shadow(0 4px 3px rgb(0 0 0 / 0.1))',
|
|
9
|
+
'xl': 'filter: drop-shadow(0 20px 13px rgb(0 0 0 / 0.03)) drop-shadow(0 8px 5px rgb(0 0 0 / 0.08))',
|
|
10
|
+
'2xl': 'filter: drop-shadow(0 25px 25px rgb(0 0 0 / 0.15))',
|
|
11
|
+
'none': 'filter: drop-shadow(0 0 #0000)',
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<Story
|
|
17
|
+
title="HstTokenGrid"
|
|
18
|
+
group="design-system"
|
|
19
|
+
responsive-disabled
|
|
20
|
+
>
|
|
21
|
+
<Variant title="default">
|
|
22
|
+
<HstTokenGrid
|
|
23
|
+
:tokens="tokens"
|
|
24
|
+
:get-name="key => key === 'DEFAULT' ? 'drop-shadow' : `drop-shadow-${key}`"
|
|
25
|
+
:col-size="160"
|
|
26
|
+
>
|
|
27
|
+
<template #default="{ token }">
|
|
28
|
+
<div
|
|
29
|
+
class="htw-w-32 htw-h-32 htw-bg-white dark:htw-bg-black htw-rounded htw-mb-2"
|
|
30
|
+
:style="token.value"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
33
|
+
</HstTokenGrid>
|
|
34
|
+
</Variant>
|
|
35
|
+
</Story>
|
|
36
|
+
</template>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default {
|
|
3
|
+
name: 'HstTokenGrid',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { VTooltip as vTooltip } from 'floating-vue'
|
|
9
|
+
import { computed, ref } from 'vue'
|
|
10
|
+
import HstCopyIcon from '../HstCopyIcon.vue'
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(defineProps<{
|
|
13
|
+
tokens: Record<string, string | number | any[] | Record<string, any>>
|
|
14
|
+
colSize?: number
|
|
15
|
+
getName?: (key: string, value: string | number | any[] | Record<string, any>) => string
|
|
16
|
+
}>(), {
|
|
17
|
+
colSize: 180,
|
|
18
|
+
getName: null,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const processedTokens = computed(() => {
|
|
22
|
+
const list = props.tokens
|
|
23
|
+
const getName = props.getName
|
|
24
|
+
return Object.entries(list).map(([key, value]) => {
|
|
25
|
+
const name = getName ? getName(key, value) : key
|
|
26
|
+
return {
|
|
27
|
+
key,
|
|
28
|
+
name,
|
|
29
|
+
value: typeof value === 'number' ? value.toString() : value,
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const colSizePx = computed(() => `${props.colSize}px`)
|
|
35
|
+
|
|
36
|
+
const hover = ref<string>(null)
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<div
|
|
41
|
+
class="histoire-token-grid htw-bind-col-size htw-grid htw-gap-4 htw-m-4"
|
|
42
|
+
:style="{
|
|
43
|
+
'--histoire-col-size': colSizePx,
|
|
44
|
+
}"
|
|
45
|
+
>
|
|
46
|
+
<div
|
|
47
|
+
v-for="token of processedTokens"
|
|
48
|
+
:key="token.key"
|
|
49
|
+
class="htw-flex htw-flex-col htw-gap-2"
|
|
50
|
+
@mouseenter="hover = token.key"
|
|
51
|
+
@mouseleave="hover = null"
|
|
52
|
+
>
|
|
53
|
+
<slot
|
|
54
|
+
:token="token"
|
|
55
|
+
/>
|
|
56
|
+
<div>
|
|
57
|
+
<div class="htw-flex htw-gap-1">
|
|
58
|
+
<pre
|
|
59
|
+
v-tooltip="token.name.length > colSize / 8 ? token.name : ''"
|
|
60
|
+
class="htw-my-0 htw-truncate htw-shrink"
|
|
61
|
+
>{{ token.name }}</pre>
|
|
62
|
+
<HstCopyIcon
|
|
63
|
+
v-if="hover === token.key"
|
|
64
|
+
:content="token.name"
|
|
65
|
+
class="htw-flex-none"
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="htw-flex htw-gap-1">
|
|
69
|
+
<pre
|
|
70
|
+
v-tooltip="token.value.length > colSize / 8 ? token.value : ''"
|
|
71
|
+
class="htw-my-0 htw-opacity-50 htw-truncate htw-shrink"
|
|
72
|
+
>{{ token.value }}</pre>
|
|
73
|
+
<HstCopyIcon
|
|
74
|
+
v-if="hover === token.key"
|
|
75
|
+
:content="typeof token.value === 'string' ? token.value : JSON.stringify(token.value)"
|
|
76
|
+
class="htw-flex-none"
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</template>
|
|
83
|
+
|
|
84
|
+
<style>
|
|
85
|
+
.htw-bind-col-size {
|
|
86
|
+
grid-template-columns: repeat(auto-fill, minmax(var(--histoire-col-size), 1fr));
|
|
87
|
+
}
|
|
88
|
+
</style>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { CSSProperties } from 'vue'
|
|
3
|
+
import HstTokenList from './HstTokenList.vue'
|
|
4
|
+
|
|
5
|
+
const tokens = {
|
|
6
|
+
'0': 0,
|
|
7
|
+
'1': '0.25rem',
|
|
8
|
+
'2': '0.5rem',
|
|
9
|
+
'3': '0.75rem',
|
|
10
|
+
'4': '1rem',
|
|
11
|
+
'5': '1.25rem',
|
|
12
|
+
'6': '1.5rem',
|
|
13
|
+
'8': '2rem',
|
|
14
|
+
'10': '2.5rem',
|
|
15
|
+
'12': '3rem',
|
|
16
|
+
'16': '4rem',
|
|
17
|
+
'20': '5rem',
|
|
18
|
+
'24': '6rem',
|
|
19
|
+
'32': '8rem',
|
|
20
|
+
'40': '10rem',
|
|
21
|
+
'48': '12rem',
|
|
22
|
+
'56': '14rem',
|
|
23
|
+
'64': '16rem',
|
|
24
|
+
'auto': 'auto',
|
|
25
|
+
'px': '1px',
|
|
26
|
+
'screen': '100vw',
|
|
27
|
+
'1/2': '50%',
|
|
28
|
+
'1/3': '33.333333%',
|
|
29
|
+
'2/3': '66.666667%',
|
|
30
|
+
'1/4': '25%',
|
|
31
|
+
'2/4': '50%',
|
|
32
|
+
'3/4': '75%',
|
|
33
|
+
'1/5': '20%',
|
|
34
|
+
'2/5': '40%',
|
|
35
|
+
'3/5': '60%',
|
|
36
|
+
'4/5': '80%',
|
|
37
|
+
'1/6': '16.666667%',
|
|
38
|
+
'2/6': '33.333333%',
|
|
39
|
+
'3/6': '50%',
|
|
40
|
+
'4/6': '66.666667%',
|
|
41
|
+
'5/6': '83.333333%',
|
|
42
|
+
'1/12': '8.333333%',
|
|
43
|
+
'2/12': '16.666667%',
|
|
44
|
+
'3/12': '25%',
|
|
45
|
+
'4/12': '33.333333%',
|
|
46
|
+
'5/12': '41.666667%',
|
|
47
|
+
'6/12': '50%',
|
|
48
|
+
'7/12': '58.333333%',
|
|
49
|
+
'8/12': '66.666667%',
|
|
50
|
+
'9/12': '75%',
|
|
51
|
+
'10/12': '83.333333%',
|
|
52
|
+
'11/12': '91.666667%',
|
|
53
|
+
'full': '100%',
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<Story
|
|
59
|
+
title="HstTokenList"
|
|
60
|
+
group="design-system"
|
|
61
|
+
responsive-disabled
|
|
62
|
+
>
|
|
63
|
+
<Variant title="default">
|
|
64
|
+
<HstTokenList
|
|
65
|
+
:tokens="tokens"
|
|
66
|
+
:get-name="key => `w-${key}`"
|
|
67
|
+
>
|
|
68
|
+
<template #default="{ token }">
|
|
69
|
+
<div class="htw-bg-gray-500/10">
|
|
70
|
+
<div
|
|
71
|
+
class="htw-h-20 htw-bg-gray-500/50"
|
|
72
|
+
:style="{
|
|
73
|
+
width: token.value as string,
|
|
74
|
+
} as CSSProperties"
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
</HstTokenList>
|
|
79
|
+
</Variant>
|
|
80
|
+
</Story>
|
|
81
|
+
</template>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default {
|
|
3
|
+
name: 'HstTokenList',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { computed, ref } from 'vue'
|
|
9
|
+
import HstCopyIcon from '../HstCopyIcon.vue'
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
tokens: Record<string, string | number | any[] | Record<string, any>>
|
|
13
|
+
getName?: (key: string, value: string | number | any[] | Record<string, any>) => string
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
const processedTokens = computed(() => {
|
|
17
|
+
const list = props.tokens
|
|
18
|
+
const getName = props.getName
|
|
19
|
+
return Object.entries(list).map(([key, value]) => {
|
|
20
|
+
const name = getName ? getName(key, value) : key
|
|
21
|
+
return {
|
|
22
|
+
key,
|
|
23
|
+
name,
|
|
24
|
+
value: typeof value === 'number' ? value.toString() : value,
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const hover = ref<string>(null)
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div
|
|
34
|
+
v-for="token of processedTokens"
|
|
35
|
+
:key="token.key"
|
|
36
|
+
class="histoire-token-list htw-flex htw-flex-col htw-gap-2 htw-my-8"
|
|
37
|
+
@mouseenter="hover = token.key"
|
|
38
|
+
@mouseleave="hover = null"
|
|
39
|
+
>
|
|
40
|
+
<slot
|
|
41
|
+
:token="token"
|
|
42
|
+
/>
|
|
43
|
+
<div class="htw-mx-4">
|
|
44
|
+
<div class="htw-flex htw-gap-1">
|
|
45
|
+
<pre class="htw-my-0 htw-truncate htw-shrink">{{ token.name }}</pre>
|
|
46
|
+
<HstCopyIcon
|
|
47
|
+
v-if="hover === token.key"
|
|
48
|
+
:content="token.name"
|
|
49
|
+
class="htw-flex-none"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="htw-flex htw-gap-1">
|
|
53
|
+
<pre class="htw-my-0 htw-opacity-50 htw-truncate htw-shrink">{{ token.value }}</pre>
|
|
54
|
+
<HstCopyIcon
|
|
55
|
+
v-if="hover === token.key"
|
|
56
|
+
:content="typeof token.value === 'string' ? token.value : JSON.stringify(token.value)"
|
|
57
|
+
class="htw-flex-none"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</template>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import HstJson from './HstJson.vue'
|
|
3
|
+
|
|
4
|
+
function initState() {
|
|
5
|
+
return {
|
|
6
|
+
film: {
|
|
7
|
+
year: 2017,
|
|
8
|
+
title: 'Blade Runner 2049',
|
|
9
|
+
actors: ['Ryan Gosling', 'Harrison Ford', 'Ana de Armas', 'Sylvia Hoeks'],
|
|
10
|
+
},
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<Story
|
|
17
|
+
title="HstJson"
|
|
18
|
+
group="controls"
|
|
19
|
+
:layout="{ type: 'single', iframe: false }"
|
|
20
|
+
>
|
|
21
|
+
<Variant
|
|
22
|
+
title="default"
|
|
23
|
+
:init-state="initState"
|
|
24
|
+
>
|
|
25
|
+
<template #default="{ state }">
|
|
26
|
+
<HstJson
|
|
27
|
+
v-model="state.film"
|
|
28
|
+
title="Textarea"
|
|
29
|
+
/>
|
|
30
|
+
<pre>{{ state.film }}</pre>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<template #controls="{ state }">
|
|
34
|
+
<HstJson
|
|
35
|
+
v-model="state.film"
|
|
36
|
+
title="Text"
|
|
37
|
+
/>
|
|
38
|
+
</template>
|
|
39
|
+
</Variant>
|
|
40
|
+
</Story>
|
|
41
|
+
</template>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default {
|
|
3
|
+
name: 'HstJson',
|
|
4
|
+
inheritAttrs: false,
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script lang="ts" setup>
|
|
9
|
+
import type {
|
|
10
|
+
ViewUpdate,
|
|
11
|
+
} from '@codemirror/view'
|
|
12
|
+
import { defaultKeymap } from '@codemirror/commands'
|
|
13
|
+
import { json } from '@codemirror/lang-json'
|
|
14
|
+
import {
|
|
15
|
+
bracketMatching,
|
|
16
|
+
defaultHighlightStyle,
|
|
17
|
+
foldGutter,
|
|
18
|
+
foldKeymap,
|
|
19
|
+
indentOnInput,
|
|
20
|
+
syntaxHighlighting,
|
|
21
|
+
} from '@codemirror/language'
|
|
22
|
+
import { lintKeymap } from '@codemirror/lint'
|
|
23
|
+
import { Compartment } from '@codemirror/state'
|
|
24
|
+
import { oneDarkHighlightStyle, oneDarkTheme } from '@codemirror/theme-one-dark'
|
|
25
|
+
import {
|
|
26
|
+
EditorView,
|
|
27
|
+
highlightActiveLine,
|
|
28
|
+
highlightActiveLineGutter,
|
|
29
|
+
highlightSpecialChars,
|
|
30
|
+
keymap,
|
|
31
|
+
} from '@codemirror/view'
|
|
32
|
+
import { Icon } from '@iconify/vue'
|
|
33
|
+
import { VTooltip as vTooltip } from 'floating-vue'
|
|
34
|
+
import { onMounted, ref, watch, watchEffect } from 'vue'
|
|
35
|
+
import { isDark } from '../../utils'
|
|
36
|
+
import HstWrapper from '../HstWrapper.vue'
|
|
37
|
+
|
|
38
|
+
const props = defineProps<{
|
|
39
|
+
title?: string
|
|
40
|
+
modelValue: unknown
|
|
41
|
+
}>()
|
|
42
|
+
|
|
43
|
+
const emit = defineEmits({
|
|
44
|
+
'update:modelValue': (newValue: unknown) => true,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
let editorView: EditorView
|
|
48
|
+
const internalValue = ref('')
|
|
49
|
+
const invalidValue = ref(false)
|
|
50
|
+
const editorElement = ref<HTMLInputElement>()
|
|
51
|
+
|
|
52
|
+
const themes = {
|
|
53
|
+
light: [EditorView.baseTheme({}), syntaxHighlighting(defaultHighlightStyle)],
|
|
54
|
+
dark: [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)],
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const themeConfig = new Compartment()
|
|
58
|
+
|
|
59
|
+
const extensions = [
|
|
60
|
+
highlightActiveLineGutter(),
|
|
61
|
+
highlightActiveLine(),
|
|
62
|
+
highlightSpecialChars(),
|
|
63
|
+
json(),
|
|
64
|
+
bracketMatching(),
|
|
65
|
+
indentOnInput(),
|
|
66
|
+
foldGutter(),
|
|
67
|
+
keymap.of([
|
|
68
|
+
...defaultKeymap,
|
|
69
|
+
...foldKeymap,
|
|
70
|
+
...lintKeymap,
|
|
71
|
+
]),
|
|
72
|
+
EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
|
|
73
|
+
internalValue.value = viewUpdate.view.state.doc.toString()
|
|
74
|
+
}),
|
|
75
|
+
themeConfig.of(themes.light),
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
onMounted(() => {
|
|
79
|
+
editorView = new EditorView({
|
|
80
|
+
doc: JSON.stringify(props.modelValue, null, 2),
|
|
81
|
+
extensions,
|
|
82
|
+
parent: editorElement.value,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
watchEffect(() => {
|
|
86
|
+
editorView.dispatch({
|
|
87
|
+
effects: [
|
|
88
|
+
themeConfig.reconfigure(themes[isDark.value ? 'dark' : 'light']),
|
|
89
|
+
],
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
watch(() => props.modelValue, () => {
|
|
95
|
+
let sameDocument
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
sameDocument = (JSON.stringify(JSON.parse(internalValue.value)) === JSON.stringify(props.modelValue))
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
sameDocument = false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!sameDocument) {
|
|
105
|
+
editorView.dispatch({ changes: [{ from: 0, to: editorView.state.doc.length, insert: JSON.stringify(props.modelValue, null, 2) }] })
|
|
106
|
+
}
|
|
107
|
+
}, { deep: true })
|
|
108
|
+
|
|
109
|
+
watch(() => internalValue.value, () => {
|
|
110
|
+
invalidValue.value = false
|
|
111
|
+
try {
|
|
112
|
+
emit('update:modelValue', JSON.parse(internalValue.value))
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
invalidValue.value = true
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<template>
|
|
121
|
+
<HstWrapper
|
|
122
|
+
:title="title"
|
|
123
|
+
class="histoire-json htw-cursor-text"
|
|
124
|
+
:class="$attrs.class"
|
|
125
|
+
:style="$attrs.style"
|
|
126
|
+
>
|
|
127
|
+
<div
|
|
128
|
+
ref="editorElement"
|
|
129
|
+
class="__histoire-json-code htw-w-full htw-border htw-border-solid htw-border-black/25 dark:htw-border-white/25 focus-within:htw-border-primary-500 dark:focus-within:htw-border-primary-500 htw-rounded-sm htw-box-border htw-overflow-auto htw-resize-y htw-min-h-32 htw-h-48 htw-relative"
|
|
130
|
+
v-bind="{ ...$attrs, class: null, style: null }"
|
|
131
|
+
/>
|
|
132
|
+
|
|
133
|
+
<template #actions>
|
|
134
|
+
<Icon
|
|
135
|
+
v-if="invalidValue"
|
|
136
|
+
v-tooltip="'JSON error'"
|
|
137
|
+
icon="carbon:warning-alt"
|
|
138
|
+
class="htw-text-orange-500"
|
|
139
|
+
/>
|
|
140
|
+
|
|
141
|
+
<slot name="actions" />
|
|
142
|
+
</template>
|
|
143
|
+
</HstWrapper>
|
|
144
|
+
</template>
|
|
145
|
+
|
|
146
|
+
<style scoped>
|
|
147
|
+
.__histoire-json-code :deep(.cm-editor) {
|
|
148
|
+
height: 100%;
|
|
149
|
+
min-width: 280px;
|
|
150
|
+
}
|
|
151
|
+
</style>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import HstNumber from './HstNumber.vue'
|
|
3
|
+
|
|
4
|
+
function initState() {
|
|
5
|
+
return {
|
|
6
|
+
count: 20,
|
|
7
|
+
step: 1,
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<Story
|
|
14
|
+
title="HstNumber"
|
|
15
|
+
group="controls"
|
|
16
|
+
>
|
|
17
|
+
<Variant
|
|
18
|
+
title="default"
|
|
19
|
+
:init-state="initState"
|
|
20
|
+
>
|
|
21
|
+
<template #default="{ state }">
|
|
22
|
+
<HstNumber
|
|
23
|
+
v-model="state.count"
|
|
24
|
+
:step="state.step"
|
|
25
|
+
title="Count"
|
|
26
|
+
/>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<template #controls="{ state }">
|
|
30
|
+
<HstNumber
|
|
31
|
+
v-model="state.count"
|
|
32
|
+
title="Count"
|
|
33
|
+
/>
|
|
34
|
+
|
|
35
|
+
<HstNumber
|
|
36
|
+
v-model="state.step"
|
|
37
|
+
title="Step"
|
|
38
|
+
/>
|
|
39
|
+
</template>
|
|
40
|
+
</Variant>
|
|
41
|
+
</Story>
|
|
42
|
+
</template>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default {
|
|
3
|
+
name: 'HstNumber',
|
|
4
|
+
inheritAttrs: false,
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script lang="ts" setup>
|
|
9
|
+
import { computed, onUnmounted, ref } from 'vue'
|
|
10
|
+
import HstWrapper from '../HstWrapper.vue'
|
|
11
|
+
|
|
12
|
+
const props = defineProps<{
|
|
13
|
+
title?: string
|
|
14
|
+
modelValue?: number | null
|
|
15
|
+
}>()
|
|
16
|
+
|
|
17
|
+
const emit = defineEmits({
|
|
18
|
+
'update:modelValue': (newValue: number) => true,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const numberModel = computed({
|
|
22
|
+
get: () => props.modelValue,
|
|
23
|
+
set: (value) => {
|
|
24
|
+
emit('update:modelValue', value)
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const input = ref<HTMLInputElement>()
|
|
29
|
+
|
|
30
|
+
function focusAndSelect() {
|
|
31
|
+
input.value.focus()
|
|
32
|
+
input.value.select()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Drag to modify
|
|
36
|
+
|
|
37
|
+
const isDragging = ref(false)
|
|
38
|
+
let startX: number
|
|
39
|
+
let startValue: number
|
|
40
|
+
|
|
41
|
+
function onMouseDown(event: MouseEvent) {
|
|
42
|
+
isDragging.value = true
|
|
43
|
+
startX = event.clientX
|
|
44
|
+
startValue = numberModel.value
|
|
45
|
+
window.addEventListener('mousemove', onMouseMove)
|
|
46
|
+
window.addEventListener('mouseup', stopDragging)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function onMouseMove(event: MouseEvent) {
|
|
50
|
+
let step = Number.parseFloat(input.value.step)
|
|
51
|
+
if (Number.isNaN(step)) {
|
|
52
|
+
step = 1
|
|
53
|
+
}
|
|
54
|
+
numberModel.value = startValue + Math.round((event.clientX - startX) / 10 / step) * step
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function stopDragging() {
|
|
58
|
+
isDragging.value = false
|
|
59
|
+
window.removeEventListener('mousemove', onMouseMove)
|
|
60
|
+
window.removeEventListener('mouseup', stopDragging)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
onUnmounted(() => {
|
|
64
|
+
stopDragging()
|
|
65
|
+
})
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<HstWrapper
|
|
70
|
+
class="histoire-number htw-cursor-ew-resize htw-items-center"
|
|
71
|
+
:title="title"
|
|
72
|
+
:class="[
|
|
73
|
+
$attrs.class,
|
|
74
|
+
{ 'htw-select-none': isDragging },
|
|
75
|
+
]"
|
|
76
|
+
:style="$attrs.style"
|
|
77
|
+
@click="focusAndSelect"
|
|
78
|
+
@mousedown="onMouseDown"
|
|
79
|
+
>
|
|
80
|
+
<input
|
|
81
|
+
ref="input"
|
|
82
|
+
v-bind="{ ...$attrs, class: null, style: null }"
|
|
83
|
+
v-model.number="numberModel"
|
|
84
|
+
type="number"
|
|
85
|
+
:class="{
|
|
86
|
+
'htw-select-none': isDragging,
|
|
87
|
+
}"
|
|
88
|
+
class="htw-text-inherit htw-bg-transparent htw-w-full htw-outline-none htw-pl-2 htw-py-1 -htw-my-1 htw-border htw-border-solid htw-border-black/25 dark:htw-border-white/25 focus:htw-border-primary-500 dark:focus:htw-border-primary-500 htw-rounded-sm htw-cursor-ew-resize htw-box-border"
|
|
89
|
+
>
|
|
90
|
+
|
|
91
|
+
<template #actions>
|
|
92
|
+
<slot name="actions" />
|
|
93
|
+
</template>
|
|
94
|
+
</HstWrapper>
|
|
95
|
+
</template>
|