@srcker/editor-vue-next 1.0.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.
Files changed (95) hide show
  1. package/.vscode/extensions.json +3 -0
  2. package/README.md +50 -0
  3. package/index.html +13 -0
  4. package/index.ts +11 -0
  5. package/jsconfig.json +8 -0
  6. package/package.json +69 -0
  7. package/public/favicon.ico +0 -0
  8. package/src/App.vue +9 -0
  9. package/src/App.vue.js +17 -0
  10. package/src/Button/BackgroundButton.vue +331 -0
  11. package/src/Button/BackgroundButton.vue.js +243 -0
  12. package/src/Button/BlockQuoteButton.vue +26 -0
  13. package/src/Button/BlockQuoteButton.vue.js +56 -0
  14. package/src/Button/BoldButton.vue +29 -0
  15. package/src/Button/BoldButton.vue.js +56 -0
  16. package/src/Button/BulletListButton.vue +114 -0
  17. package/src/Button/BulletListButton.vue.js +147 -0
  18. package/src/Button/CodeBlockButton.vue +28 -0
  19. package/src/Button/CodeBlockButton.vue.js +56 -0
  20. package/src/Button/CodeButton.vue +30 -0
  21. package/src/Button/CodeButton.vue.js +56 -0
  22. package/src/Button/FontSizeButton.vue +85 -0
  23. package/src/Button/FontSizeButton.vue.js +131 -0
  24. package/src/Button/FormatButton.vue +25 -0
  25. package/src/Button/FormatButton.vue.js +54 -0
  26. package/src/Button/HeadingButton.vue +103 -0
  27. package/src/Button/HeadingButton.vue.js +164 -0
  28. package/src/Button/ImageUploadButton.vue +93 -0
  29. package/src/Button/ImageUploadButton.vue.js +123 -0
  30. package/src/Button/IndentLeftButton.vue +25 -0
  31. package/src/Button/IndentLeftButton.vue.js +54 -0
  32. package/src/Button/IndentRightButton.vue +29 -0
  33. package/src/Button/IndentRightButton.vue.js +54 -0
  34. package/src/Button/ItalicButton.vue +29 -0
  35. package/src/Button/ItalicButton.vue.js +56 -0
  36. package/src/Button/LineHeightButton.vue +88 -0
  37. package/src/Button/LineHeightButton.vue.js +131 -0
  38. package/src/Button/LinkButton.vue +44 -0
  39. package/src/Button/LinkButton.vue.js +69 -0
  40. package/src/Button/OrderedListButton.vue +121 -0
  41. package/src/Button/OrderedListButton.vue.js +146 -0
  42. package/src/Button/RedoButton.vue +29 -0
  43. package/src/Button/RedoButton.vue.js +55 -0
  44. package/src/Button/StrikeButton.vue +30 -0
  45. package/src/Button/StrikeButton.vue.js +56 -0
  46. package/src/Button/SubscriptButton.vue +29 -0
  47. package/src/Button/SubscriptButton.vue.js +56 -0
  48. package/src/Button/SuperscriptButton.vue +29 -0
  49. package/src/Button/SuperscriptButton.vue.js +56 -0
  50. package/src/Button/TextAlignCenterButton.vue +26 -0
  51. package/src/Button/TextAlignCenterButton.vue.js +56 -0
  52. package/src/Button/TextAlignLeftButton.vue +26 -0
  53. package/src/Button/TextAlignLeftButton.vue.js +56 -0
  54. package/src/Button/TextAlignRightButton.vue +26 -0
  55. package/src/Button/TextAlignRightButton.vue.js +56 -0
  56. package/src/Button/TextColorButton.vue +329 -0
  57. package/src/Button/TextColorButton.vue.js +243 -0
  58. package/src/Button/ThemeButton.vue +34 -0
  59. package/src/Button/ThemeButton.vue.js +63 -0
  60. package/src/Button/UnderLineButton.vue +29 -0
  61. package/src/Button/UnderLineButton.vue.js +56 -0
  62. package/src/Button/UndoButton.vue +29 -0
  63. package/src/Button/UndoButton.vue.js +55 -0
  64. package/src/Components/IconArrow.vue +16 -0
  65. package/src/Components/IconArrow.vue.js +30 -0
  66. package/src/Components/IconCheck.vue +40 -0
  67. package/src/Components/IconCheck.vue.js +59 -0
  68. package/src/EditorToolbar.vue +150 -0
  69. package/src/EditorToolbar.vue.js +306 -0
  70. package/src/Extensions/BulletListStyle.js +19 -0
  71. package/src/Extensions/BulletListStyle.ts +24 -0
  72. package/src/Extensions/FontSize.js +27 -0
  73. package/src/Extensions/FontSize.ts +37 -0
  74. package/src/Extensions/Indent.js +58 -0
  75. package/src/Extensions/Indent.ts +73 -0
  76. package/src/Extensions/OrderedListStyle.js +19 -0
  77. package/src/Extensions/OrderedListStyle.ts +24 -0
  78. package/src/Extensions/UploadImage.js +18 -0
  79. package/src/Extensions/UploadImage.ts +22 -0
  80. package/src/Extensions/shims.d.ts +26 -0
  81. package/src/RichEditor.vue +191 -0
  82. package/src/RichEditor.vue.js +199 -0
  83. package/src/env.d.ts +7 -0
  84. package/src/index.js +6 -0
  85. package/src/index.ts +11 -0
  86. package/src/main.js +4 -0
  87. package/src/main.ts +7 -0
  88. package/src/styles/style.scss +196 -0
  89. package/src/styles/theme.css +28 -0
  90. package/src/styles/variables.css +158 -0
  91. package/src/styles/variables.scss +175 -0
  92. package/src/types.js +1 -0
  93. package/src/types.ts +7 -0
  94. package/tsconfig.json +17 -0
  95. package/vite.config.ts +29 -0
