Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Guillaume Chau <guillaume.b.chau@gmail.com> Related to #30
- Loading branch information
Showing
15 changed files
with
374 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,21 @@ | ||
import { watch } from 'vue' | ||
import { useDark, useToggle } from '@vueuse/core' | ||
|
||
export const isDark = useDark({ valueDark: 'htw-dark' }) | ||
export const toggleDark = useToggle(isDark) | ||
|
||
function applyDarkToControls () { | ||
window.__hst_controls_dark?.forEach(ref => { | ||
ref.value = isDark.value | ||
}) | ||
} | ||
|
||
watch(isDark, () => { | ||
applyDarkToControls() | ||
}, { | ||
immediate: true, | ||
}) | ||
|
||
window.__hst_controls_dark_ready = () => { | ||
applyDarkToControls() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,16 @@ | ||
/// <reference types="vite/client" /> | ||
|
||
import type { Ref } from '@histoire/vendors/vue' | ||
|
||
declare module '*.vue' { | ||
import { ComponentOptions } from 'vue' | ||
const comp: ComponentOptions | ||
export default comp | ||
} | ||
|
||
global { | ||
interface Window { | ||
__hst_controls_dark: Ref<boolean>[] | ||
__hst_controls_dark_ready: () => void | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
packages/histoire-controls/src/components/json/HstJson.story.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<script lang="ts" setup> | ||
import HstJson from './HstJson.vue' | ||
function initState () { | ||
return { | ||
film: { | ||
year: 2017, | ||
title: 'Blade Runner 2049', | ||
actors: ['Ryan Gosling', 'Harrison Ford', 'Ana de Armas', 'Sylvia Hoeks'], | ||
}, | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<Story | ||
title="HstJson" | ||
group="controls" | ||
:layout="{ type: 'single', iframe: false }" | ||
> | ||
<Variant | ||
title="default" | ||
:init-state="initState" | ||
> | ||
<template #default="{ state }"> | ||
<HstJson | ||
v-model="state.film" | ||
title="Textarea" | ||
/> | ||
<pre>{{ state.film }}</pre> | ||
</template> | ||
|
||
<template #controls="{ state }"> | ||
<HstJson | ||
v-model="state.film" | ||
title="Text" | ||
/> | ||
</template> | ||
</Variant> | ||
</Story> | ||
</template> |
148 changes: 148 additions & 0 deletions
148
packages/histoire-controls/src/components/json/HstJson.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
<script lang="ts"> | ||
export default { | ||
name: 'HstJson', | ||
inheritAttrs: false, | ||
} | ||
</script> | ||
|
||
<script lang="ts" setup> | ||
import { ref, onMounted, watch, watchEffect } from 'vue' | ||
import { Icon } from '@iconify/vue' | ||
import HstWrapper from '../HstWrapper.vue' | ||
import { VTooltip as vTooltip } from 'floating-vue' | ||
import { Compartment } from '@codemirror/state' | ||
import { | ||
EditorView, | ||
keymap, | ||
highlightActiveLineGutter, | ||
highlightActiveLine, | ||
highlightSpecialChars, | ||
ViewUpdate, | ||
} from '@codemirror/view' | ||
import { defaultKeymap } from '@codemirror/commands' | ||
import { json } from '@codemirror/lang-json' | ||
import { | ||
defaultHighlightStyle, | ||
syntaxHighlighting, | ||
indentOnInput, | ||
bracketMatching, | ||
foldGutter, | ||
foldKeymap, | ||
} from '@codemirror/language' | ||
import { lintKeymap } from '@codemirror/lint' | ||
import { oneDarkTheme, oneDarkHighlightStyle } from '@codemirror/theme-one-dark' | ||
import { isDark } from '../../utils' | ||
const props = defineProps<{ | ||
title?: string | ||
modelValue: unknown | ||
}>() | ||
const emits = defineEmits({ | ||
'update:modelValue': (newValue: unknown) => true, | ||
}) | ||
let editorView: EditorView | ||
const internalValue = ref('') | ||
const invalidValue = ref(false) | ||
const editorElement = ref<HTMLInputElement>() | ||
const themes = { | ||
light: [EditorView.baseTheme({}), syntaxHighlighting(defaultHighlightStyle)], | ||
dark: [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)], | ||
} | ||
const themeConfig = new Compartment() | ||
const extensions = [ | ||
highlightActiveLineGutter(), | ||
highlightActiveLine(), | ||
highlightSpecialChars(), | ||
json(), | ||
bracketMatching(), | ||
indentOnInput(), | ||
foldGutter(), | ||
keymap.of([ | ||
...defaultKeymap, | ||
...foldKeymap, | ||
...lintKeymap, | ||
]), | ||
EditorView.updateListener.of((viewUpdate: ViewUpdate) => { | ||
internalValue.value = viewUpdate.view.state.doc.toString() | ||
}), | ||
themeConfig.of(themes.light), | ||
] | ||
onMounted(() => { | ||
editorView = new EditorView({ | ||
doc: JSON.stringify(props.modelValue, null, 2), | ||
extensions, | ||
parent: editorElement.value, | ||
}) | ||
watchEffect(() => { | ||
editorView.dispatch({ | ||
effects: [ | ||
themeConfig.reconfigure(themes[isDark.value ? 'dark' : 'light']), | ||
], | ||
}) | ||
}) | ||
}) | ||
watch(() => props.modelValue, () => { | ||
let sameDocument | ||
try { | ||
sameDocument = (JSON.stringify(JSON.parse(internalValue.value)) === JSON.stringify(props.modelValue)) | ||
} catch (e) { | ||
sameDocument = false | ||
} | ||
if (!sameDocument) { | ||
editorView.dispatch({ changes: [{ from: 0, to: editorView.state.doc.length, insert: JSON.stringify(props.modelValue, null, 2) }] }) | ||
} | ||
}, { deep: true }) | ||
watch(() => internalValue.value, () => { | ||
invalidValue.value = false | ||
try { | ||
emits('update:modelValue', JSON.parse(internalValue.value)) | ||
} catch (e) { | ||
invalidValue.value = true | ||
} | ||
}) | ||
</script> | ||
|
||
<template> | ||
<HstWrapper | ||
:title="title" | ||
class="htw-cursor-text" | ||
:class="$attrs.class" | ||
:style="$attrs.style" | ||
> | ||
<div | ||
ref="editorElement" | ||
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" | ||
v-bind="{ ...$attrs, class: null, style: null }" | ||
/> | ||
|
||
<template #actions> | ||
<Icon | ||
v-if="invalidValue" | ||
v-tooltip="'JSON error'" | ||
icon="carbon:warning-alt" | ||
class="htw-text-orange-500" | ||
/> | ||
|
||
<slot name="actions" /> | ||
</template> | ||
</HstWrapper> | ||
</template> | ||
|
||
<style scoped> | ||
.__histoire-json-code :deep(.cm-editor) { | ||
height: 100%; | ||
min-width: 280px; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import type { Ref } from '@histoire/vendors/vue' | ||
|
||
global { | ||
interface Window { | ||
__hst_controls_dark: Ref<boolean>[] | ||
__hst_controls_dark_ready: () => void | ||
} | ||
} |
Oops, something went wrong.