@streamscloud/kit 0.2.1 → 0.2.2

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 (104) hide show
  1. package/dist/styles/_input.scss +98 -0
  2. package/dist/styles/_mixins.scss +2 -2
  3. package/dist/styles/reset.css +1 -1
  4. package/dist/ui/color-picker/cmp.color-picker.svelte +3 -12
  5. package/dist/ui/color-picker/cmp.color-picker.svelte.d.ts +3 -9
  6. package/dist/ui/dialog/cmp.dialog.svelte +1 -1
  7. package/dist/ui/dropdown/cmp.dropdown-item.svelte +93 -0
  8. package/dist/ui/dropdown/cmp.dropdown-item.svelte.d.ts +32 -0
  9. package/dist/ui/dropdown/cmp.dropdown-panel.svelte +29 -0
  10. package/dist/ui/dropdown/cmp.dropdown-panel.svelte.d.ts +18 -0
  11. package/dist/ui/dropdown/cmp.dropdown.svelte +72 -7
  12. package/dist/ui/dropdown/cmp.dropdown.svelte.d.ts +3 -1
  13. package/dist/ui/dropdown/index.d.ts +2 -0
  14. package/dist/ui/dropdown/index.js +2 -0
  15. package/dist/ui/dynamic-component/cmp.dynamic-component.svelte +0 -5
  16. package/dist/ui/dynamic-component/cmp.dynamic-component.svelte.d.ts +2 -8
  17. package/dist/ui/emoji-picker/cmp.emoji-panel.svelte +186 -0
  18. package/dist/ui/emoji-picker/cmp.emoji-panel.svelte.d.ts +21 -0
  19. package/dist/ui/emoji-picker/cmp.emoji-picker.svelte +35 -0
  20. package/dist/ui/emoji-picker/cmp.emoji-picker.svelte.d.ts +15 -0
  21. package/dist/ui/emoji-picker/emoji-list.d.ts +2 -0
  22. package/dist/ui/emoji-picker/emoji-list.js +1754 -0
  23. package/dist/ui/emoji-picker/emoji-picker-localization.d.ts +5 -0
  24. package/dist/ui/emoji-picker/emoji-picker-localization.js +40 -0
  25. package/dist/ui/emoji-picker/index.d.ts +2 -0
  26. package/dist/ui/emoji-picker/index.js +2 -0
  27. package/dist/ui/emoji-picker/types.d.ts +8 -0
  28. package/dist/ui/emoji-picker/types.js +1 -0
  29. package/dist/ui/form-group/cmp.form-group-label.svelte.d.ts +1 -0
  30. package/dist/ui/form-group/cmp.form-group.svelte.d.ts +1 -0
  31. package/dist/ui/icon-text/cmp.icon-text.svelte +0 -9
  32. package/dist/ui/icon-text/cmp.icon-text.svelte.d.ts +4 -9
  33. package/dist/ui/inputs/index.d.ts +6 -0
  34. package/dist/ui/inputs/index.js +5 -0
  35. package/dist/ui/inputs/input/cmp.input-validatable.svelte +57 -0
  36. package/dist/ui/inputs/input/cmp.input-validatable.svelte.d.ts +56 -0
  37. package/dist/ui/inputs/input/cmp.input.svelte +235 -0
  38. package/dist/ui/inputs/input/cmp.input.svelte.d.ts +60 -0
  39. package/dist/ui/inputs/input/index.d.ts +2 -0
  40. package/dist/ui/inputs/input/index.js +2 -0
  41. package/dist/ui/inputs/input-emoji-picker/cmp.input-emoji-picker.svelte +44 -0
  42. package/dist/ui/inputs/input-emoji-picker/cmp.input-emoji-picker.svelte.d.ts +9 -0
  43. package/dist/ui/inputs/input-emoji-picker/index.d.ts +2 -0
  44. package/dist/ui/inputs/input-emoji-picker/index.js +2 -0
  45. package/dist/ui/inputs/input-emoji-picker/input-emoji-picker-container.d.ts +2 -0
  46. package/dist/ui/inputs/input-emoji-picker/input-emoji-picker-container.js +16 -0
  47. package/dist/ui/inputs/numeral-input/cmp.numeral-input-validatable.svelte +55 -0
  48. package/dist/ui/inputs/numeral-input/cmp.numeral-input-validatable.svelte.d.ts +62 -0
  49. package/dist/ui/inputs/numeral-input/cmp.numeral-input.svelte +248 -0
  50. package/dist/ui/inputs/numeral-input/cmp.numeral-input.svelte.d.ts +66 -0
  51. package/dist/ui/inputs/numeral-input/index.d.ts +2 -0
  52. package/dist/ui/inputs/numeral-input/index.js +2 -0
  53. package/dist/ui/inputs/pin-input/cmp.pin-input.svelte +58 -0
  54. package/dist/ui/inputs/pin-input/cmp.pin-input.svelte.d.ts +23 -0
  55. package/dist/ui/inputs/pin-input/index.d.ts +1 -0
  56. package/dist/ui/inputs/pin-input/index.js +1 -0
  57. package/dist/ui/inputs/pin-input/pin-input-generator.d.ts +27 -0
  58. package/dist/ui/inputs/pin-input/pin-input-generator.js +114 -0
  59. package/dist/ui/inputs/rich-text-input/cmp.rich-text-input.svelte +55 -0
  60. package/dist/ui/inputs/rich-text-input/cmp.rich-text-input.svelte.d.ts +43 -0
  61. package/dist/ui/inputs/rich-text-input/index.d.ts +2 -0
  62. package/dist/ui/inputs/rich-text-input/index.js +1 -0
  63. package/dist/ui/inputs/rich-text-input/rich-text-input-localization.d.ts +12 -0
  64. package/dist/ui/inputs/rich-text-input/rich-text-input-localization.js +48 -0
  65. package/dist/ui/inputs/rich-text-input/tinymce-input.svelte +250 -0
  66. package/dist/ui/inputs/rich-text-input/tinymce-input.svelte.d.ts +25 -0
  67. package/dist/ui/inputs/rich-text-input/tinymce.declarations.d.ts +7 -0
  68. package/dist/ui/inputs/rich-text-input/types.d.ts +4 -0
  69. package/dist/ui/inputs/rich-text-input/types.js +1 -0
  70. package/dist/ui/inputs/rich-text-input/validated-link-button.d.ts +3 -0
  71. package/dist/ui/inputs/rich-text-input/validated-link-button.js +78 -0
  72. package/dist/ui/inputs/textarea/cmp.textarea-validatable.svelte +35 -0
  73. package/dist/ui/inputs/textarea/cmp.textarea-validatable.svelte.d.ts +53 -0
  74. package/dist/ui/inputs/textarea/cmp.textarea.svelte +247 -0
  75. package/dist/ui/inputs/textarea/cmp.textarea.svelte.d.ts +57 -0
  76. package/dist/ui/inputs/textarea/index.d.ts +2 -0
  77. package/dist/ui/inputs/textarea/index.js +2 -0
  78. package/dist/ui/media-viewer-dialog/cmp.media-viewer-dialog.svelte.d.ts +2 -0
  79. package/dist/ui/selects/_multiselect.scss +282 -0
  80. package/dist/ui/selects/_singleselect.scss +175 -0
  81. package/dist/ui/selects/cmp.multiselect.svelte +530 -0
  82. package/dist/ui/selects/cmp.multiselect.svelte.d.ts +85 -0
  83. package/dist/ui/selects/cmp.search-multiselect.svelte +532 -0
  84. package/dist/ui/selects/cmp.search-multiselect.svelte.d.ts +67 -0
  85. package/dist/ui/selects/cmp.singleselect.svelte +381 -0
  86. package/dist/ui/selects/cmp.singleselect.svelte.d.ts +78 -0
  87. package/dist/ui/selects/index.d.ts +5 -0
  88. package/dist/ui/selects/index.js +4 -0
  89. package/dist/ui/selects/select-localization.d.ts +6 -0
  90. package/dist/ui/selects/select-localization.js +27 -0
  91. package/dist/ui/selects/types.d.ts +29 -0
  92. package/dist/ui/selects/types.js +1 -0
  93. package/dist/ui/time-ago/cmp.time-ago.svelte +0 -6
  94. package/dist/ui/time-ago/cmp.time-ago.svelte.d.ts +2 -6
  95. package/dist/ui/validatable/_validatable.scss +34 -0
  96. package/dist/ui/validatable/cmp.validatable.svelte +57 -0
  97. package/dist/ui/validatable/cmp.validatable.svelte.d.ts +49 -0
  98. package/dist/ui/validatable/cmp.validation-error.svelte +52 -0
  99. package/dist/ui/validatable/cmp.validation-error.svelte.d.ts +42 -0
  100. package/dist/ui/validatable/index.d.ts +2 -0
  101. package/dist/ui/validatable/index.js +2 -0
  102. package/package.json +31 -5
  103. package/dist/ui/color-picker/cmp.input-stub.svelte +0 -98
  104. package/dist/ui/color-picker/cmp.input-stub.svelte.d.ts +0 -40