@@ -0,0 +1,191 @@
1
+ <script setup lang="ts">
2
+ import { useEditor, EditorContent } from '@tiptap/vue-3'
3
+ import StarterKit from '@tiptap/starter-kit'
4
+ import Placeholder from '@tiptap/extension-placeholder'
5
+ import TextAlign from '@tiptap/extension-text-align'
6
+ import Highlight from '@tiptap/extension-highlight'
7
+ import Underline from '@tiptap/extension-underline'
8
+ import Link from '@tiptap/extension-link'
9
+ import Code from '@tiptap/extension-code'
10
+ import CodeBlock from '@tiptap/extension-code-block'
11
+ import Focus from '@tiptap/extension-focus'
12
+ import CharacterCount from '@tiptap/extension-character-count'
13
+ import TaskList from '@tiptap/extension-task-list'
14
+ import TaskItem from '@tiptap/extension-task-item'
15
+ import Heading from '@tiptap/extension-heading'
16
+ import Subscript from '@tiptap/extension-subscript'
17
+ import Superscript from '@tiptap/extension-superscript'
18
+
19
+ import { Color } from '@tiptap/extension-color'
20
+ import { LineHeight, TextStyle, BackgroundColor } from '@tiptap/extension-text-style'
21
+
22
+
23
+ import { BulletListStyle } from './Extensions/BulletListStyle'
24
+ import { OrderedListStyle } from './Extensions/OrderedListStyle'
25
+ import { Indent } from './Extensions/Indent'
26
+ import { FontSize } from './Extensions/FontSize'
27
+ import { UploadImage } from './Extensions/UploadImage'
28
+ import { BubbleMenu } from '@tiptap/vue-3/menus'
29
+
30
+ import EditorToolbar from './EditorToolbar.vue'
31
+
32
+ import { UploadImageResult } from 'src/types';
33
+
34
+ import "./styles/theme.css";
35
+ import "./styles/variables.css";
36
+ import { ref } from 'vue'
37
+
38
+
39
+ // props 类型
40
+ interface EditorProps {
41
+ theme?: 'light' | 'dark'
42
+ modelValue?: string
43
+ uploadImage?: (file: File) => Promise<UploadImageResult>
44
+ }
45
+
46
+ const props = defineProps<EditorProps>()
47
+
48
+ // 默认主题
49
+ const theme = ref<'light' | 'dark'>(props.theme ?? 'light')
50
+
51
+ // 切换主题
52
+ const toggleTheme = () => {
53
+ theme.value = theme.value === 'light' ? 'dark' : 'light'
54
+ }
55
+
56
+
57
+ // --- 3. 编辑器配置 ---
58
+ const editor = useEditor({
59
+ content: props.modelValue,
60
+ extensions: [
61
+ StarterKit,
62
+ OrderedListStyle,
63
+ BulletListStyle,
64
+ Subscript,
65
+ Superscript,
66
+ BackgroundColor,
67
+ Placeholder.configure({ placeholder: '请输入内容...' }),
68
+ TextAlign.configure({ types: ['heading', 'paragraph'] }),
69
+ Highlight.configure({ multicolor: true }),
70
+ Underline,
71
+ Link.configure({ openOnClick: false, autolink: true }),
72
+ UploadImage,
73
+ Focus.configure({ className: 'is-focused', mode: 'all' }),
74
+ CharacterCount, TaskList, TaskItem.configure({ nested: true }),
75
+ TextStyle,
76
+ Color,
77
+ FontSize,
78
+ LineHeight,
79
+ Indent,
80
+ Heading.configure({
81
+ levels: [1, 2, 3, 4, 5],
82
+ }),
83
+ // Indent,
84
+ // ListStyle,
85
+ CodeBlock.configure({
86
+ HTMLAttributes: {
87
+ class: 'code-block',
88
+ },
89
+ }),
90
+ Code
91
+
92
+ ],
93
+ })
94
+
95
+
96
+ const uploadImageBase64 = async (file: File) => {
97
+ console.log(file)
98
+
99
+ const reader = new FileReader()
100
+ reader.readAsDataURL(file)
101
+
102
+ const url = await new Promise<string>((resolve, reject) => {
103
+ reader.onload = () => resolve(reader.result as string)
104
+ reader.onerror = (err) => reject(err)
105
+ })
106
+
107
+ return {
108
+ url,
109
+ name: file.name,
110
+ }
111
+ }
112
+ </script>
113
+
114
+ <template>
115
+ <div class="srcker-editor">
116
+
117
+ <EditorToolbar
118
+ :editor="editor!"
119
+ :class="theme"
120
+ :theme="theme"
121
+ @toggle-theme="()=>toggleTheme()"
122
+ :uploadImage="(file)=> props.uploadImage? props.uploadImage(file):uploadImageBase64(file)" />
123
+
124
+ <BubbleMenu
125
+ v-if="editor"
126
+ class="bubble-menu"
127
+ :should-show="() => editor!.isActive('image')"
128
+ :options="{ placement: 'top-start', offset: 8 }"
129
+ :editor="editor">
130
+ <button @click="editor.chain().focus().updateAttributes('image', { width: '20%' }).run()">20%</button>
131
+ <button @click="editor.chain().focus().updateAttributes('image', { width: '50%' }).run()">50%</button>
132
+ <button @click="editor.chain().focus().updateAttributes('image', { width: '100%' }).run()">100%</button>
133
+ <button @click="editor.chain().focus().updateAttributes('image', { width: 'auto' }).run()">默认</button>
134
+ </BubbleMenu>
135
+
136
+ <EditorContent :editor="editor" class="editor-body srcker-html-content" :class="theme" />
137
+ </div>
138
+ </template>
139
+
140
+ <style lang="scss" scoped>
141
+
142
+ .srcker-editor {
143
+ display: flex;
144
+ flex-direction: column;
145
+
146
+ .bubble-menu{
147
+ padding: 5px;
148
+ background: #fff;
149
+ box-shadow: 0 0 15px rgba($color: #000000, $alpha: 0.15);
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 0 10px;
153
+ border-radius: 10px;
154
+
155
+ button{
156
+ outline: none;
157
+ border: none;
158
+ font-size: 14px;
159
+ color: #333;
160
+ font-weight: bold;
161
+ background: none;
162
+ cursor: pointer;
163
+ padding: 8px 10px;
164
+ border-radius: 8px;
165
+ transition: all 0.2s ease-in-out;
166
+
167
+ &:hover{
168
+ background: rgba($color: #000000, $alpha: 0.15);
169
+ }
170
+ }
171
+ }
172
+
173
+
174
+ .editor-body {
175
+ flex: 1;
176
+ cursor: text;
177
+ padding: 20px;
178
+ min-height: 400px;
179
+ color: var(--color);
180
+ background: var(--background);
181
+
182
+ :deep(.ProseMirror) {
183
+ outline: none;
184
+ }
185
+ }
186
+
187
+ }
188
+
189
+
190
+
191
+ </style>
@@ -0,0 +1,199 @@
1
+ import { useEditor, EditorContent } from '@tiptap/vue-3';
2
+ import StarterKit from '@tiptap/starter-kit';
3
+ import Placeholder from '@tiptap/extension-placeholder';
4
+ import TextAlign from '@tiptap/extension-text-align';
5
+ import Highlight from '@tiptap/extension-highlight';
6
+ import Underline from '@tiptap/extension-underline';
7
+ import Link from '@tiptap/extension-link';
8
+ import Code from '@tiptap/extension-code';
9
+ import CodeBlock from '@tiptap/extension-code-block';
10
+ import Focus from '@tiptap/extension-focus';
11
+ import CharacterCount from '@tiptap/extension-character-count';
12
+ import TaskList from '@tiptap/extension-task-list';
13
+ import TaskItem from '@tiptap/extension-task-item';
14
+ import Heading from '@tiptap/extension-heading';
15
+ import Subscript from '@tiptap/extension-subscript';
16
+ import Superscript from '@tiptap/extension-superscript';
17
+ import { Color } from '@tiptap/extension-color';
18
+ import { LineHeight, TextStyle, BackgroundColor } from '@tiptap/extension-text-style';
19
+ import { BulletListStyle } from './Extensions/BulletListStyle';
20
+ import { OrderedListStyle } from './Extensions/OrderedListStyle';
21
+ import { Indent } from './Extensions/Indent';
22
+ import { FontSize } from './Extensions/FontSize';
23
+ import { UploadImage } from './Extensions/UploadImage';
24
+ import { BubbleMenu } from '@tiptap/vue-3/menus';
25
+ import EditorToolbar from './EditorToolbar.vue';
26
+ import "./styles/theme.css";
27
+ import "./styles/variables.css";
28
+ import { ref } from 'vue';
29
+ const props = defineProps();
30
+ // 默认主题
31
+ const theme = ref(props.theme ?? 'light');
32
+ // 切换主题
33
+ const toggleTheme = () => {
34
+ theme.value = theme.value === 'light' ? 'dark' : 'light';
35
+ };
36
+ // --- 3. 编辑器配置 ---
37
+ const editor = useEditor({
38
+ content: props.modelValue,
39
+ extensions: [
40
+ StarterKit,
41
+ OrderedListStyle,
42
+ BulletListStyle,
43
+ Subscript,
44
+ Superscript,
45
+ BackgroundColor,
46
+ Placeholder.configure({ placeholder: '请输入内容...' }),
47
+ TextAlign.configure({ types: ['heading', 'paragraph'] }),
48
+ Highlight.configure({ multicolor: true }),
49
+ Underline,
50
+ Link.configure({ openOnClick: false, autolink: true }),
51
+ UploadImage,
52
+ Focus.configure({ className: 'is-focused', mode: 'all' }),
53
+ CharacterCount, TaskList, TaskItem.configure({ nested: true }),
54
+ TextStyle,
55
+ Color,
56
+ FontSize,
57
+ LineHeight,
58
+ Indent,
59
+ Heading.configure({
60
+ levels: [1, 2, 3, 4, 5],
61
+ }),
62
+ // Indent,
63
+ // ListStyle,
64
+ CodeBlock.configure({
65
+ HTMLAttributes: {
66
+ class: 'code-block',
67
+ },
68
+ }),
69
+ Code
70
+ ],
71
+ });
72
+ const uploadImageBase64 = async (file) => {
73
+ console.log(file);
74
+ const reader = new FileReader();
75
+ reader.readAsDataURL(file);
76
+ const url = await new Promise((resolve, reject) => {
77
+ reader.onload = () => resolve(reader.result);
78
+ reader.onerror = (err) => reject(err);
79
+ });
80
+ return {
81
+ url,
82
+ name: file.name,
83
+ };
84
+ };
85
+ const __VLS_ctx = {
86
+ ...{},
87
+ ...{},
88
+ ...{},
89
+ ...{},
90
+ };
91
+ let __VLS_components;
92
+ let __VLS_intrinsics;
93
+ let __VLS_directives;
94
+ __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
95
+ ...{ class: "srcker-editor" },
96
+ });
97
+ /** @type {__VLS_StyleScopedClasses['srcker-editor']} */ ;
98
+ const __VLS_0 = EditorToolbar;
99
+ // @ts-ignore
100
+ const __VLS_1 = __VLS_asFunctionalComponent1(__VLS_0, new __VLS_0({
101
+ ...{ 'onToggleTheme': {} },
102
+ editor: (__VLS_ctx.editor),
103
+ ...{ class: (__VLS_ctx.theme) },
104
+ theme: (__VLS_ctx.theme),
105
+ uploadImage: ((file) => props.uploadImage ? props.uploadImage(file) : __VLS_ctx.uploadImageBase64(file)),
106
+ }));
107
+ const __VLS_2 = __VLS_1({
108
+ ...{ 'onToggleTheme': {} },
109
+ editor: (__VLS_ctx.editor),
110
+ ...{ class: (__VLS_ctx.theme) },
111
+ theme: (__VLS_ctx.theme),
112
+ uploadImage: ((file) => props.uploadImage ? props.uploadImage(file) : __VLS_ctx.uploadImageBase64(file)),
113
+ }, ...__VLS_functionalComponentArgsRest(__VLS_1));
114
+ let __VLS_5;
115
+ const __VLS_6 = ({ toggleTheme: {} },
116
+ { onToggleTheme: (() => __VLS_ctx.toggleTheme()) });
117
+ var __VLS_3;
118
+ var __VLS_4;
119
+ if (__VLS_ctx.editor) {
120
+ let __VLS_7;
121
+ /** @ts-ignore @type {typeof __VLS_components.BubbleMenu | typeof __VLS_components.BubbleMenu} */
122
+ BubbleMenu;
123
+ // @ts-ignore
124
+ const __VLS_8 = __VLS_asFunctionalComponent1(__VLS_7, new __VLS_7({
125
+ ...{ class: "bubble-menu" },
126
+ shouldShow: (() => __VLS_ctx.editor.isActive('image')),
127
+ options: ({ placement: 'top-start', offset: 8 }),
128
+ editor: (__VLS_ctx.editor),
129
+ }));
130
+ const __VLS_9 = __VLS_8({
131
+ ...{ class: "bubble-menu" },
132
+ shouldShow: (() => __VLS_ctx.editor.isActive('image')),
133
+ options: ({ placement: 'top-start', offset: 8 }),
134
+ editor: (__VLS_ctx.editor),
135
+ }, ...__VLS_functionalComponentArgsRest(__VLS_8));
136
+ /** @type {__VLS_StyleScopedClasses['bubble-menu']} */ ;
137
+ const { default: __VLS_12 } = __VLS_10.slots;
138
+ __VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({
139
+ ...{ onClick: (...[$event]) => {
140
+ if (!(__VLS_ctx.editor))
141
+ return;
142
+ __VLS_ctx.editor.chain().focus().updateAttributes('image', { width: '20%' }).run();
143
+ // @ts-ignore
144
+ [editor, editor, editor, editor, editor, theme, theme, uploadImageBase64, toggleTheme,];
145
+ } },
146
+ });
147
+ __VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({
148
+ ...{ onClick: (...[$event]) => {
149
+ if (!(__VLS_ctx.editor))
150
+ return;
151
+ __VLS_ctx.editor.chain().focus().updateAttributes('image', { width: '50%' }).run();
152
+ // @ts-ignore
153
+ [editor,];
154
+ } },
155
+ });
156
+ __VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({
157
+ ...{ onClick: (...[$event]) => {
158
+ if (!(__VLS_ctx.editor))
159
+ return;
160
+ __VLS_ctx.editor.chain().focus().updateAttributes('image', { width: '100%' }).run();
161
+ // @ts-ignore
162
+ [editor,];
163
+ } },
164
+ });
165
+ __VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({
166
+ ...{ onClick: (...[$event]) => {
167
+ if (!(__VLS_ctx.editor))
168
+ return;
169
+ __VLS_ctx.editor.chain().focus().updateAttributes('image', { width: 'auto' }).run();
170
+ // @ts-ignore
171
+ [editor,];
172
+ } },
173
+ });
174
+ // @ts-ignore
175
+ [];
176
+ var __VLS_10;
177
+ }
178
+ let __VLS_13;
179
+ /** @ts-ignore @type {typeof __VLS_components.EditorContent} */
180
+ EditorContent;
181
+ // @ts-ignore
182
+ const __VLS_14 = __VLS_asFunctionalComponent1(__VLS_13, new __VLS_13({
183
+ editor: (__VLS_ctx.editor),
184
+ ...{ class: "editor-body srcker-html-content" },
185
+ ...{ class: (__VLS_ctx.theme) },
186
+ }));
187
+ const __VLS_15 = __VLS_14({
188
+ editor: (__VLS_ctx.editor),
189
+ ...{ class: "editor-body srcker-html-content" },
190
+ ...{ class: (__VLS_ctx.theme) },
191
+ }, ...__VLS_functionalComponentArgsRest(__VLS_14));
192
+ /** @type {__VLS_StyleScopedClasses['editor-body']} */ ;
193
+ /** @type {__VLS_StyleScopedClasses['srcker-html-content']} */ ;
194
+ // @ts-ignore
195
+ [editor, theme,];
196
+ const __VLS_export = (await import('vue')).defineComponent({
197
+ __typeProps: {},
198
+ });
199
+ export default {};
package/src/env.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ declare module '*.vue' {
4
+ import { DefineComponent } from 'vue'
5
+ const component: DefineComponent<{}, {}, any>
6
+ export default component
7
+ }
package/src/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import './styles/style.scss';
2
+ import RichEditor from './RichEditor.vue';
3
+ RichEditor.install = (app) => {
4
+ app.component('RichEditor', RichEditor);
5
+ };
6
+ export default RichEditor;
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { App } from 'vue'
2
+ import './styles/style.scss'
3
+
4
+ import RichEditor from './RichEditor.vue';
5
+
6
+
7
+ RichEditor.install = (app: App) => {
8
+ app.component('RichEditor', RichEditor)
9
+ }
10
+
11
+ export default RichEditor
package/src/main.js ADDED
@@ -0,0 +1,4 @@
1
+ import { createApp } from 'vue';
2
+ import App from './App.vue';
3
+ import './styles/style.scss';
4
+ createApp(App).mount('#app');
package/src/main.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { createApp } from 'vue'
2
+ import App from './App.vue'
3
+
4
+
5
+ import './styles/style.scss'
6
+
7
+ createApp(App).mount('#app')
@@ -0,0 +1,196 @@
1
+ /* 进入 / 离开 公共过渡 */
2
+ .dropdown-enter-active,
3
+ .dropdown-leave-active {
4
+ transition:
5
+ opacity 0.1s ease,
6
+ transform 0.1s cubic-bezier(0.4, 0, 0.2, 1);
7
+ transform-origin: top center;
8
+ }
9
+
10
+ /* 初始 & 离开结束状态 */
11
+ .dropdown-enter-from,
12
+ .dropdown-leave-to {
13
+ opacity: 0;
14
+ transform: scale(0.95) translateY(-4px);
15
+ }
16
+
17
+ /* 进入完成 & 离开开始 */
18
+ .dropdown-enter-to,
19
+ .dropdown-leave-from {
20
+ opacity: 1;
21
+ transform: scale(1) translateY(0);
22
+ }
23
+
24
+
25
+
26
+ .icon-button {
27
+ width: 30px;
28
+ height: 30px;
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ background: transparent;
33
+ border-radius: 6px;
34
+ cursor: pointer;
35
+ font-size: 14px;
36
+ color: var(--color);
37
+ border: none;
38
+ position: relative;
39
+ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
40
+
41
+ .icon{
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ svg{
46
+ width: 14px;
47
+ height: 14px;
48
+ }
49
+ }
50
+
51
+ &:hover {
52
+ color: var(--hover-color);
53
+ background: var(--hover-background);
54
+ .tips{
55
+ opacity: 1;
56
+ }
57
+ }
58
+ &.active {
59
+ background: var(--active-background);
60
+ color: var(--active-color);
61
+ }
62
+
63
+ &:disabled {
64
+ color: var(--disabled-color);
65
+ background: var(--disabled-background);
66
+ cursor: not-allowed;
67
+ }
68
+ .tips{
69
+ position: absolute;
70
+ background: #000;
71
+ color: #fff;
72
+ font-size: 12px;
73
+ white-space: nowrap;
74
+ padding: 5px 10px;
75
+ border-radius: 5px;
76
+ left: 50%;
77
+ bottom: -30px;
78
+ z-index: 88;
79
+ opacity: 0;
80
+ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
81
+ transform: translateX(-50%);
82
+
83
+ &::after {
84
+ content: '';
85
+ position: absolute;
86
+ left: 50%;
87
+ top: -6px;
88
+ transform: translateX(-50%);
89
+ width: 0;
90
+ height: 0;
91
+ border-left: 6px solid transparent;
92
+ border-right: 6px solid transparent;
93
+ border-bottom: 6px solid #000;
94
+ }
95
+ }
96
+ }
97
+
98
+ .dropdown-button{
99
+ position: relative;
100
+ display: inline-flex;
101
+ align-items: center;
102
+
103
+ .trigger {
104
+ height: 25px;
105
+ padding: 3px 8px;
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: space-between;
109
+ cursor: pointer;
110
+ border-radius: 6px;
111
+ font-size: 14px;
112
+ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
113
+ color: var(--color);
114
+ background: var(--background);
115
+
116
+ &:hover,
117
+ &.is-open {
118
+ color: var(--hover-color);
119
+ background: var(--hover-background);
120
+ }
121
+
122
+ .icon{
123
+ display: inline-flex;
124
+ align-items: center;
125
+ justify-content: center;
126
+ svg{
127
+ height: 14px;
128
+ }
129
+ }
130
+
131
+ .label {
132
+ margin: 0 5px;
133
+ display: inline-flex;
134
+ align-items: center;
135
+ white-space: nowrap;
136
+ overflow: hidden;
137
+ text-overflow: ellipsis;
138
+ }
139
+ .arrow {
140
+ font-size: 14px;
141
+ color: #666;
142
+ display: inline-flex;
143
+ align-items: center;
144
+ }
145
+ }
146
+
147
+ .dropdown {
148
+ position: absolute;
149
+ top: 100%;
150
+ left: 0;
151
+ margin-top: 5px;
152
+ background: #333;
153
+ box-shadow: 0 0px 15px rgba(0,0,0,0.05);
154
+ border-radius: 10px;
155
+ z-index: 1000;
156
+ overflow-y: auto;
157
+ padding: 5px 0;
158
+ transition: all 0.3s cubic-bezier(0.46, 0.03, 0.52, 0.96);
159
+
160
+ .item {
161
+ display: flex;
162
+ align-items: center;
163
+ cursor: pointer;
164
+ position: relative;
165
+ padding: 5px 20px;
166
+ line-height: 30px;
167
+ color: var(--color);
168
+
169
+ &:hover {
170
+ background-color: var(--hover-background);
171
+ }
172
+
173
+ &.is-selected {
174
+ background-color: var(--active-background);
175
+ }
176
+
177
+ .icon {
178
+ font-size: 14px;
179
+ width: 20px;
180
+ display: flex;
181
+ align-items: center;
182
+ justify-content: center;
183
+ font-weight: bold;
184
+ padding-right: 10px;
185
+ opacity: 0.5;
186
+ }
187
+
188
+ .text {
189
+ font-size: 14px;
190
+ white-space: nowrap;
191
+ }
192
+ }
193
+ }
194
+
195
+
196
+ }
@@ -0,0 +1,28 @@
1
+ /* ================== Dark Theme ================== */
2
+ .dark {
3
+ --color: rgba(255, 255, 255, 0.9);
4
+ --background: #0e0e11;
5
+ --border-color: rgba(255, 255, 255, 0.1);
6
+ --active-color: #226bf0;
7
+ --active-background: rgba(255, 255, 255, 0.08);
8
+ --hover-color: #aaa;
9
+ --hover-background: rgba(255, 255, 255, 0.1);
10
+ --disabled-color: rgba(255, 255, 255, 0.4);
11
+ --disabled-background: none;
12
+ }
13
+
14
+ .light {
15
+ --color: #1f1f1f;
16
+ --background: #ffffff;
17
+
18
+ --border-color: rgba(0, 0, 0, 0.1);
19
+
20
+ --active-color: #1677ff;
21
+ --active-background: #e6f4ff;
22
+
23
+ --hover-color: #555;
24
+ --hover-background: rgba(0, 0, 0, 0.05);
25
+
26
+ --disabled-color: rgba(0, 0, 0, 0.4);
27
+ --disabled-background: none;
28
+ }