@foxui/text 0.4.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 (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +16 -0
  3. package/dist/components/advanced-text-area/AdvancedTextArea.svelte +58 -0
  4. package/dist/components/advanced-text-area/AdvancedTextArea.svelte.d.ts +12 -0
  5. package/dist/components/advanced-text-area/index.d.ts +1 -0
  6. package/dist/components/advanced-text-area/index.js +1 -0
  7. package/dist/components/index.d.ts +3 -0
  8. package/dist/components/index.js +3 -0
  9. package/dist/components/plain-text-editor/PlainTextEditor.svelte +133 -0
  10. package/dist/components/plain-text-editor/PlainTextEditor.svelte.d.ts +14 -0
  11. package/dist/components/plain-text-editor/index.d.ts +1 -0
  12. package/dist/components/plain-text-editor/index.js +1 -0
  13. package/dist/components/rich-text-editor/Icon.svelte +120 -0
  14. package/dist/components/rich-text-editor/Icon.svelte.d.ts +7 -0
  15. package/dist/components/rich-text-editor/RichTextEditor.svelte +427 -0
  16. package/dist/components/rich-text-editor/RichTextEditor.svelte.d.ts +18 -0
  17. package/dist/components/rich-text-editor/RichTextEditorLinkMenu.svelte +95 -0
  18. package/dist/components/rich-text-editor/RichTextEditorLinkMenu.svelte.d.ts +10 -0
  19. package/dist/components/rich-text-editor/RichTextEditorMenu.svelte +184 -0
  20. package/dist/components/rich-text-editor/RichTextEditorMenu.svelte.d.ts +19 -0
  21. package/dist/components/rich-text-editor/RichTextLink.d.ts +13 -0
  22. package/dist/components/rich-text-editor/RichTextLink.js +105 -0
  23. package/dist/components/rich-text-editor/Select.svelte +72 -0
  24. package/dist/components/rich-text-editor/Select.svelte.d.ts +13 -0
  25. package/dist/components/rich-text-editor/code.css +142 -0
  26. package/dist/components/rich-text-editor/image-upload/ImageUploadComponent.svelte +47 -0
  27. package/dist/components/rich-text-editor/image-upload/ImageUploadComponent.svelte.d.ts +4 -0
  28. package/dist/components/rich-text-editor/image-upload/ImageUploadNode.d.ts +45 -0
  29. package/dist/components/rich-text-editor/image-upload/ImageUploadNode.js +56 -0
  30. package/dist/components/rich-text-editor/index.d.ts +2 -0
  31. package/dist/components/rich-text-editor/index.js +1 -0
  32. package/dist/components/rich-text-editor/slash-menu/SuggestionSelect.svelte +88 -0
  33. package/dist/components/rich-text-editor/slash-menu/SuggestionSelect.svelte.d.ts +22 -0
  34. package/dist/components/rich-text-editor/slash-menu/index.d.ts +32 -0
  35. package/dist/components/rich-text-editor/slash-menu/index.js +168 -0
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +1 -0
  38. package/dist/types.d.ts +1 -0
  39. package/package.json +93 -0
@@ -0,0 +1,56 @@
1
+ import { Node, mergeAttributes } from '@tiptap/core';
2
+ import { SvelteNodeViewRenderer } from 'svelte-tiptap';
3
+ import ImageUploadComponent from './ImageUploadComponent.svelte';
4
+ export const ImageUploadNode = Node.create({
5
+ name: 'imageUpload',
6
+ group: 'block',
7
+ atom: true,
8
+ draggable: true,
9
+ selectable: false,
10
+ inline: false,
11
+ addAttributes() {
12
+ return {
13
+ accept: {
14
+ default: this.options.accept
15
+ },
16
+ limit: {
17
+ default: this.options.limit
18
+ },
19
+ maxSize: {
20
+ default: this.options.maxSize
21
+ },
22
+ preview: {
23
+ default: this.options.preview
24
+ }
25
+ };
26
+ },
27
+ addOptions() {
28
+ return {
29
+ accept: 'image/*',
30
+ limit: 1,
31
+ maxSize: 0,
32
+ upload: undefined,
33
+ onError: undefined,
34
+ onSuccess: undefined
35
+ };
36
+ },
37
+ addCommands() {
38
+ return {
39
+ setImageUploadNode: (options = {}) => ({ commands }) => {
40
+ return commands.insertContent({
41
+ type: this.name,
42
+ attrs: options
43
+ });
44
+ }
45
+ };
46
+ },
47
+ parseHTML() {
48
+ return [{ tag: 'div[data-type="image-upload"]' }];
49
+ },
50
+ renderHTML({ HTMLAttributes }) {
51
+ return ['div', mergeAttributes({ 'data-type': 'image-upload' }, HTMLAttributes)];
52
+ },
53
+ addNodeView() {
54
+ return SvelteNodeViewRenderer(ImageUploadComponent);
55
+ }
56
+ });
@@ -0,0 +1,2 @@
1
+ export { default as RichTextEditor } from './RichTextEditor.svelte';
2
+ export type RichTextTypes = 'paragraph' | 'heading-1' | 'heading-2' | 'heading-3' | 'blockquote' | 'code' | 'bullet-list' | 'ordered-list';
@@ -0,0 +1 @@
1
+ export { default as RichTextEditor } from './RichTextEditor.svelte';
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ import { cn } from '@foxui/core';
3
+ import type { Editor, Range } from '@tiptap/core';
4
+ import Icon from '../Icon.svelte';
5
+ import type { RichTextTypes } from '..';
6
+
7
+ type Props = {
8
+ items: {
9
+ value: RichTextTypes;
10
+ label: string;
11
+ command: ({ editor, range }: { editor: Editor; range: Range }) => void;
12
+ }[];
13
+ range: Range;
14
+ editor: Editor;
15
+ active?: number;
16
+ };
17
+
18
+ let { items, range, editor, active }: Props = $props();
19
+
20
+ let activeIndex = $state(active ?? 0);
21
+
22
+ export function setItems(value: any[]) {
23
+ items = value;
24
+ }
25
+
26
+ export function setRange(value: Range) {
27
+ range = value;
28
+ }
29
+
30
+ export function onKeyDown(event: KeyboardEvent) {
31
+ if (event.repeat) {
32
+ return false;
33
+ }
34
+ switch (event.key) {
35
+ case 'ArrowUp': {
36
+ if (activeIndex <= 0) {
37
+ activeIndex = items.length - 1;
38
+ } else {
39
+ activeIndex--;
40
+ }
41
+ return true;
42
+ }
43
+ case 'ArrowDown': {
44
+ if (activeIndex >= items.length - 1) {
45
+ activeIndex = 0;
46
+ } else {
47
+ activeIndex++;
48
+ }
49
+ return true;
50
+ }
51
+ case 'Enter': {
52
+ const selected = items[activeIndex];
53
+
54
+ if (selected) {
55
+ selected.command({ editor, range });
56
+ return true;
57
+ }
58
+ }
59
+ }
60
+
61
+ return false;
62
+ }
63
+ </script>
64
+
65
+ <menu
66
+ class={cn(
67
+ 'bg-base-50/50 border-base-500/20 overflow-hidden rounded-2xl border shadow-lg backdrop-blur-xl',
68
+ 'dark:bg-base-900/50 dark:border-base-500/10',
69
+ 'motion-safe:animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
70
+ "divide-base-300/30 dark:divide-base-800 divide-y text-sm"
71
+ )}
72
+ >
73
+
74
+ {#each items as item, index}
75
+ <button
76
+ onclick={() => item.command({ editor, range })}
77
+ class={cn(
78
+ 'text-base-900 dark:text-base-200 group relative isolate flex min-w-28 w-full cursor-pointer items-center gap-3 px-3 py-2 font-medium [&_svg]:size-3.5',
79
+ activeIndex === index
80
+ ? 'text-accent-700 dark:text-accent-400 bg-accent-500/10'
81
+ : 'hover:bg-accent-500/10'
82
+ )}
83
+ >
84
+ <Icon name={item.value} />
85
+ {item.label}
86
+ </button>
87
+ {/each}
88
+ </menu>
@@ -0,0 +1,22 @@
1
+ import type { Editor, Range } from '@tiptap/core';
2
+ import type { RichTextTypes } from '..';
3
+ type Props = {
4
+ items: {
5
+ value: RichTextTypes;
6
+ label: string;
7
+ command: ({ editor, range }: {
8
+ editor: Editor;
9
+ range: Range;
10
+ }) => void;
11
+ }[];
12
+ range: Range;
13
+ editor: Editor;
14
+ active?: number;
15
+ };
16
+ declare const SuggestionSelect: import("svelte").Component<Props, {
17
+ setItems: (value: any[]) => void;
18
+ setRange: (value: Range) => void;
19
+ onKeyDown: (event: KeyboardEvent) => boolean;
20
+ }, "">;
21
+ type SuggestionSelect = ReturnType<typeof SuggestionSelect>;
22
+ export default SuggestionSelect;
@@ -0,0 +1,32 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import type { Editor, Range } from '@tiptap/core';
3
+ import { PluginKey } from '@tiptap/pm/state';
4
+ import type { SuggestionKeyDownProps, SuggestionProps } from '@tiptap/suggestion';
5
+ import type { RichTextTypes } from '..';
6
+ declare const _default: Extension<any, any>;
7
+ export default _default;
8
+ export declare function suggestion({ char, pluginKey, switchTo, processImageFile }: {
9
+ char: string;
10
+ pluginKey: string;
11
+ switchTo: (value: RichTextTypes) => void;
12
+ processImageFile: (file: File) => void;
13
+ }): {
14
+ char: string;
15
+ pluginKey: PluginKey<any>;
16
+ items: ({ query }: {
17
+ query: string;
18
+ }) => {
19
+ value: string;
20
+ label: string;
21
+ command: ({ editor, range }: {
22
+ editor: Editor;
23
+ range: Range;
24
+ }) => void;
25
+ }[];
26
+ render: () => {
27
+ onStart: (props: SuggestionProps) => void;
28
+ onUpdate: (props: SuggestionProps) => void;
29
+ onKeyDown: (props: SuggestionKeyDownProps) => boolean;
30
+ onExit: () => void;
31
+ };
32
+ };
@@ -0,0 +1,168 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import Suggestion from '@tiptap/suggestion';
3
+ import { PluginKey } from '@tiptap/pm/state';
4
+ import SuggestionSelect from './SuggestionSelect.svelte';
5
+ import { mount, unmount } from 'svelte';
6
+ import tippy, {} from 'tippy.js';
7
+ export default Extension.create({
8
+ name: 'slash',
9
+ addOptions() {
10
+ return {
11
+ suggestion: {
12
+ char: '/',
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ command: ({ editor, range, props }) => {
15
+ props.command({ editor, range });
16
+ }
17
+ }
18
+ };
19
+ },
20
+ addProseMirrorPlugins() {
21
+ return [
22
+ Suggestion({
23
+ editor: this.editor,
24
+ ...this.options.suggestion
25
+ })
26
+ ];
27
+ }
28
+ });
29
+ export function suggestion({ char, pluginKey, switchTo, processImageFile }) {
30
+ return {
31
+ char,
32
+ pluginKey: new PluginKey(pluginKey),
33
+ items: ({ query }) => {
34
+ return [
35
+ {
36
+ value: 'paragraph',
37
+ label: 'Paragraph',
38
+ command: ({ editor, range }) => {
39
+ editor.chain().focus().deleteRange(range).run();
40
+ switchTo('paragraph');
41
+ }
42
+ },
43
+ {
44
+ value: 'heading-1',
45
+ label: 'Heading 1',
46
+ command: ({ editor, range }) => {
47
+ editor.chain().focus().deleteRange(range).run();
48
+ switchTo('heading-1');
49
+ }
50
+ },
51
+ {
52
+ value: 'heading-2',
53
+ label: 'Heading 2',
54
+ command: ({ editor, range }) => {
55
+ editor.chain().focus().deleteRange(range).run();
56
+ switchTo('heading-2');
57
+ }
58
+ },
59
+ {
60
+ value: 'heading-3',
61
+ label: 'Heading 3',
62
+ command: ({ editor, range }) => {
63
+ editor.chain().focus().deleteRange(range).run();
64
+ switchTo('heading-3');
65
+ }
66
+ },
67
+ {
68
+ value: 'blockquote',
69
+ label: 'Blockquote',
70
+ command: ({ editor, range }) => {
71
+ editor.chain().focus().deleteRange(range).run();
72
+ switchTo('blockquote');
73
+ }
74
+ },
75
+ {
76
+ value: 'code',
77
+ label: 'Code Block',
78
+ command: ({ editor, range }) => {
79
+ editor.chain().focus().deleteRange(range).run();
80
+ switchTo('code');
81
+ }
82
+ },
83
+ {
84
+ value: 'bullet-list',
85
+ label: 'Bullet List',
86
+ command: ({ editor, range }) => {
87
+ editor.chain().focus().deleteRange(range).run();
88
+ switchTo('bullet-list');
89
+ }
90
+ },
91
+ {
92
+ value: 'ordered-list',
93
+ label: 'Ordered List',
94
+ command: ({ editor, range }) => {
95
+ editor.chain().focus().deleteRange(range).run();
96
+ switchTo('ordered-list');
97
+ }
98
+ },
99
+ {
100
+ value: 'image',
101
+ label: 'Add Image',
102
+ command: ({ editor, range }) => {
103
+ editor.chain().focus().deleteRange(range).run();
104
+ const fileInput = document.createElement('input');
105
+ fileInput.type = 'file';
106
+ fileInput.click();
107
+ fileInput.addEventListener('change', (event) => {
108
+ const input = event.target;
109
+ if (!input.files?.length)
110
+ return;
111
+ const file = input.files[0];
112
+ if (!file?.type.startsWith('image/'))
113
+ return;
114
+ processImageFile(file);
115
+ input.remove();
116
+ });
117
+ }
118
+ }
119
+ ].filter((item) => item.label.toLowerCase().includes(query.toLowerCase()));
120
+ },
121
+ render: () => {
122
+ let component;
123
+ let popup;
124
+ return {
125
+ onStart: (props) => {
126
+ const element = document.createElement('div');
127
+ component = mount(SuggestionSelect, {
128
+ target: element,
129
+ props
130
+ });
131
+ if (!props.clientRect) {
132
+ return;
133
+ }
134
+ popup = tippy('body', {
135
+ getReferenceClientRect: props.clientRect,
136
+ appendTo: () => document.body,
137
+ content: element,
138
+ showOnCreate: true,
139
+ interactive: true,
140
+ trigger: 'manual',
141
+ placement: 'bottom-start'
142
+ });
143
+ },
144
+ onUpdate: (props) => {
145
+ component.setItems(props.items);
146
+ component.setRange(props.range);
147
+ if (!props.clientRect) {
148
+ return;
149
+ }
150
+ popup[0].setProps({
151
+ getReferenceClientRect: props.clientRect
152
+ });
153
+ },
154
+ onKeyDown: (props) => {
155
+ if (props.event.key === 'Escape') {
156
+ popup[0].hide();
157
+ return true;
158
+ }
159
+ return component.onKeyDown(props.event);
160
+ },
161
+ onExit: () => {
162
+ popup[0].destroy();
163
+ unmount(component);
164
+ }
165
+ };
166
+ }
167
+ };
168
+ }
@@ -0,0 +1 @@
1
+ export * from './components';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './components';
@@ -0,0 +1 @@
1
+ export * from './index';
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@foxui/text",
3
+ "private": false,
4
+ "version": "0.4.0",
5
+ "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "sideEffects": [
10
+ "**/*.css"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/types.d.ts",
15
+ "svelte": "./dist/index.js"
16
+ }
17
+ },
18
+ "types": "./dist/types.d.ts",
19
+ "svelte": "./dist/index.js",
20
+ "devDependencies": {
21
+ "@eslint/compat": "^1.2.5",
22
+ "@eslint/js": "^9.18.0",
23
+ "@sveltejs/adapter-auto": "^6.0.0",
24
+ "@sveltejs/adapter-static": "^3.0.8",
25
+ "@sveltejs/kit": "^2.16.0",
26
+ "@sveltejs/package": "^2.3.11",
27
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
28
+ "@tailwindcss/forms": "^0.5.9",
29
+ "@tailwindcss/typography": "^0.5.15",
30
+ "@tailwindcss/vite": "^4.1.5",
31
+ "eslint": "^9.18.0",
32
+ "eslint-config-prettier": "^10.0.1",
33
+ "eslint-plugin-svelte": "^3.0.0",
34
+ "globals": "^16.0.0",
35
+ "prettier": "^3.4.2",
36
+ "prettier-plugin-svelte": "^3.3.3",
37
+ "prettier-plugin-tailwindcss": "^0.6.11",
38
+ "svelte": "^5.0.0",
39
+ "svelte-check": "^4.0.0",
40
+ "tailwindcss": "^4.1.5",
41
+ "typescript": "^5.0.0",
42
+ "typescript-eslint": "^8.20.0",
43
+ "vite": "^6.2.6"
44
+ },
45
+ "dependencies": {
46
+ "@tiptap/core": "^2.12.0",
47
+ "@tiptap/extension-bubble-menu": "^2.12.0",
48
+ "@tiptap/extension-code-block-lowlight": "^2.12.0",
49
+ "@tiptap/extension-document": "^2.12.0",
50
+ "@tiptap/extension-dropcursor": "^2.12.0",
51
+ "@tiptap/extension-history": "^2.12.0",
52
+ "@tiptap/extension-image": "^2.12.0",
53
+ "@tiptap/extension-link": "^2.12.0",
54
+ "@tiptap/extension-paragraph": "^2.12.0",
55
+ "@tiptap/extension-placeholder": "^2.12.0",
56
+ "@tiptap/extension-strike": "^2.12.0",
57
+ "@tiptap/extension-text": "^2.12.0",
58
+ "@tiptap/extension-typography": "^2.12.0",
59
+ "@tiptap/extension-underline": "^2.12.0",
60
+ "@tiptap/pm": "^2.12.0",
61
+ "@tiptap/starter-kit": "^2.12.0",
62
+ "@tiptap/suggestion": "^2.12.0",
63
+ "bits-ui": "^1.4.3",
64
+ "lowlight": "^3.3.0",
65
+ "state": "link:@tiptap/pm/state",
66
+ "svelte-tiptap": "^2.1.0",
67
+ "tippy.js": "^6.3.7",
68
+ "@foxui/core": "0.4.0"
69
+ },
70
+ "peerDependencies": {
71
+ "svelte": ">=5",
72
+ "tailwindcss": ">=3"
73
+ },
74
+ "description": "ui kit - svelte 5 + tailwind 4 - text components",
75
+ "homepage": "https://flo-bit.dev/ui-kit",
76
+ "repository": {
77
+ "type": "git",
78
+ "url": "git+https://github.com/flo-bit/ui-kit.git"
79
+ },
80
+ "author": "flo-bit (http://flo-bit.dev/)",
81
+ "bugs": "https://github.com/flo-bit/ui-kit/issues",
82
+ "license": "MIT",
83
+ "scripts": {
84
+ "dev": "vite dev",
85
+ "build": "vite build && npm run prepack",
86
+ "build:package": "vite build && npm run prepack",
87
+ "preview": "vite preview",
88
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
89
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
90
+ "format": "prettier --write .",
91
+ "lint": "prettier --check . && eslint ."
92
+ }
93
+ }