@poveste/controls 0.6.0 → 0.7.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 (49) hide show
  1. package/package.json +16 -3
  2. package/postcss.config.cjs +0 -7
  3. package/src/components/HstCopyIcon.story.vue +0 -26
  4. package/src/components/HstCopyIcon.vue +0 -38
  5. package/src/components/HstWrapper.vue +0 -41
  6. package/src/components/button/HstButton.story.vue +0 -30
  7. package/src/components/button/HstButton.vue +0 -26
  8. package/src/components/button/HstButtonGroup.story.vue +0 -79
  9. package/src/components/button/HstButtonGroup.vue +0 -71
  10. package/src/components/checkbox/HstCheckbox.spec.ts +0 -28
  11. package/src/components/checkbox/HstCheckbox.story.vue +0 -47
  12. package/src/components/checkbox/HstCheckbox.vue +0 -56
  13. package/src/components/checkbox/HstCheckboxList.story.vue +0 -49
  14. package/src/components/checkbox/HstCheckboxList.vue +0 -82
  15. package/src/components/checkbox/HstSimpleCheckbox.story.vue +0 -28
  16. package/src/components/checkbox/HstSimpleCheckbox.vue +0 -82
  17. package/src/components/checkbox/__snapshots__/HstCheckbox.spec.ts.snap +0 -5
  18. package/src/components/colorselect/HstColorSelect.story.vue +0 -28
  19. package/src/components/colorselect/HstColorSelect.vue +0 -89
  20. package/src/components/design-tokens/HstColorShades.story.vue +0 -392
  21. package/src/components/design-tokens/HstColorShades.vue +0 -101
  22. package/src/components/design-tokens/HstTokenGrid.story.vue +0 -36
  23. package/src/components/design-tokens/HstTokenGrid.vue +0 -88
  24. package/src/components/design-tokens/HstTokenList.story.vue +0 -81
  25. package/src/components/design-tokens/HstTokenList.vue +0 -62
  26. package/src/components/json/HstJson.story.vue +0 -41
  27. package/src/components/json/HstJson.vue +0 -167
  28. package/src/components/json/serialize.spec.ts +0 -85
  29. package/src/components/json/serialize.ts +0 -54
  30. package/src/components/number/HstNumber.story.vue +0 -42
  31. package/src/components/number/HstNumber.vue +0 -95
  32. package/src/components/radio/HstRadio.story.vue +0 -51
  33. package/src/components/radio/HstRadio.vue +0 -109
  34. package/src/components/select/CustomSelect.vue +0 -126
  35. package/src/components/select/HstSelect.story.vue +0 -161
  36. package/src/components/select/HstSelect.vue +0 -40
  37. package/src/components/slider/HstSlider.story.vue +0 -37
  38. package/src/components/slider/HstSlider.vue +0 -106
  39. package/src/components/text/HstText.story.vue +0 -63
  40. package/src/components/text/HstText.vue +0 -44
  41. package/src/components/textarea/HstTextarea.story.vue +0 -35
  42. package/src/components/textarea/HstTextarea.vue +0 -44
  43. package/src/end.d.ts +0 -8
  44. package/src/index.ts +0 -54
  45. package/src/style/main.css +0 -65
  46. package/src/types.ts +0 -4
  47. package/src/utils.ts +0 -12
  48. package/tsconfig.json +0 -53
  49. package/vite.config.ts +0 -46