@@ -0,0 +1,55 @@
1
+ <script lang="ts">import { InputEmojiPicker, inputEmojiPickerContainer } from '../input-emoji-picker';
2
+ import { default as TinymceInput } from './tinymce-input.svelte';
3
+ let { value, options = undefined, debounce = 0, placeholder = undefined, autofocus = false, disabled = false, rows = 0, emoji = false, borderless = false, on } = $props();
4
+ let insertEmojiToTinymce = $state(null);
5
+ const onEmojiSelected = (emojiChar) => {
6
+ insertEmojiToTinymce?.(emojiChar);
7
+ };
8
+ </script>
9
+
10
+ <div class="rich-text-input" class:rich-text-input--has-emoji={emoji} use:inputEmojiPickerContainer>
11
+ <TinymceInput
12
+ value={value}
13
+ options={options}
14
+ placeholder={placeholder}
15
+ disabled={disabled}
16
+ borderless={borderless}
17
+ rows={rows}
18
+ autofocus={autofocus || ''}
19
+ debounce={debounce}
20
+ on={{
21
+ change: (e) => on?.change?.(e),
22
+ mounted: (e) => (insertEmojiToTinymce = e.insertEmoji)
23
+ }} />
24
+ {#if emoji}
25
+ <InputEmojiPicker on={{ select: onEmojiSelected }} />
26
+ {/if}
27
+ </div>
28
+
29
+ <!--
30
+ @component
31
+ Rich text editor powered by TinyMCE with optional emoji picker support.
32
+
33
+ ### CSS Custom Properties
34
+ | Property | Description | Default |
35
+ |---|---|---|
36
+ | `--sc-kit--rich-text-input--root--font-size` | Root font size for em scaling | `1rem` |
37
+ | `--sc-kit--rich-text-input--width` | Container width | `100%` |
38
+ | `--sc-kit--rich-text-input--padding--block` | Block (vertical) padding | `0.5em` |
39
+ | `--sc-kit--rich-text-input--padding--inline` | Inline (horizontal) padding | `0.5em` |
40
+ | `--sc-kit--rich-text-input--accent-color` | Focus accent color | light-dark primary-500/primary-400 |
41
+ | `--sc-kit--rich-text-input--background` | Background color | light-dark white/gray-900 |
42
+ | `--sc-kit--rich-text-input--background--disabled` | Disabled background | light-dark neutral-50/neutral-800 |
43
+ | `--sc-kit--rich-text-input--border-color` | Border color | light-dark neutral-300/neutral-600 |
44
+ | `--sc-kit--rich-text-input--border-radius` | Border radius | `0.25em` |
45
+ | `--sc-kit--rich-text-input--text--font-size` | Text font size | `0.875em` |
46
+ | `--sc-kit--rich-text-input--text--color` | Text color | light-dark gray-800/white |
47
+ | `--sc-kit--rich-text-input--placeholder--color` | Placeholder color | inherited from border |
48
+ -->
49
+
50
+ <style>.rich-text-input {
51
+ position: relative;
52
+ }
53
+ .rich-text-input--has-emoji {
54
+ --_--input--padding-right: 1.5em;
55
+ }</style>
@@ -0,0 +1,43 @@
1
+ import type { RichTextInputOptions } from './types';
2
+ type Props = {
3
+ /** HTML content */
4
+ value: string | null | undefined;
5
+ /** TinyMCE configuration options */
6
+ options?: RichTextInputOptions;
7
+ /** Debounce delay in ms for change events @default 0 */
8
+ debounce?: number;
9
+ placeholder?: string;
10
+ autofocus?: boolean;
11
+ disabled?: boolean;
12
+ /** Minimum visible rows @default 0 */
13
+ rows?: number;
14
+ /** Show emoji picker trigger */
15
+ emoji?: boolean;
16
+ /** Remove border, background, and accent shadow */
17
+ borderless?: boolean;
18
+ on: {
19
+ change: (value: string) => void;
20
+ };
21
+ };
22
+ /**
23
+ * Rich text editor powered by TinyMCE with optional emoji picker support.
24
+ *
25
+ * ### CSS Custom Properties
26
+ * | Property | Description | Default |
27
+ * |---|---|---|
28
+ * | `--sc-kit--rich-text-input--root--font-size` | Root font size for em scaling | `1rem` |
29
+ * | `--sc-kit--rich-text-input--width` | Container width | `100%` |
30
+ * | `--sc-kit--rich-text-input--padding--block` | Block (vertical) padding | `0.5em` |
31
+ * | `--sc-kit--rich-text-input--padding--inline` | Inline (horizontal) padding | `0.5em` |
32
+ * | `--sc-kit--rich-text-input--accent-color` | Focus accent color | light-dark primary-500/primary-400 |
33
+ * | `--sc-kit--rich-text-input--background` | Background color | light-dark white/gray-900 |
34
+ * | `--sc-kit--rich-text-input--background--disabled` | Disabled background | light-dark neutral-50/neutral-800 |
35
+ * | `--sc-kit--rich-text-input--border-color` | Border color | light-dark neutral-300/neutral-600 |
36
+ * | `--sc-kit--rich-text-input--border-radius` | Border radius | `0.25em` |
37
+ * | `--sc-kit--rich-text-input--text--font-size` | Text font size | `0.875em` |
38
+ * | `--sc-kit--rich-text-input--text--color` | Text color | light-dark gray-800/white |
39
+ * | `--sc-kit--rich-text-input--placeholder--color` | Placeholder color | inherited from border |
40
+ */
41
+ declare const Cmp: import("svelte").Component<Props, {}, "">;
42
+ type Cmp = ReturnType<typeof Cmp>;
43
+ export default Cmp;
@@ -0,0 +1,2 @@
1
+ export { default as RichTextInput } from './cmp.rich-text-input.svelte';
2
+ export type { RichTextInputOptions } from './types';
@@ -0,0 +1 @@
1
+ export { default as RichTextInput } from './cmp.rich-text-input.svelte';
@@ -0,0 +1,12 @@
1
+ export declare class RichTextInputLocalization {
2
+ get linkButton(): string;
3
+ get linkDialogTitle(): string;
4
+ get linkUrlLabel(): string;
5
+ get linkTextLabel(): string;
6
+ get linkTargetLabel(): string;
7
+ get linkTargetSelf(): string;
8
+ get linkTargetBlank(): string;
9
+ get linkCancelButton(): string;
10
+ get linkSaveButton(): string;
11
+ get linkUrlInvalid(): string;
12
+ }
@@ -0,0 +1,48 @@
1
+ import { AppLocale } from '../../../core/locale';
2
+ const loc = {
3
+ linkButton: { en: 'Insert/Edit Link', no: 'Sett inn/rediger lenke' },
4
+ linkDialogTitle: { en: 'Insert/Edit Link', no: 'Sett inn/rediger lenke' },
5
+ linkUrlLabel: { en: 'URL', no: 'URL' },
6
+ linkTextLabel: { en: 'Text to display', no: 'Tekst a vise' },
7
+ linkTargetLabel: { en: 'Open link in...', no: 'Apne lenke i...' },
8
+ linkTargetSelf: { en: 'Current window', no: 'Gjeldende vindu' },
9
+ linkTargetBlank: { en: 'New window', no: 'Nytt vindu' },
10
+ linkCancelButton: { en: 'Cancel', no: 'Avbryt' },
11
+ linkSaveButton: { en: 'Save', no: 'Lagre' },
12
+ linkUrlInvalid: {
13
+ en: 'The URL you entered is not valid. Please enter a valid URL.',
14
+ no: 'URLen du skrev inn er ikke gyldig. Vennligst skriv inn en gyldig URL.'
15
+ }
16
+ };
17
+ export class RichTextInputLocalization {
18
+ get linkButton() {
19
+ return loc.linkButton[AppLocale.current];
20
+ }
21
+ get linkDialogTitle() {
22
+ return loc.linkDialogTitle[AppLocale.current];
23
+ }
24
+ get linkUrlLabel() {
25
+ return loc.linkUrlLabel[AppLocale.current];
26
+ }
27
+ get linkTextLabel() {
28
+ return loc.linkTextLabel[AppLocale.current];
29
+ }
30
+ get linkTargetLabel() {
31
+ return loc.linkTargetLabel[AppLocale.current];
32
+ }
33
+ get linkTargetSelf() {
34
+ return loc.linkTargetSelf[AppLocale.current];
35
+ }
36
+ get linkTargetBlank() {
37
+ return loc.linkTargetBlank[AppLocale.current];
38
+ }
39
+ get linkCancelButton() {
40
+ return loc.linkCancelButton[AppLocale.current];
41
+ }
42
+ get linkSaveButton() {
43
+ return loc.linkSaveButton[AppLocale.current];
44
+ }
45
+ get linkUrlInvalid() {
46
+ return loc.linkUrlInvalid[AppLocale.current];
47
+ }
48
+ }
@@ -0,0 +1,250 @@
1
+ <script lang="ts">import { AppTheme } from '../../../core/theme';
2
+ import { DomHelper, HtmlHelper, Utils } from '../../../core/utils';
3
+ import { HtmlBlock } from '../../html-block';
4
+ import { RichTextInputLocalization } from './rich-text-input-localization';
5
+ import { addValidatedLinkButton } from './validated-link-button';
6
+ import { onDestroy, onMount } from 'svelte';
7
+ let { value, options = undefined, debounce = 0, placeholder = undefined, disabled = false, borderless = false, autofocus = 'false', modelEvents = 'change input undo redo', rows = 0, conf = undefined, on } = $props();
8
+ const id = `ti-${crypto.randomUUID()}`;
9
+ const fixedToolbarId = `fixed-toolbar-${id}`;
10
+ let element;
11
+ let editorRef = $state.raw(null);
12
+ let everFocused = false;
13
+ let lastVal = $state.raw(undefined);
14
+ let tinymce;
15
+ $effect(() => {
16
+ if (editorRef && lastVal !== value) {
17
+ setEditorValue(editorRef, value ?? '');
18
+ }
19
+ });
20
+ $effect(() => {
21
+ if (editorRef) {
22
+ editorRef.mode.set(disabled ? 'readonly' : 'design');
23
+ }
24
+ });
25
+ onMount(async () => {
26
+ const loc = new RichTextInputLocalization();
27
+ on?.mounted?.({
28
+ insertEmoji: (emoji) => {
29
+ if (!everFocused && editorRef) {
30
+ editorRef.selection.select(editorRef.getBody(), true);
31
+ editorRef.selection.collapse(false);
32
+ }
33
+ return new Promise((resolve) => {
34
+ setTimeout(() => {
35
+ editorRef?.execCommand('mceInsertContent', false, emoji);
36
+ resolve(true);
37
+ });
38
+ });
39
+ }
40
+ });
41
+ if (debounce) {
42
+ applyChange = Utils.debounce(applyChange, debounce);
43
+ }
44
+ const { default: tmi } = await import('tinymce');
45
+ await Promise.all([
46
+ import('tinymce/themes/silver'),
47
+ AppTheme.isDarkMode ? import('tinymce/skins/ui/oxide-dark/skin.min.css') : import('tinymce/skins/ui/oxide/skin.min.css'),
48
+ import('tinymce/skins/ui/oxide/content.inline.min.css'),
49
+ import('tinymce/icons/default'),
50
+ import('tinymce/plugins/link'),
51
+ import('tinymce/plugins/lists'),
52
+ import('tinymce/plugins/paste'),
53
+ import('tinymce/plugins/advlist'),
54
+ import('tinymce/plugins/table')
55
+ ]);
56
+ tinymce = tmi;
57
+ await tinymce.init({
58
+ target: element,
59
+ inline: true,
60
+ skin: false,
61
+ fixed_toolbar_container: `#${options?.fixedToolbarId || fixedToolbarId}`,
62
+ menubar: false,
63
+ auto_focus: autofocus,
64
+ placeholder: placeholder,
65
+ readonly: disabled,
66
+ toolbar: 'undo redo | formatselect | fontselect fontsizeselect | bold italic underline | forecolor backcolor | alignleft aligncenter alignright | bullist numlist outdent indent | lineheight | table | validatedlink',
67
+ toolbar_persist: options?.persistToolbar,
68
+ plugins: ['lists', 'paste', 'advlist', 'table'],
69
+ advlist_bullet_styles: 'default,circle,square,disc',
70
+ advlist_number_styles: 'default,lower-alpha,lower-roman,upper-alpha,upper-roman',
71
+ lineheight_formats: '1 1.15 1.5 2 2.5 3',
72
+ font_formats: 'Arial=arial,helvetica,sans-serif; Comic Sans MS=comic sans ms,cursive; Courier New=courier new,courier,monospace; Georgia=georgia,palatino; Helvetica=helvetica; Impact=impact,charcoal,sans-serif; Lucida Console=lucida console,monaco,monospace; Palatino Linotype=palatino linotype,book antiqua,palatino,serif; Tahoma=tahoma,geneva,sans-serif; Times New Roman=times new roman,times,serif; Trebuchet MS=trebuchet ms,sans-serif; Verdana=verdana,geneva,sans-serif',
73
+ fontsize_formats: '8px 10px 12px 14px 16px 18px 24px 36px 48px',
74
+ block_formats: 'Paragraph=p; Heading 1=h1; Heading 2=h2; Heading 3=h3; Heading 4=h4; Heading 5=h5; Heading 6=h6',
75
+ contextmenu: 'link',
76
+ paste_data_images: false,
77
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
78
+ paste_preprocess: (_, args) => {
79
+ if (!args.wordContent && !args.internal) {
80
+ const text = HtmlHelper.stripHtml(args.content);
81
+ const lines = text.split('\n');
82
+ args.content = '';
83
+ lines.forEach((line) => {
84
+ if (!line) {
85
+ line = '&nbsp;';
86
+ }
87
+ args.content += `<p>${line}</p>`;
88
+ });
89
+ }
90
+ },
91
+ ...conf,
92
+ setup: (editor) => {
93
+ editorRef = editor;
94
+ editor.on('init', () => {
95
+ if (rows) {
96
+ editor.bodyElement.style.minHeight = DomHelper.calcHeightBasedOnRows(editor.bodyElement, rows);
97
+ }
98
+ editor.on(modelEvents, () => applyChange(editor));
99
+ });
100
+ editor.on('blur', () => {
101
+ HtmlHelper.clearSelection();
102
+ notifyFocused(false);
103
+ });
104
+ editor.on('focus', () => {
105
+ everFocused = true;
106
+ notifyFocused(true);
107
+ });
108
+ addValidatedLinkButton(editor, loc);
109
+ if (typeof conf?.setup === 'function') {
110
+ conf.setup(editor);
111
+ }
112
+ }
113
+ });
114
+ });
115
+ onDestroy(() => {
116
+ if (editorRef) {
117
+ tinymce.remove(editorRef);
118
+ }
119
+ editorRef = null;
120
+ });
121
+ const notifyFocused = (foc) => {
122
+ on?.focused?.(foc);
123
+ };
124
+ let applyChange = (editor) => {
125
+ lastVal = editor.getContent();
126
+ if (lastVal !== value) {
127
+ value = lastVal;
128
+ }
129
+ on?.change?.(lastVal);
130
+ };
131
+ const setEditorValue = (editor, val) => {
132
+ editor.setContent(val);
133
+ lastVal = val;
134
+ };
135
+ </script>
136
+
137
+ <div class="tinymce-input" class:tinymce-input--disabled={disabled} class:tinymce-input--borderless={borderless}>
138
+ <span class="tinymce-input__toolbar" id={fixedToolbarId}></span>
139
+ <HtmlBlock>
140
+ <div class="tinymce-input__input" id={id} bind:this={element}></div>
141
+ </HtmlBlock>
142
+ </div>
143
+
144
+ <style>.tinymce-input {
145
+ --_tinymce-input--padding--block: var(--sc-kit--rich-text-input--padding--block, 0.5em);
146
+ --_tinymce-input--placeholder--color: var(--sc-kit--rich-text-input--placeholder--color, var(--_input--border-color));
147
+ --_--input--root--font-size: var(--sc-kit--rich-text-input--root--font-size);
148
+ --_--input--width: var(--sc-kit--rich-text-input--width);
149
+ --_--input--background: var(--sc-kit--rich-text-input--background);
150
+ --_--input--background--disabled: var(--sc-kit--rich-text-input--background--disabled);
151
+ --_--input--border-color: var(--sc-kit--rich-text-input--border-color);
152
+ --_--input--border-radius: var(--sc-kit--rich-text-input--border-radius);
153
+ --_--input--text--font-size: var(--sc-kit--rich-text-input--text--font-size);
154
+ --_--input--text--color: var(--sc-kit--rich-text-input--text--color);
155
+ --_--input--placeholder--color: var(--_tinymce-input--placeholder--color);
156
+ --_--input--accent-color: var(--sc-kit--rich-text-input--accent-color);
157
+ --_--input--padding--inline: var(--sc-kit--rich-text-input--padding--inline);
158
+ --_--input--padding--block: var(--_tinymce-input--padding--block);
159
+ --_input--root--font-size: var(--_--input--root--font-size, 1rem);
160
+ --_input--height: var(--_--input--height, 2em);
161
+ --_input--width: var(--_--input--width, 100%);
162
+ --_input--background: var(--_--input--background, light-dark(#ffffff, #1c1c1c));
163
+ --_input--background--disabled: var(--_--input--background--disabled, light-dark(#f9fafb, #1f2937));
164
+ --_input--border-color: var(--_--input--border-color, light-dark(#d1d5db, #4b5563));
165
+ --_input--border-radius: var(--_--input--border-radius, 0.25em);
166
+ --_input--icon--size: var(--_--input--icon--size, 1em);
167
+ --_input--icon--color: var(--_--input--icon--color, var(--_input--border-color));
168
+ --_input--text--font-size: var(--_--input--text--font-size, 0.875em);
169
+ --_input--text--color: var(--_--input--text--color, light-dark(#2e2e2e, #ffffff));
170
+ --_input--placeholder--color: var(--_--input--placeholder--color, var(--_input--border-color));
171
+ --_input--accent-color: var(--_--input--accent-color, light-dark(#144ab0, #5a8dec));
172
+ --_input--padding--inline: var(--_--input--padding--inline, 0.5em);
173
+ --_input--padding--block: var(--_--input--padding--block, 0);
174
+ --_input--padding-top: var(--_--input--padding-top, var(--_input--padding--block));
175
+ --_input--padding-right: var(--_--input--padding-right, var(--_input--padding--inline));
176
+ --_input--padding-bottom: var(--_--input--padding-bottom, var(--_input--padding--block));
177
+ --_input--padding-left: var(--_--input--padding-left, var(--_input--padding--inline));
178
+ font-size: var(--_input--root--font-size);
179
+ height: var(--_input--height);
180
+ color: var(--_input--text--color);
181
+ border: 1px solid var(--_input--border-color);
182
+ border-radius: var(--_input--border-radius);
183
+ width: var(--_input--width);
184
+ min-width: var(--_input--width);
185
+ background: var(--_input--background);
186
+ padding-top: var(--_input--padding-top);
187
+ padding-right: var(--_input--padding-right);
188
+ padding-bottom: var(--_input--padding-bottom);
189
+ padding-left: var(--_input--padding-left);
190
+ --_input--default-shadow-color: transparent;
191
+ --_input--accent-shadow: var(--_input--explicit-shadow-color, var(--_input--default-shadow-color));
192
+ position: relative;
193
+ box-shadow: inset 0 -0.13em var(--_input--accent-shadow);
194
+ transition: box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1);
195
+ }
196
+ .tinymce-input:focus, .tinymce-input:focus-within {
197
+ --_input--default-shadow-color: var(--_input--accent-color);
198
+ }
199
+ .tinymce-input--disabled {
200
+ background-color: var(--_input--background--disabled);
201
+ cursor: default;
202
+ }
203
+ .tinymce-input {
204
+ cursor: text;
205
+ min-height: 2em;
206
+ height: auto;
207
+ display: block;
208
+ position: relative;
209
+ }
210
+ .tinymce-input--borderless {
211
+ --_input--border-color: transparent;
212
+ --_input--background: transparent;
213
+ --_input--background--disabled: transparent;
214
+ --_input--accent-color: transparent;
215
+ --_input--explicit-shadow-color: transparent;
216
+ --_input--height: auto;
217
+ }
218
+ .tinymce-input :global(.tox-tinymce-inline .tox-editor-header) {
219
+ border-radius: 0.375em !important;
220
+ overflow: hidden;
221
+ }
222
+ .tinymce-input__input {
223
+ font-size: var(--_input--text--font-size);
224
+ color: var(--_input--text--color);
225
+ }
226
+ .tinymce-input__input::placeholder {
227
+ color: var(--_input--placeholder--color);
228
+ }
229
+ .tinymce-input__input:-webkit-autofill {
230
+ -webkit-text-fill-color: var(--_input--text--color) !important;
231
+ }
232
+ .tinymce-input__input {
233
+ min-height: 100%;
234
+ background: transparent;
235
+ }
236
+ .tinymce-input__input::before {
237
+ color: var(--_tinymce-input--placeholder--color) !important;
238
+ }
239
+ .tinymce-input__input:focus {
240
+ outline: none;
241
+ }
242
+ .tinymce-input__toolbar {
243
+ position: absolute;
244
+ left: 0;
245
+ bottom: 100%;
246
+ }
247
+
248
+ :global(.tox .tox-collection__item-label) {
249
+ white-space: nowrap;
250
+ }</style>
@@ -0,0 +1,25 @@
1
+ import type { RichTextInputOptions } from './types';
2
+ import type { RawEditorSettings } from 'tinymce';
3
+ type Props = {
4
+ value: string | null | undefined;
5
+ options?: RichTextInputOptions;
6
+ debounce?: number;
7
+ placeholder?: string;
8
+ disabled?: boolean;
9
+ /** Remove border, background, and accent shadow */
10
+ borderless?: boolean;
11
+ autofocus?: true | string;
12
+ modelEvents?: string;
13
+ rows?: number;
14
+ conf?: RawEditorSettings;
15
+ on: {
16
+ change: (value: string) => void;
17
+ focused?: (focused: boolean) => void;
18
+ mounted?: (data: {
19
+ insertEmoji: (emoji: string) => void;
20
+ }) => void;
21
+ };
22
+ };
23
+ declare const TinymceInput: import("svelte").Component<Props, {}, "">;
24
+ type TinymceInput = ReturnType<typeof TinymceInput>;
25
+ export default TinymceInput;
@@ -0,0 +1,7 @@
1
+ declare module 'tinymce/themes/silver';
2
+ declare module 'tinymce/icons/default';
3
+ declare module 'tinymce/plugins/link';
4
+ declare module 'tinymce/plugins/lists';
5
+ declare module 'tinymce/plugins/paste';
6
+ declare module 'tinymce/plugins/advlist';
7
+ declare module 'tinymce/plugins/table';
@@ -0,0 +1,4 @@
1
+ export type RichTextInputOptions = {
2
+ persistToolbar?: boolean;
3
+ fixedToolbarId?: string;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { RichTextInputLocalization } from './rich-text-input-localization';
2
+ import type { Editor } from 'tinymce';
3
+ export declare const addValidatedLinkButton: (editor: Editor, loc: RichTextInputLocalization) => void;
@@ -0,0 +1,78 @@
1
+ import { validateHref } from '../../../core/utils';
2
+ const DEFAULT_TARGET = '_blank';
3
+ export const addValidatedLinkButton = (editor, loc) => {
4
+ editor.ui.registry.addButton('validatedlink', {
5
+ icon: 'link',
6
+ tooltip: loc.linkButton,
7
+ onAction: () => {
8
+ const linkNode = editor.dom.getParent(editor.selection.getNode(), 'a');
9
+ let initialUrl = '';
10
+ let initialText = '';
11
+ let initialTarget = DEFAULT_TARGET;
12
+ if (linkNode) {
13
+ initialUrl = linkNode.getAttribute('href') ?? '';
14
+ initialText = linkNode.textContent ?? '';
15
+ initialTarget = linkNode.getAttribute('target') ?? DEFAULT_TARGET;
16
+ }
17
+ else {
18
+ initialText = editor.selection.getContent({ format: 'text' }) ?? '';
19
+ }
20
+ let userChangedText = false;
21
+ editor.windowManager.open({
22
+ title: loc.linkDialogTitle,
23
+ body: {
24
+ type: 'panel',
25
+ items: [
26
+ { type: 'input', name: 'url', label: loc.linkUrlLabel },
27
+ { type: 'input', name: 'text', label: loc.linkTextLabel },
28
+ {
29
+ type: 'listbox',
30
+ name: 'openIn',
31
+ label: loc.linkTargetLabel,
32
+ items: [
33
+ { text: loc.linkTargetSelf, value: '_self' },
34
+ { text: loc.linkTargetBlank, value: '_blank' }
35
+ ]
36
+ }
37
+ ]
38
+ },
39
+ initialData: {
40
+ url: initialUrl,
41
+ text: initialText,
42
+ openIn: initialTarget
43
+ },
44
+ buttons: [
45
+ { type: 'cancel', text: loc.linkCancelButton },
46
+ { type: 'submit', text: loc.linkSaveButton, primary: true }
47
+ ],
48
+ onChange: (api, details) => {
49
+ if (details.name === 'text' && !userChangedText) {
50
+ userChangedText = true;
51
+ }
52
+ if (details.name === 'url') {
53
+ const data = api.getData();
54
+ if (!userChangedText && !initialText) {
55
+ api.setData({ text: data.url });
56
+ }
57
+ }
58
+ },
59
+ onSubmit: (api) => {
60
+ const data = api.getData();
61
+ if (!validateHref(data.url, { allowRelative: 'prefixed' }).valid) {
62
+ editor.windowManager.alert(loc.linkUrlInvalid);
63
+ return;
64
+ }
65
+ if (linkNode) {
66
+ editor.dom.setAttrib(linkNode, 'href', data.url);
67
+ editor.dom.setAttrib(linkNode, 'target', data.openIn);
68
+ linkNode.textContent = data.text ?? data.url;
69
+ }
70
+ else {
71
+ editor.insertContent(`<a href="${data.url}" target="${data.openIn}">${data.text ?? data.url}</a>`);
72
+ }
73
+ api.close();
74
+ }
75
+ });
76
+ }
77
+ });
78
+ };
@@ -0,0 +1,35 @@
1
+ <script lang="ts" generics="T extends Record<string, unknown>">import { Utils } from '../../../core/utils';
2
+ import { FormValidationHandler } from '../../../core/validation';
3
+ import { Validatable } from '../../validatable';
4
+ import { default as Textarea } from './cmp.textarea.svelte';
5
+ let { name, handler, placeholder = '', debounce = 0, hidePlaceholderOnFocus = true, submitOnEnter = false, rows = 0, maxlength = 50000, id = '', disabled = false, autofocus = false, on } = $props();
6
+ const onChange = (value) => {
7
+ on?.change?.(value);
8
+ handler.updateValidateField(name, value);
9
+ };
10
+ const handleChangeDebounced = $derived(debounce ? Utils.debounce(onChange, debounce) : onChange);
11
+ </script>
12
+
13
+ <Validatable handler={handler} name={name}>
14
+ <Textarea
15
+ id={id}
16
+ autofocus={autofocus}
17
+ hidePlaceholderOnFocus={hidePlaceholderOnFocus}
18
+ submitOnEnter={submitOnEnter}
19
+ rows={rows}
20
+ maxlength={maxlength}
21
+ disabled={disabled}
22
+ placeholder={placeholder}
23
+ value={handler.formStringified[name]}
24
+ title={!!handler.touched[name] && !!handler.errors[name] ? handler.errors[name] : ''}
25
+ on={{
26
+ change: (e) => handleChangeDebounced(e),
27
+ submit: () => on?.submit?.(),
28
+ mounted: (e) => on?.mounted?.(e)
29
+ }} />
30
+ </Validatable>
31
+
32
+ <!--
33
+ @component
34
+ Textarea wrapped with form validation.
35
+ -->
@@ -0,0 +1,53 @@
1
+ import { FormValidationHandler } from '../../../core/validation';
2
+ declare function $$render<T extends Record<string, unknown>>(): {
3
+ props: {
4
+ /** Field name in the form handler */
5
+ name: keyof T & string;
6
+ /** Form validation handler instance */
7
+ handler: FormValidationHandler<T>;
8
+ placeholder?: string;
9
+ /** Debounce delay in ms @default 0 */
10
+ debounce?: number;
11
+ /** Hide placeholder text when focused @default true */
12
+ hidePlaceholderOnFocus?: boolean;
13
+ /** Fire submit callback on Enter key */
14
+ submitOnEnter?: boolean;
15
+ /** Minimum visible rows @default 0 */
16
+ rows?: number;
17
+ maxlength?: number;
18
+ id?: string;
19
+ disabled?: boolean;
20
+ autofocus?: boolean;
21
+ on?: {
22
+ change?: (value: string) => void;
23
+ /** Fires on Enter when submitOnEnter is enabled */
24
+ submit?: () => void;
25
+ /** Fires after mount, provides the textarea element ref */
26
+ mounted?: (data: {
27
+ textarea: HTMLTextAreaElement;
28
+ }) => void;
29
+ };
30
+ };
31
+ exports: {};
32
+ bindings: "";
33
+ slots: {};
34
+ events: {};
35
+ };
36
+ declare class __sveltets_Render<T extends Record<string, unknown>> {
37
+ props(): ReturnType<typeof $$render<T>>['props'];
38
+ events(): ReturnType<typeof $$render<T>>['events'];
39
+ slots(): ReturnType<typeof $$render<T>>['slots'];
40
+ bindings(): "";
41
+ exports(): {};
42
+ }
43
+ interface $$IsomorphicComponent {
44
+ new <T extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
45
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
46
+ } & ReturnType<__sveltets_Render<T>['exports']>;
47
+ <T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
48
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
49
+ }
50
+ /** Textarea wrapped with form validation. */
51
+ declare const Cmp: $$IsomorphicComponent;
52
+ type Cmp<T extends Record<string, unknown>> = InstanceType<typeof Cmp<T>>;
53
+ export default Cmp;