@dosgato/dialog 1.2.8 → 1.3.1
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/dist/Button.svelte +15 -13
- package/dist/ButtonGroup.svelte +44 -40
- package/dist/Checkbox.svelte +13 -12
- package/dist/Container.svelte +50 -46
- package/dist/Dialog.svelte +56 -41
- package/dist/FieldAutocomplete.svelte +77 -70
- package/dist/FieldCheckbox.svelte +25 -22
- package/dist/FieldChoices.svelte +74 -68
- package/dist/FieldChooserLink.svelte +148 -150
- package/dist/FieldDate.svelte +19 -18
- package/dist/FieldDateTime.svelte +36 -34
- package/dist/FieldDualListbox.svelte +80 -79
- package/dist/FieldHidden.svelte +16 -15
- package/dist/FieldIdentifier.svelte +18 -17
- package/dist/FieldMultiple.svelte +71 -74
- package/dist/FieldMultiselect.svelte +81 -72
- package/dist/FieldNumber.svelte +20 -19
- package/dist/FieldRadio.svelte +42 -41
- package/dist/FieldSelect.svelte +45 -45
- package/dist/FieldStandard.svelte +28 -27
- package/dist/FieldText.svelte +27 -24
- package/dist/FieldTextArea.svelte +24 -24
- package/dist/FileIcon.svelte +10 -8
- package/dist/Form.svelte +40 -18
- package/dist/Form.svelte.d.ts +15 -13
- package/dist/FormDialog.svelte +40 -25
- package/dist/FormDialog.svelte.d.ts +23 -17
- package/dist/Icon.svelte +38 -33
- package/dist/InlineMessage.svelte +31 -29
- package/dist/InlineMessages.svelte +10 -7
- package/dist/Input.svelte +40 -39
- package/dist/Listbox.svelte +102 -109
- package/dist/MaxLength.svelte +19 -18
- package/dist/Radio.svelte +18 -15
- package/dist/Switcher.svelte +37 -33
- package/dist/Tab.svelte +23 -21
- package/dist/Tabs.svelte +111 -110
- package/dist/Tooltip.svelte +7 -7
- package/dist/chooser/Chooser.svelte +83 -76
- package/dist/chooser/ChooserPreview.svelte +16 -14
- package/dist/chooser/Details.svelte +6 -4
- package/dist/chooser/Thumbnail.svelte +20 -16
- package/dist/chooser/UploadUI.svelte +78 -69
- package/dist/code/CodeEditor.svelte +63 -66
- package/dist/code/FieldCodeEditor.svelte +21 -19
- package/dist/colorpicker/FieldColorPicker.svelte +36 -35
- package/dist/cropper/FieldCropper.svelte +142 -141
- package/dist/iconpicker/FieldIconPicker.svelte +102 -94
- package/dist/imageposition/FieldImagePosition.svelte +107 -98
- package/dist/tagpicker/FieldTagPicker.svelte +63 -54
- package/dist/tree/LoadIcon.svelte +0 -1
- package/dist/tree/Tree.svelte +198 -192
- package/dist/tree/Tree.svelte.d.ts +5 -5
- package/dist/tree/TreeCell.svelte +10 -6
- package/dist/tree/TreeCell.svelte.d.ts +5 -5
- package/dist/tree/TreeNode.svelte +213 -241
- package/dist/tree/TreeNode.svelte.d.ts +5 -5
- package/package.json +8 -9
|
@@ -1,82 +1,90 @@
|
|
|
1
|
-
<script
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export let
|
|
15
|
-
export let
|
|
16
|
-
export let
|
|
17
|
-
export let
|
|
18
|
-
export let
|
|
19
|
-
export let
|
|
20
|
-
export let
|
|
21
|
-
export let
|
|
22
|
-
export let
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
$:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
1
|
+
<script lang="ts" generics="F">
|
|
2
|
+
import browserIcon from '@iconify-icons/ph/browser'
|
|
3
|
+
import folderIcon from '@iconify-icons/ph/folder'
|
|
4
|
+
import folderNotchOpen from '@iconify-icons/ph/folder-notch-open'
|
|
5
|
+
import { createEventDispatcher, getContext, onMount, setContext, tick } from 'svelte'
|
|
6
|
+
import { isNotBlank, randomid } from 'txstate-utils'
|
|
7
|
+
import { Button, Dialog, iconForMime, Tabs, Tree, type TypedTreeItem, type TabStore, UploadUI, expandTreePath } from '..'
|
|
8
|
+
import { CHOOSER_API_CONTEXT, type AnyItem, type Client } from './ChooserAPI'
|
|
9
|
+
import { CHOOSER_STORE_CONTEXT, ChooserStore } from './ChooserStore'
|
|
10
|
+
import ChooserPreview from './ChooserPreview.svelte'
|
|
11
|
+
|
|
12
|
+
const chooserClient = getContext<Client>(CHOOSER_API_CONTEXT)
|
|
13
|
+
|
|
14
|
+
export let label: string | undefined = undefined
|
|
15
|
+
export let images = false
|
|
16
|
+
export let pages = false
|
|
17
|
+
export let assets = images
|
|
18
|
+
export let folders = false
|
|
19
|
+
export let required = false
|
|
20
|
+
export let initialSource: string | undefined = undefined
|
|
21
|
+
export let initialPath: string | undefined = undefined
|
|
22
|
+
export let activeSources: string[] | undefined = undefined
|
|
23
|
+
export let passthruFilters: F | undefined = undefined
|
|
24
|
+
export let filter: undefined | ((item: AnyItem) => boolean | Promise<boolean>) = undefined
|
|
25
|
+
export let store = new ChooserStore<F>(chooserClient)
|
|
26
|
+
store.filter = filter
|
|
27
|
+
|
|
28
|
+
setContext(CHOOSER_STORE_CONTEXT, store)
|
|
29
|
+
const dispatch = createEventDispatcher()
|
|
30
|
+
|
|
31
|
+
let showuploader = false
|
|
32
|
+
let tabStore: TabStore
|
|
33
|
+
const { sources, source, preview, treeStore, selected } = store
|
|
34
|
+
$: currentName = tabStore?.currentName()
|
|
35
|
+
|
|
36
|
+
$: if ($currentName) {
|
|
37
|
+
store.setSource($currentName)
|
|
38
|
+
treeStore.refresh().catch(console.error)
|
|
39
|
+
}
|
|
40
|
+
$: store.setPreview($selected)
|
|
41
|
+
|
|
42
|
+
function onChoose () {
|
|
43
|
+
dispatch('change', $store.preview)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function onDeselect () {
|
|
47
|
+
store.setPreview(undefined)
|
|
48
|
+
thumbnailExpanded = false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function onUploadComplete () {
|
|
52
|
+
treeStore.openAndRefresh($selected!).catch(console.error)
|
|
53
|
+
showuploader = false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function selectPreview (preloadPath) {
|
|
57
|
+
if (!$store.initialized) return
|
|
49
58
|
if (preloadPath) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (focusedElement?.classList.contains('tree-node'))
|
|
58
|
-
focusedElement.scrollIntoView({ block: 'center' });
|
|
59
|
+
const currentSelection = await expandTreePath(treeStore, preloadPath.split('/').filter(isNotBlank))
|
|
60
|
+
if (!currentSelection) return
|
|
61
|
+
store.setPreview(currentSelection)
|
|
62
|
+
treeStore.select(currentSelection, { clear: true })
|
|
63
|
+
await tick()
|
|
64
|
+
const focusedElement = document.querySelector('.tree-node[tabindex="0"]')
|
|
65
|
+
if (focusedElement?.classList.contains('tree-node')) focusedElement.scrollIntoView({ block: 'center' })
|
|
59
66
|
}
|
|
60
|
-
}
|
|
61
|
-
onMount(async () => {
|
|
62
|
-
const preloadSource = $preview?.source ?? $selected?.source ?? $source?.name ?? initialSource
|
|
63
|
-
const preloadPath = $preview?.path ?? $selected?.path ?? initialPath
|
|
64
|
-
await store.init({ images, pages, assets, folders, activeSources, initialSource: preloadSource, initialPath: preloadPath, passthruFilters, filter })
|
|
65
|
-
await treeStore.refresh()
|
|
66
|
-
await selectPreview(preloadPath)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
}
|
|
68
|
+
onMount(async () => {
|
|
69
|
+
const preloadSource = $preview?.source ?? $selected?.source ?? $source?.name ?? initialSource
|
|
70
|
+
const preloadPath = $preview?.path ?? $selected?.path ?? initialPath
|
|
71
|
+
await store.init({ images, pages, assets, folders, activeSources, initialSource: preloadSource, initialPath: preloadPath, passthruFilters, filter })
|
|
72
|
+
await treeStore.refresh()
|
|
73
|
+
await selectPreview(preloadPath)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const previewId = randomid()
|
|
77
|
+
let thumbnailExpanded = false
|
|
78
|
+
|
|
79
|
+
function iconForItem (item: TypedTreeItem<AnyItem>) {
|
|
71
80
|
if (item.icon) {
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
};
|
|
81
|
+
return item.icon
|
|
82
|
+
} else {
|
|
83
|
+
return {
|
|
84
|
+
icon: item.type === 'asset' ? iconForMime(item.mime) : (item.type === 'folder' ? (item.open ? folderNotchOpen : folderIcon) : browserIcon)
|
|
85
|
+
}
|
|
78
86
|
}
|
|
79
|
-
}
|
|
87
|
+
}
|
|
80
88
|
</script>
|
|
81
89
|
|
|
82
90
|
<Dialog size="xl" ignoreTabs title={label} on:escape continueText="Choose" disabled={!$preview && required} cancelText="Cancel">
|
|
@@ -150,5 +158,4 @@ function iconForItem(item) {
|
|
|
150
158
|
height: 70%;
|
|
151
159
|
}
|
|
152
160
|
}
|
|
153
|
-
|
|
154
161
|
</style>
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export let
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ChooserStore } from './ChooserStore'
|
|
3
|
+
import Thumbnail from './Thumbnail.svelte'
|
|
4
|
+
import Details from './Details.svelte'
|
|
5
|
+
import { createEventDispatcher } from 'svelte'
|
|
6
|
+
|
|
7
|
+
export let previewId: string
|
|
8
|
+
export let store: ChooserStore
|
|
9
|
+
const { preview } = store
|
|
10
|
+
export let thumbnailExpanded: boolean
|
|
11
|
+
const dispatch = createEventDispatcher()
|
|
12
|
+
|
|
13
|
+
function reactToPreview (_preview) {
|
|
14
|
+
if (thumbnailExpanded) dispatch('thumbnailsizechange')
|
|
15
|
+
}
|
|
16
|
+
$: reactToPreview($preview)
|
|
14
17
|
</script>
|
|
15
18
|
|
|
16
19
|
<section id={previewId} class="dialog-chooser-preview" class:image={$preview?.type === 'asset' && $preview.image} class:collapsed={!thumbnailExpanded} tabindex="-1">
|
|
@@ -141,5 +144,4 @@ $: reactToPreview($preview);
|
|
|
141
144
|
display: none;
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
|
-
|
|
145
147
|
</style>
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { AnyItem } from './ChooserAPI'
|
|
3
|
+
import { bytesToHuman, cleanUrl, type BrokenURL, type RawURL, humanFileType } from './ChooserStore'
|
|
4
|
+
|
|
5
|
+
export let item: AnyItem | RawURL | BrokenURL
|
|
6
|
+
export let singleLine = false
|
|
4
7
|
</script>
|
|
5
8
|
|
|
6
9
|
<dl class="dialog-chooser-info" aria-live="polite" class:multiLine={!singleLine} class:asset={item.type === 'asset'}>
|
|
@@ -88,5 +91,4 @@ export let singleLine = false;
|
|
|
88
91
|
white-space: nowrap;
|
|
89
92
|
text-overflow: ellipsis;
|
|
90
93
|
}
|
|
91
|
-
|
|
92
94
|
</style>
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import folderOutline from '@iconify-icons/mdi/folder-outline.js'
|
|
3
|
+
import fileLinkOutline from '@iconify-icons/mdi/file-link-outline.js'
|
|
4
|
+
import FileIcon from '../FileIcon.svelte'
|
|
5
|
+
import Icon from '../Icon.svelte'
|
|
6
|
+
import type { AnyItem } from './ChooserAPI'
|
|
7
|
+
import type { BrokenURL, RawURL } from './ChooserStore'
|
|
8
|
+
import { ScreenReaderOnly } from '@txstate-mws/svelte-components'
|
|
9
|
+
import { collapseIconSVG, expandIconSVG } from './icons'
|
|
10
|
+
import { createEventDispatcher } from 'svelte'
|
|
11
|
+
|
|
12
|
+
export let item: AnyItem | RawURL | BrokenURL
|
|
13
|
+
export let larger = false
|
|
14
|
+
export let expandable = false
|
|
15
|
+
export let expanded = false
|
|
16
|
+
const dispatch = createEventDispatcher()
|
|
17
|
+
|
|
18
|
+
function toggleThumbnailSize () {
|
|
19
|
+
dispatch('thumbnailsizechange')
|
|
20
|
+
}
|
|
16
21
|
</script>
|
|
17
22
|
<div class="dialog-chooser-thumbnail" class:expanded>
|
|
18
23
|
{#if item.type === 'asset'}
|
|
@@ -70,5 +75,4 @@ function toggleThumbnailSize() {
|
|
|
70
75
|
width: 100%;
|
|
71
76
|
height: 100%;
|
|
72
77
|
}
|
|
73
|
-
|
|
74
78
|
</style>
|
|
@@ -1,78 +1,88 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export let
|
|
8
|
-
export let
|
|
9
|
-
export let
|
|
10
|
-
export let
|
|
11
|
-
export let
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import trashLight from '@iconify-icons/ph/trash-light'
|
|
3
|
+
import { roundTo, unique } from 'txstate-utils'
|
|
4
|
+
import { createEventDispatcher } from 'svelte'
|
|
5
|
+
import { Dialog, FileIcon, Icon, type Client, type Folder } from '..'
|
|
6
|
+
|
|
7
|
+
export let title: string
|
|
8
|
+
export let folder: Folder
|
|
9
|
+
export let maxFiles: number = 200
|
|
10
|
+
export let escapable = true
|
|
11
|
+
export let mimeWhitelist: string[] = []
|
|
12
|
+
export let mimeBlacklist: string[] = []
|
|
13
|
+
export let uploader: NonNullable<Client['upload']>
|
|
14
|
+
$: whitelist = new Set(mimeWhitelist)
|
|
15
|
+
$: blacklist = new Set(mimeBlacklist)
|
|
16
|
+
|
|
17
|
+
const dispatch = createEventDispatcher()
|
|
18
|
+
|
|
19
|
+
let dragover = 0
|
|
20
|
+
let uploadList: File[] = []
|
|
21
|
+
let uploadLocked = false
|
|
22
|
+
let uploadProgress = 0
|
|
23
|
+
let uploadError: string | undefined
|
|
24
|
+
|
|
25
|
+
function onUploadEnter (e: DragEvent) {
|
|
26
|
+
if (e.dataTransfer?.items.length) dragover++
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function onUploadLeave (e: DragEvent) {
|
|
30
|
+
if (e.dataTransfer?.items.length) dragover--
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function onUploadDrop (e: DragEvent) {
|
|
34
|
+
e.preventDefault()
|
|
35
|
+
dragover = 0
|
|
31
36
|
if (!uploadLocked && e.dataTransfer?.items?.length) {
|
|
32
|
-
|
|
37
|
+
uploadList = unique(uploadList.concat(Array.from(e.dataTransfer.files)), 'name').slice(-1 * maxFiles)
|
|
33
38
|
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
e.currentTarget.value = ''
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
uploadLocked = true
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function onUploadChange (e: InputEvent & { currentTarget: HTMLInputElement }) {
|
|
42
|
+
const files = e.currentTarget.files
|
|
43
|
+
if (files?.length) uploadList = unique(uploadList.concat(Array.from(files)), 'name').slice(-1 * maxFiles)
|
|
44
|
+
e.currentTarget.value = ''
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function onUploadSubmit () {
|
|
48
|
+
if (uploadLocked) return
|
|
49
|
+
uploadLocked = true
|
|
45
50
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
const data = new FormData()
|
|
52
|
+
for (let i = 0; i < uploadList.length; i++) {
|
|
53
|
+
data.append('file' + i, uploadList[i])
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// TODO: accept new list of children from uploader function and feed it
|
|
57
|
+
// to the tree instead of refreshing the tree
|
|
58
|
+
await uploader(
|
|
59
|
+
folder,
|
|
60
|
+
uploadList,
|
|
61
|
+
ratio => { uploadProgress = ratio }
|
|
62
|
+
)
|
|
63
|
+
uploadList = []
|
|
64
|
+
uploadError = undefined
|
|
65
|
+
dispatch('saved')
|
|
66
|
+
} catch (e: any) {
|
|
67
|
+
uploadError = e.message
|
|
68
|
+
} finally {
|
|
69
|
+
uploadLocked = false
|
|
62
70
|
}
|
|
63
|
-
}
|
|
64
|
-
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function onUploadEscape () {
|
|
65
74
|
if (!uploadLocked && escapable) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
uploadList = []
|
|
76
|
+
uploadError = undefined
|
|
77
|
+
dispatch('escape')
|
|
69
78
|
}
|
|
70
|
-
}
|
|
71
|
-
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function onDeleteFile (file: File) {
|
|
72
82
|
return () => {
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
}
|
|
83
|
+
uploadList = uploadList.filter(f => f !== file)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
76
86
|
</script>
|
|
77
87
|
|
|
78
88
|
<Dialog {title} disabled={!uploadList.length} cancelText="Cancel" continueText="Upload" on:escape={onUploadEscape} on:continue={onUploadSubmit}>
|
|
@@ -157,5 +167,4 @@ function onDeleteFile(file) {
|
|
|
157
167
|
li div {
|
|
158
168
|
width: 100%;
|
|
159
169
|
}
|
|
160
|
-
|
|
161
170
|
</style>
|
|
@@ -1,70 +1,68 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
export let
|
|
9
|
-
export let
|
|
10
|
-
export let
|
|
11
|
-
export let
|
|
12
|
-
export let
|
|
13
|
-
export let
|
|
14
|
-
export let
|
|
15
|
-
export let
|
|
16
|
-
export let
|
|
17
|
-
|
|
18
|
-
let
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { type HTMLActionEntry, passActions } from '@txstate-mws/svelte-components'
|
|
3
|
+
import type { EditorView, ViewUpdate } from '@codemirror/view'
|
|
4
|
+
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
|
5
|
+
import { getDescribedBy } from '..'
|
|
6
|
+
let className = ''
|
|
7
|
+
export { className as class }
|
|
8
|
+
export let id: string | undefined = undefined
|
|
9
|
+
export let rows = 8
|
|
10
|
+
export let language: 'js' | 'css' | 'html'
|
|
11
|
+
export let use: HTMLActionEntry[] = []
|
|
12
|
+
export let inputelement: HTMLElement | undefined = undefined
|
|
13
|
+
export let extradescid: string | undefined = undefined
|
|
14
|
+
export let helptextid: string | undefined = undefined
|
|
15
|
+
export let messagesid: string | undefined = undefined
|
|
16
|
+
export let valid: boolean | undefined = undefined
|
|
17
|
+
export let invalid: boolean | undefined = undefined
|
|
18
|
+
export let value: any
|
|
19
|
+
|
|
20
|
+
const dispatch = createEventDispatcher()
|
|
21
|
+
|
|
22
|
+
let updateCode: (code: string) => void
|
|
23
|
+
let editorelement: HTMLElement | undefined
|
|
24
|
+
let editor: EditorView
|
|
25
|
+
onMount(async () => {
|
|
26
|
+
const { EditorView, minimalSetup } = await import('codemirror')
|
|
27
|
+
const { indentWithTab } = await import('@codemirror/commands')
|
|
28
|
+
const { lineNumbers, highlightActiveLine, highlightActiveLineGutter, keymap } = await import('@codemirror/view')
|
|
29
|
+
const { langmap } = await import('./langs.js')
|
|
26
30
|
editor = new EditorView({
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
31
|
+
extensions: [
|
|
32
|
+
minimalSetup,
|
|
33
|
+
lineNumbers(),
|
|
34
|
+
highlightActiveLine(),
|
|
35
|
+
highlightActiveLineGutter(),
|
|
36
|
+
langmap[language],
|
|
37
|
+
keymap.of([indentWithTab]),
|
|
38
|
+
EditorView.updateListener.of((v: ViewUpdate) => {
|
|
39
|
+
if (v.docChanged) {
|
|
40
|
+
const newval = editor.state.doc.toString()
|
|
41
|
+
if (value !== newval) dispatch('change', newval)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
],
|
|
45
|
+
parent: editorelement
|
|
46
|
+
})
|
|
44
47
|
updateCode = code => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
inputelement?.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
inputelement?.setAttribute('aria-describedby', descby);
|
|
64
|
-
else
|
|
65
|
-
inputelement?.removeAttribute('aria-describedby');
|
|
66
|
-
}
|
|
67
|
-
$: updateValidState(invalid, messagesid);
|
|
48
|
+
if (editor.state.doc.toString() !== code) editor.update([editor.state.update({ changes: { from: 0, to: editor.state.doc.length, insert: code } })])
|
|
49
|
+
}
|
|
50
|
+
inputelement = editorelement?.querySelector('.cm-content') ?? undefined
|
|
51
|
+
if (id) inputelement?.setAttribute('id', id)
|
|
52
|
+
if (className) inputelement?.classList.add(...className.split(' '), ...[])
|
|
53
|
+
updateValidState()
|
|
54
|
+
})
|
|
55
|
+
onDestroy(() => {
|
|
56
|
+
editor?.destroy()
|
|
57
|
+
})
|
|
58
|
+
$: updateCode?.(value ?? '')
|
|
59
|
+
function updateValidState (..._: any[]) {
|
|
60
|
+
inputelement?.setAttribute('aria-invalid', String(!!invalid))
|
|
61
|
+
const descby = getDescribedBy([messagesid, helptextid, extradescid])
|
|
62
|
+
if (descby) inputelement?.setAttribute('aria-describedby', descby)
|
|
63
|
+
else inputelement?.removeAttribute('aria-describedby')
|
|
64
|
+
}
|
|
65
|
+
$: updateValidState(invalid, messagesid)
|
|
68
66
|
</script>
|
|
69
67
|
|
|
70
68
|
<div bind:this={editorelement} style:--cm-editor-minh="{rows * 1.5}em" class:valid class:invalid on:paste on:focusout use:passActions={use}></div>
|
|
@@ -81,5 +79,4 @@ $: updateValidState(invalid, messagesid);
|
|
|
81
79
|
overflow: auto;
|
|
82
80
|
resize: vertical;
|
|
83
81
|
}
|
|
84
|
-
|
|
85
82
|
</style>
|
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
export let
|
|
9
|
-
export let
|
|
10
|
-
export let
|
|
11
|
-
export let
|
|
12
|
-
export let
|
|
13
|
-
export let
|
|
14
|
-
export let
|
|
15
|
-
export let
|
|
16
|
-
export let
|
|
17
|
-
export let
|
|
18
|
-
export let
|
|
19
|
-
export let
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLActionEntry } from '@txstate-mws/svelte-components'
|
|
3
|
+
import { nullableSerialize, nullableDeserialize } from '@txstate-mws/svelte-forms'
|
|
4
|
+
import { FieldStandard } from '..'
|
|
5
|
+
import CodeEditor from './CodeEditor.svelte'
|
|
6
|
+
let className = ''
|
|
7
|
+
export { className as class }
|
|
8
|
+
export let id: string | undefined = undefined
|
|
9
|
+
export let path: string
|
|
10
|
+
export let label: string = ''
|
|
11
|
+
export let notNull = false
|
|
12
|
+
export let defaultValue: any = notNull ? '' : undefined
|
|
13
|
+
export let rows = 8
|
|
14
|
+
export let conditional: boolean | undefined = undefined
|
|
15
|
+
export let required = false
|
|
16
|
+
export let use: HTMLActionEntry[] = []
|
|
17
|
+
export let inputelement: HTMLElement = undefined as any
|
|
18
|
+
export let related: true | number = 0
|
|
19
|
+
export let extradescid: string | undefined = undefined
|
|
20
|
+
export let helptext: string | undefined = undefined
|
|
21
|
+
export let language: 'js' | 'css' | 'html'
|
|
20
22
|
</script>
|
|
21
23
|
|
|
22
24
|
<FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} {related} {helptext} serialize={!notNull ? nullableSerialize : undefined} deserialize={!notNull ? nullableDeserialize : undefined} let:value let:valid let:invalid let:id let:onBlur let:setVal let:messagesid let:helptextid>
|