@@ -1,36 +0,0 @@
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="w-32 h-32 bg-white dark:bg-black rounded mb-2"
30
- :style="token.value"
31
- />
32
- </template>
33
- </HstTokenGrid>
34
- </Variant>
35
- </Story>
36
- </template>
@@ -1,88 +0,0 @@
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="poveste-token-grid bind-col-size grid gap-4 m-4"
42
- :style="{
43
- '--poveste-col-size': colSizePx,
44
- }"
45
- >
46
- <div
47
- v-for="token of processedTokens"
48
- :key="token.key"
49
- class="flex flex-col gap-2"
50
- @mouseenter="hover = token.key"
51
- @mouseleave="hover = null"
52
- >
53
- <slot
54
- :token="token"
55
- />
56
- <div>
57
- <div class="flex gap-1">
58
- <pre
59
- v-tooltip="token.name.length > colSize / 8 ? token.name : ''"
60
- class="my-0 truncate shrink"
61
- >{{ token.name }}</pre>
62
- <HstCopyIcon
63
- v-if="hover === token.key"
64
- :content="token.name"
65
- class="flex-none"
66
- />
67
- </div>
68
- <div class="flex gap-1">
69
- <pre
70
- v-tooltip="token.value.length > colSize / 8 ? token.value : ''"
71
- class="my-0 opacity-50 truncate 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="flex-none"
77
- />
78
- </div>
79
- </div>
80
- </div>
81
- </div>
82
- </template>
83
-
84
- <style>
85
- .bind-col-size {
86
- grid-template-columns: repeat(auto-fill, minmax(var(--poveste-col-size), 1fr));
87
- }
88
- </style>
@@ -1,81 +0,0 @@
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="bg-gray-500/10">
70
- <div
71
- class="h-20 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>
@@ -1,62 +0,0 @@
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="poveste-token-list flex flex-col gap-2 my-8"
37
- @mouseenter="hover = token.key"
38
- @mouseleave="hover = null"
39
- >
40
- <slot
41
- :token="token"
42
- />
43
- <div class="mx-4">
44
- <div class="flex gap-1">
45
- <pre class="my-0 truncate shrink">{{ token.name }}</pre>
46
- <HstCopyIcon
47
- v-if="hover === token.key"
48
- :content="token.name"
49
- class="flex-none"
50
- />
51
- </div>
52
- <div class="flex gap-1">
53
- <pre class="my-0 opacity-50 truncate 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="flex-none"
58
- />
59
- </div>
60
- </div>
61
- </div>
62
- </template>
@@ -1,41 +0,0 @@
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>
@@ -1,167 +0,0 @@
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
- import { stringifyState } from './serialize.js'
38
-
39
- const props = defineProps<{
40
- title?: string
41
- modelValue: unknown
42
- }>()
43
-
44
- const emit = defineEmits({
45
- 'update:modelValue': (newValue: unknown) => true,
46
- })
47
-
48
- let editorView: EditorView
49
- // The document this control last rendered from `modelValue`. A change producing
50
- // exactly this is our own render, not an edit, and must not be written back:
51
- // `stringifyState` substitutes markers for what JSON cannot carry, so echoing
52
- // our own document replaces a cycle in the story's state with the string
53
- // `[Circular]`, and a function with `[Function]`.
54
- let renderedDoc: string | undefined
55
- const internalValue = ref('')
56
- const invalidValue = ref(false)
57
- const editorElement = ref<HTMLInputElement>()
58
-
59
- const themes = {
60
- light: [EditorView.baseTheme({}), syntaxHighlighting(defaultHighlightStyle)],
61
- dark: [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)],
62
- }
63
-
64
- const themeConfig = new Compartment()
65
-
66
- const extensions = [
67
- highlightActiveLineGutter(),
68
- highlightActiveLine(),
69
- highlightSpecialChars(),
70
- json(),
71
- bracketMatching(),
72
- indentOnInput(),
73
- foldGutter(),
74
- keymap.of([
75
- ...defaultKeymap,
76
- ...foldKeymap,
77
- ...lintKeymap,
78
- ]),
79
- EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
80
- internalValue.value = viewUpdate.view.state.doc.toString()
81
- }),
82
- themeConfig.of(themes.light),
83
- ]
84
-
85
- onMounted(() => {
86
- renderedDoc = stringifyState(props.modelValue, 2)
87
-
88
- editorView = new EditorView({
89
- doc: renderedDoc,
90
- extensions,
91
- parent: editorElement.value,
92
- })
93
-
94
- watchEffect(() => {
95
- editorView.dispatch({
96
- effects: [
97
- themeConfig.reconfigure(themes[isDark.value ? 'dark' : 'light']),
98
- ],
99
- })
100
- })
101
- })
102
-
103
- watch(() => props.modelValue, () => {
104
- let sameDocument
105
-
106
- try {
107
- sameDocument = (stringifyState(JSON.parse(internalValue.value)) === stringifyState(props.modelValue))
108
- }
109
- catch (e) {
110
- sameDocument = false
111
- }
112
-
113
- if (!sameDocument) {
114
- renderedDoc = stringifyState(props.modelValue, 2)
115
- editorView.dispatch({ changes: [{ from: 0, to: editorView.state.doc.length, insert: renderedDoc }] })
116
- }
117
- }, { deep: true })
118
-
119
- watch(() => internalValue.value, () => {
120
- // Our own render coming back round. The model already holds this, and parsing
121
- // it would hand back the markers instead of the values they stand for.
122
- if (internalValue.value === renderedDoc) {
123
- return
124
- }
125
-
126
- invalidValue.value = false
127
- try {
128
- emit('update:modelValue', JSON.parse(internalValue.value))
129
- }
130
- catch (e) {
131
- invalidValue.value = true
132
- }
133
- })
134
- </script>
135
-
136
- <template>
137
- <HstWrapper
138
- :title="title"
139
- class="poveste-json cursor-text"
140
- :class="$attrs.class"
141
- :style="$attrs.style"
142
- >
143
- <div
144
- ref="editorElement"
145
- class="__poveste-json-code w-full border border-solid border-black/25 dark:border-white/25 focus-within:border-primary-500 dark:focus-within:border-primary-500 rounded-sm box-border overflow-auto resize-y min-h-32 h-48 relative"
146
- v-bind="{ ...$attrs, class: null, style: null }"
147
- />
148
-
149
- <template #actions>
150
- <Icon
151
- v-if="invalidValue"
152
- v-tooltip="'JSON error'"
153
- icon="carbon:warning-alt"
154
- class="text-orange-500"
155
- />
156
-
157
- <slot name="actions" />
158
- </template>
159
- </HstWrapper>
160
- </template>
161
-
162
- <style scoped>
163
- .__poveste-json-code :deep(.cm-editor) {
164
- height: 100%;
165
- min-width: 280px;
166
- }
167
- </style>
@@ -1,85 +0,0 @@
1
- import { EditorView } from '@codemirror/view'
2
- import { mount } from '@vue/test-utils'
3
- import { describe, expect, it } from 'vitest'
4
- import { nextTick } from 'vue'
5
- import HstJson from './HstJson.vue'
6
- import { stringifyState } from './serialize.js'
7
-
8
- describe('stringifyState', () => {
9
- it('matches JSON.stringify on values JSON can express', () => {
10
- const value = { a: 1, b: 'two', c: [1, 2], d: { e: null }, f: true }
11
- expect(stringifyState(value)).toBe(JSON.stringify(value))
12
- expect(stringifyState(value, 2)).toBe(JSON.stringify(value, null, 2))
13
- })
14
-
15
- it('names a cycle instead of throwing', () => {
16
- // The ComplexParameter story in the vue3 example holds exactly this, and
17
- // once the state sync started delivering the story's own scope it reached
18
- // the editor. `JSON.stringify` throws here, from a lifecycle hook, taking
19
- // the rest of the flush — the panel and the toolbar — down with it.
20
- const parent: any = { name: 'hello' }
21
- const child: any = { parent }
22
- parent.child = child
23
-
24
- expect(JSON.parse(stringifyState(parent))).toEqual({
25
- name: 'hello',
26
- child: { parent: '[Circular]' },
27
- })
28
- })
29
-
30
- it('names a function, which JSON.stringify drops silently', () => {
31
- expect(JSON.parse(stringifyState({ action: () => {} }))).toEqual({ action: '[Function]' })
32
- })
33
-
34
- it('does not mistake a shared reference for a cycle', () => {
35
- // The guard tracks ancestors, not everything seen. The same object twice as
36
- // a sibling serialises fine and must not be blanked out.
37
- const shared = { a: 1 }
38
- expect(JSON.parse(stringifyState({ x: shared, y: shared }))).toEqual({ x: { a: 1 }, y: { a: 1 } })
39
- })
40
-
41
- it('names a node rather than walking it', () => {
42
- const el = document.createElement('div')
43
- expect(JSON.parse(stringifyState({ el }))).toEqual({ el: '[Node]' })
44
- })
45
-
46
- it('carries a bigint across as its digits', () => {
47
- expect(JSON.parse(stringifyState({ n: 10n }))).toEqual({ n: '10' })
48
- })
49
- })
50
-
51
- describe('hstJson round-trip', () => {
52
- it('does not write its own markers back into the model', async () => {
53
- // The markers stand for what JSON cannot carry. Parsing the rendered
54
- // document and emitting it replaces the thing itself with its label — a
55
- // cycle in the story's state becomes the string `[Circular]` — and it used
56
- // to happen on mount, with nobody touching the editor.
57
- const parent: any = { name: 'hello' }
58
- parent.child = { parent }
59
-
60
- const wrapper = mount(HstJson, { props: { modelValue: parent, title: 'Recursive' } })
61
- await nextTick()
62
-
63
- await wrapper.setProps({ modelValue: { ...parent, name: 'changed' } })
64
- await nextTick()
65
- await new Promise(resolve => setTimeout(resolve, 50))
66
-
67
- expect(wrapper.emitted('update:modelValue')).toBeUndefined()
68
- })
69
-
70
- it('still reports a document the reader changed', async () => {
71
- // The guard is keyed on the exact text this control last rendered, so it
72
- // must not swallow anything else. Without this, suppressing the echo would
73
- // quietly turn the editor read-only and the test above would still pass.
74
- const wrapper = mount(HstJson, { props: { modelValue: { a: 1 }, title: 'Plain' } })
75
- await nextTick()
76
-
77
- const view = EditorView.findFromDOM(wrapper.find('.cm-content').element as HTMLElement)!
78
- expect(view).toBeTruthy()
79
-
80
- view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: '{ "a": 2 }' } })
81
- await nextTick()
82
-
83
- expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([{ a: 2 }])
84
- })
85
- })
@@ -1,54 +0,0 @@
1
- /**
2
- * `JSON.stringify` for values that were never promised to be JSON.
3
- *
4
- * State reaches this control straight out of a story's own scope, so it holds
5
- * whatever a `<script setup>` holds: a function, a DOM node, an object that
6
- * refers back to itself. `JSON.stringify` throws on the cycle, and it throws
7
- * from inside a lifecycle hook — which takes the rest of that flush with it, so
8
- * the panel and the toolbar beside it never mount. A marker in the editor is
9
- * the smaller loss, and it is what the story events pane already does.
10
- *
11
- * Nodes and windows are named rather than walked. Both are cyclic, so the guard
12
- * below would terminate on them, but only after walking a few thousand
13
- * properties of `window` on every keystroke.
14
- */
15
- export function stringifyState(value: unknown, space?: number) {
16
- // Ancestors, not everything seen: the same object appearing twice as a
17
- // sibling is a shared reference, not a cycle, and is perfectly serialisable.
18
- // The replacer's `this` is the holder, which is what lets the stack unwind.
19
- const ancestors: any[] = []
20
-
21
- return JSON.stringify(value, function (this: any, _key, current) {
22
- if (typeof current === 'function') {
23
- return '[Function]'
24
- }
25
-
26
- if (typeof current === 'bigint') {
27
- return current.toString()
28
- }
29
-
30
- if (current === null || typeof current !== 'object') {
31
- return current
32
- }
33
-
34
- if (typeof Node !== 'undefined' && current instanceof Node) {
35
- return '[Node]'
36
- }
37
-
38
- if (typeof Window !== 'undefined' && current instanceof Window) {
39
- return '[Window]'
40
- }
41
-
42
- while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) {
43
- ancestors.pop()
44
- }
45
-
46
- if (ancestors.includes(current)) {
47
- return '[Circular]'
48
- }
49
-
50
- ancestors.push(current)
51
-
52
- return current
53
- }, space)
54
- }
@@ -1,42 +0,0 @@
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>