@iyulab/editor-components 0.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.
- package/CHANGELOG.md +4 -0
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/dist/_virtual/_@oxc-project_runtime@0.130.0/helpers/decorate.js +9 -0
- package/dist/_virtual/_@oxc-project_runtime@0.130.0/helpers/decorateMetadata.js +6 -0
- package/dist/components/code-editor/UCodeEditor.d.ts +34 -0
- package/dist/components/code-editor/UCodeEditor.js +103 -0
- package/dist/components/code-editor/UCodeEditor.styles.d.ts +1 -0
- package/dist/components/code-editor/UCodeEditor.styles.js +53 -0
- package/dist/components/code-editor/UCodeEditor.worker.d.ts +1 -0
- package/dist/components/code-editor/UCodeEditor.worker.js +14 -0
- package/dist/components/text-editor/UTextEditor.d.ts +62 -0
- package/dist/components/text-editor/UTextEditor.js +186 -0
- package/dist/components/text-editor/UTextEditor.styles.d.ts +1 -0
- package/dist/components/text-editor/UTextEditor.styles.js +119 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/react/UCodeEditor.d.ts +9 -0
- package/dist/react/UCodeEditor.js +10 -0
- package/dist/react/UTextEditor.d.ts +9 -0
- package/dist/react/UTextEditor.js +10 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +2 -0
- package/package.json +71 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Iyulab, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @iyulab/editor-components
|
|
2
|
+
|
|
3
|
+
다양한 에디터 컴포넌트를 제공하는 라이브러리입니다.
|
|
4
|
+
|
|
5
|
+
## 포함된 컴포넌트
|
|
6
|
+
|
|
7
|
+
### CodeEditor
|
|
8
|
+
Monaco Editor를 기반으로 한 코드 에디터 컴포넌트입니다.
|
|
9
|
+
|
|
10
|
+
**특징:**
|
|
11
|
+
- 구문 강조 (JavaScript, TypeScript, JSON, CSS, HTML, Python 등)
|
|
12
|
+
- 자동완성 및 IntelliSense
|
|
13
|
+
- 에러 검사 및 린팅
|
|
14
|
+
- 다크/라이트 테마 지원
|
|
15
|
+
- 읽기 전용 모드
|
|
16
|
+
- 커스터마이징 가능한 폰트 크기
|
|
17
|
+
|
|
18
|
+
**사용법:**
|
|
19
|
+
```html
|
|
20
|
+
<code-editor
|
|
21
|
+
label="My Code Editor"
|
|
22
|
+
language="javascript"
|
|
23
|
+
theme="light"
|
|
24
|
+
font-size="14"
|
|
25
|
+
value="console.log('Hello World!');"
|
|
26
|
+
read-only="false">
|
|
27
|
+
</code-editor>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Properties:**
|
|
31
|
+
- `noHeader`: 헤더 표시 여부 (boolean, default: false)
|
|
32
|
+
- `label`: 헤더 레이블 텍스트 (string, default: "Editor")
|
|
33
|
+
- `theme`: 테마 ("light" | "dark", default: "light")
|
|
34
|
+
- `readOnly`: 읽기 전용 모드 (boolean, default: false)
|
|
35
|
+
- `language`: 프로그래밍 언어 (string, default: "json")
|
|
36
|
+
- `fontSize`: 폰트 크기 (number, default: 14)
|
|
37
|
+
- `value`: 에디터 내용 (string, default: "")
|
|
38
|
+
|
|
39
|
+
### RichTextEditor
|
|
40
|
+
Quill.js를 기반으로 한 리치 텍스트 에디터 컴포넌트입니다.
|
|
41
|
+
|
|
42
|
+
**특징:**
|
|
43
|
+
- WYSIWYG 편집 환경
|
|
44
|
+
- 다양한 텍스트 포맷팅 (굵게, 기울임, 밑줄 등)
|
|
45
|
+
- 목록, 헤더, 인용구 지원
|
|
46
|
+
- 링크 및 이미지 삽입
|
|
47
|
+
- 커스터마이징 가능한 툴바
|
|
48
|
+
- 다크/라이트 테마 지원
|
|
49
|
+
- HTML, 텍스트, Delta 형식 지원
|
|
50
|
+
|
|
51
|
+
**사용법:**
|
|
52
|
+
```html
|
|
53
|
+
<rich-text-editor
|
|
54
|
+
label="My Rich Text Editor"
|
|
55
|
+
theme="light"
|
|
56
|
+
height="300"
|
|
57
|
+
placeholder="Start writing..."
|
|
58
|
+
value="<h2>Hello World!</h2><p>This is <strong>rich text</strong>.</p>"
|
|
59
|
+
read-only="false">
|
|
60
|
+
</rich-text-editor>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Properties:**
|
|
64
|
+
- `noHeader`: 헤더 표시 여부 (boolean, default: false)
|
|
65
|
+
- `label`: 헤더 레이블 텍스트 (string, default: "Rich Text Editor")
|
|
66
|
+
- `theme`: 테마 ("light" | "dark", default: "light")
|
|
67
|
+
- `readOnly`: 읽기 전용 모드 (boolean, default: false)
|
|
68
|
+
- `placeholder`: 플레이스홀더 텍스트 (string, default: "Start writing...")
|
|
69
|
+
- `value`: 에디터 HTML 내용 (string, default: "")
|
|
70
|
+
- `height`: 에디터 높이 (number, default: 300)
|
|
71
|
+
- `toolbar`: 커스텀 툴바 설정 (array, optional)
|
|
72
|
+
|
|
73
|
+
**Methods:**
|
|
74
|
+
- `getHTML()`: HTML 형식으로 내용 반환
|
|
75
|
+
- `getText()`: 플레인 텍스트로 내용 반환
|
|
76
|
+
- `getDelta()`: Quill Delta 형식으로 내용 반환
|
|
77
|
+
- `setHTML(html)`: HTML 형식으로 내용 설정
|
|
78
|
+
- `setDelta(delta)`: Delta 형식으로 내용 설정
|
|
79
|
+
- `clear()`: 모든 내용 삭제
|
|
80
|
+
- `focus()`: 에디터에 포커스
|
|
81
|
+
|
|
82
|
+
## 설치
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm install @iyulab/editor-components
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 사용법
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
import '@iyulab/editor-components';
|
|
92
|
+
|
|
93
|
+
// 또는 개별 컴포넌트만 import
|
|
94
|
+
import { CodeEditor, RichTextEditor } from '@iyulab/editor-components';
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 이벤트
|
|
98
|
+
|
|
99
|
+
### CodeEditor
|
|
100
|
+
- `change`: 에디터 내용이 변경될 때 발생 (detail: string)
|
|
101
|
+
|
|
102
|
+
### RichTextEditor
|
|
103
|
+
- `change`: 에디터 내용이 변경될 때 발생 (detail: { html, text, delta })
|
|
104
|
+
- `copy`: 복사 버튼 클릭 시 발생 (detail: string)
|
|
105
|
+
|
|
106
|
+
## 데모
|
|
107
|
+
|
|
108
|
+
프로젝트에 포함된 `demo.html` 파일을 통해 컴포넌트들을 실제로 테스트해볼 수 있습니다.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npm run start
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 라이선스
|
|
115
|
+
|
|
116
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region \0@oxc-project+runtime@0.130.0/helpers/decorate.js
|
|
2
|
+
function __decorate(decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { __decorate };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
2
|
+
/**
|
|
3
|
+
* code editor component used by monaco-editor
|
|
4
|
+
*/
|
|
5
|
+
export declare class UCodeEditor extends UElement {
|
|
6
|
+
static styles: import('lit').CSSResultGroup[];
|
|
7
|
+
/** Specifies whether the header should be displayed or not. @default false */
|
|
8
|
+
headless: boolean;
|
|
9
|
+
/** The label text displayed in the header of the code editor. @default "Editor" */
|
|
10
|
+
label: string;
|
|
11
|
+
/** The visual theme of the code editor. Can be "light" or "dark". @default "light" */
|
|
12
|
+
theme: "light" | "dark";
|
|
13
|
+
/** Whether the editor should be in read-only mode, preventing user input. @default false */
|
|
14
|
+
readOnly: boolean;
|
|
15
|
+
/** The programming language for syntax highlighting (e.g., "json", "javascript", "typescript"). @default "json" */
|
|
16
|
+
language: string;
|
|
17
|
+
/** The font size in pixels for the text in the editor. @default 14 */
|
|
18
|
+
fontSize: number;
|
|
19
|
+
/** The current text content of the code editor. @default "" */
|
|
20
|
+
value: string;
|
|
21
|
+
private container;
|
|
22
|
+
private editor?;
|
|
23
|
+
private observer;
|
|
24
|
+
connectedCallback(): void;
|
|
25
|
+
disconnectedCallback(): void;
|
|
26
|
+
protected firstUpdated(changedProperties: any): Promise<void>;
|
|
27
|
+
protected updated(changedProperties: any): Promise<void>;
|
|
28
|
+
render(): import('lit-html').TemplateResult<1>;
|
|
29
|
+
}
|
|
30
|
+
declare global {
|
|
31
|
+
interface HTMLElementTagNameMap {
|
|
32
|
+
"u-code-editor": UCodeEditor;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import "./UCodeEditor.worker.js";
|
|
2
|
+
import { styles } from "./UCodeEditor.styles.js";
|
|
3
|
+
import { __decorateMetadata } from "../../_virtual/_@oxc-project_runtime@0.130.0/helpers/decorateMetadata.js";
|
|
4
|
+
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.130.0/helpers/decorate.js";
|
|
5
|
+
import { html, unsafeCSS } from "lit";
|
|
6
|
+
import { customElement, property } from "lit/decorators.js";
|
|
7
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
8
|
+
import * as monaco from "monaco-editor";
|
|
9
|
+
import monacoStyles from "monaco-editor/min/vs/editor/editor.main.css?inline";
|
|
10
|
+
import { Theme } from "@iyulab/components/dist/utilities/Theme.js";
|
|
11
|
+
import { UElement } from "@iyulab/components/dist/components/UElement.js";
|
|
12
|
+
//#region src/components/code-editor/UCodeEditor.ts
|
|
13
|
+
var UCodeEditor = class UCodeEditor extends UElement {
|
|
14
|
+
constructor(..._args) {
|
|
15
|
+
super(..._args);
|
|
16
|
+
this.headless = false;
|
|
17
|
+
this.label = "Editor";
|
|
18
|
+
this.theme = "light";
|
|
19
|
+
this.readOnly = false;
|
|
20
|
+
this.language = "json";
|
|
21
|
+
this.fontSize = 14;
|
|
22
|
+
this.value = "";
|
|
23
|
+
this.container = createRef();
|
|
24
|
+
this.observer = new MutationObserver(() => {
|
|
25
|
+
const theme = Theme.get() === "dark" ? "dark" : "light";
|
|
26
|
+
if (this.theme !== theme) this.theme = theme;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
static {
|
|
30
|
+
this.styles = [
|
|
31
|
+
super.styles,
|
|
32
|
+
unsafeCSS(monacoStyles),
|
|
33
|
+
styles
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
connectedCallback() {
|
|
37
|
+
super.connectedCallback();
|
|
38
|
+
this.theme = Theme.get() === "dark" ? "dark" : "light";
|
|
39
|
+
this.observer.observe(document.documentElement, {
|
|
40
|
+
attributes: true,
|
|
41
|
+
attributeFilter: ["data-theme"]
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
disconnectedCallback() {
|
|
45
|
+
this.observer.disconnect();
|
|
46
|
+
super.disconnectedCallback();
|
|
47
|
+
}
|
|
48
|
+
async firstUpdated(changedProperties) {
|
|
49
|
+
super.firstUpdated(changedProperties);
|
|
50
|
+
await this.updateComplete;
|
|
51
|
+
this.editor = monaco.editor.create(this.container.value, {
|
|
52
|
+
language: this.language,
|
|
53
|
+
theme: this.theme === "light" ? "vs-light" : "vs-dark",
|
|
54
|
+
fontSize: this.fontSize,
|
|
55
|
+
automaticLayout: true,
|
|
56
|
+
minimap: { enabled: false },
|
|
57
|
+
lineNumbersMinChars: 2,
|
|
58
|
+
lineDecorationsWidth: 1,
|
|
59
|
+
readOnly: this.readOnly,
|
|
60
|
+
value: this.value,
|
|
61
|
+
scrollBeyondLastLine: false,
|
|
62
|
+
scrollbar: { alwaysConsumeMouseWheel: false }
|
|
63
|
+
});
|
|
64
|
+
this.editor.onDidChangeModelContent(() => {
|
|
65
|
+
this.value = this.editor.getValue();
|
|
66
|
+
this.dispatchEvent(new Event("change", {
|
|
67
|
+
bubbles: true,
|
|
68
|
+
composed: true
|
|
69
|
+
}));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async updated(changedProperties) {
|
|
73
|
+
super.updated(changedProperties);
|
|
74
|
+
if (changedProperties.has("value") && this.value !== this.editor?.getValue() && !this.editor?.hasWidgetFocus()) this.editor?.setValue(this.value);
|
|
75
|
+
if (changedProperties.has("theme") && this.editor) this.editor.updateOptions({ theme: this.theme === "light" ? "vs-light" : "vs-dark" });
|
|
76
|
+
if (changedProperties.has("language") && this.editor) monaco.editor.setModelLanguage(this.editor.getModel(), this.language);
|
|
77
|
+
}
|
|
78
|
+
render() {
|
|
79
|
+
return html`
|
|
80
|
+
<div class="header" ?hidden=${this.headless}>
|
|
81
|
+
<div class="title">${this.label}</div>
|
|
82
|
+
<div class="flex"></div>
|
|
83
|
+
<slot name="header-actions"></slot>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="editor">
|
|
86
|
+
<main ${ref(this.container)}></main>
|
|
87
|
+
</div>
|
|
88
|
+
`;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
__decorate([property({
|
|
92
|
+
type: Boolean,
|
|
93
|
+
reflect: true
|
|
94
|
+
}), __decorateMetadata("design:type", Boolean)], UCodeEditor.prototype, "headless", void 0);
|
|
95
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UCodeEditor.prototype, "label", void 0);
|
|
96
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UCodeEditor.prototype, "theme", void 0);
|
|
97
|
+
__decorate([property({ type: Boolean }), __decorateMetadata("design:type", Boolean)], UCodeEditor.prototype, "readOnly", void 0);
|
|
98
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UCodeEditor.prototype, "language", void 0);
|
|
99
|
+
__decorate([property({ type: Number }), __decorateMetadata("design:type", Number)], UCodeEditor.prototype, "fontSize", void 0);
|
|
100
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UCodeEditor.prototype, "value", void 0);
|
|
101
|
+
UCodeEditor = __decorate([customElement("u-code-editor")], UCodeEditor);
|
|
102
|
+
//#endregion
|
|
103
|
+
export { UCodeEditor };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import('lit').CSSResult;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
//#region src/components/code-editor/UCodeEditor.styles.ts
|
|
3
|
+
var styles = css`
|
|
4
|
+
:host {
|
|
5
|
+
position: relative;
|
|
6
|
+
display: block;
|
|
7
|
+
width: 100%;
|
|
8
|
+
height: 100%;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
--header-height: 32px;
|
|
11
|
+
}
|
|
12
|
+
:host([headless]) {
|
|
13
|
+
--header-height: 0px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.header {
|
|
17
|
+
position: relative;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: row;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: space-between;
|
|
22
|
+
height: 24px;
|
|
23
|
+
gap: 4px;
|
|
24
|
+
padding: 4px;
|
|
25
|
+
|
|
26
|
+
.title {
|
|
27
|
+
font-size: 16px;
|
|
28
|
+
line-height: 20px;
|
|
29
|
+
font-weight: 600;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
text-overflow: ellipsis;
|
|
32
|
+
white-space: nowrap;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.flex {
|
|
36
|
+
flex: 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.editor {
|
|
41
|
+
position: absolute;
|
|
42
|
+
width: 100%;
|
|
43
|
+
height: calc(100% - var(--header-height));
|
|
44
|
+
overflow: hidden;
|
|
45
|
+
|
|
46
|
+
main {
|
|
47
|
+
width: 100%;
|
|
48
|
+
height: 100%;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
//#endregion
|
|
53
|
+
export { styles };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
|
|
2
|
+
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
|
|
3
|
+
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
|
|
4
|
+
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
|
|
5
|
+
import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
|
|
6
|
+
//#region src/components/code-editor/UCodeEditor.worker.ts
|
|
7
|
+
self.MonacoEnvironment = { getWorker(_moduleId, label) {
|
|
8
|
+
if (label === "json") return new jsonWorker();
|
|
9
|
+
if (label === "css" || label === "scss" || label === "less") return new cssWorker();
|
|
10
|
+
if (label === "html" || label === "handlebars" || label === "razor") return new htmlWorker();
|
|
11
|
+
if (label === "typescript" || label === "javascript") return new tsWorker();
|
|
12
|
+
return new editorWorker();
|
|
13
|
+
} };
|
|
14
|
+
//#endregion
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
2
|
+
export declare class UTextEditor extends UElement {
|
|
3
|
+
static styles: import('lit').CSSResultGroup[];
|
|
4
|
+
/** Specifies whether the header should be displayed or not. @default false */
|
|
5
|
+
headless: boolean;
|
|
6
|
+
/** The label text displayed in the header of the rich text editor. @default "Rich Text Editor" */
|
|
7
|
+
label: string;
|
|
8
|
+
/** The visual theme of the editor. Can be "light" or "dark". @default "light" */
|
|
9
|
+
theme?: "light" | "dark" | "system";
|
|
10
|
+
/** Whether the editor should be in read-only mode, preventing user input. @default false */
|
|
11
|
+
readOnly: boolean;
|
|
12
|
+
/** The placeholder text shown when the editor is empty. @default "Start writing..." */
|
|
13
|
+
placeholder: string;
|
|
14
|
+
/** The current HTML content of the rich text editor. @default "" */
|
|
15
|
+
value: string;
|
|
16
|
+
/** The height of the editor in pixels. @default 300 */
|
|
17
|
+
height: number;
|
|
18
|
+
/** Custom toolbar configuration. If not provided, uses default toolbar. */
|
|
19
|
+
toolbar?: string[][];
|
|
20
|
+
private container;
|
|
21
|
+
private quill;
|
|
22
|
+
private observer;
|
|
23
|
+
connectedCallback(): void;
|
|
24
|
+
disconnectedCallback(): void;
|
|
25
|
+
protected firstUpdated(changedProperties: any): Promise<void>;
|
|
26
|
+
protected updated(changedProperties: any): Promise<void>;
|
|
27
|
+
render(): import('lit-html').TemplateResult<1>;
|
|
28
|
+
private updateEditorHeight;
|
|
29
|
+
/**
|
|
30
|
+
* Get the current content as HTML
|
|
31
|
+
*/
|
|
32
|
+
getHTML(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Get the current content as plain text
|
|
35
|
+
*/
|
|
36
|
+
getText(): string;
|
|
37
|
+
/**
|
|
38
|
+
* Get the current content as Quill Delta
|
|
39
|
+
*/
|
|
40
|
+
getDelta(): any;
|
|
41
|
+
/**
|
|
42
|
+
* Set content from HTML
|
|
43
|
+
*/
|
|
44
|
+
setHTML(html: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Set content from Quill Delta
|
|
47
|
+
*/
|
|
48
|
+
setDelta(delta: any): void;
|
|
49
|
+
/**
|
|
50
|
+
* Clear all content
|
|
51
|
+
*/
|
|
52
|
+
clear(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Focus the editor
|
|
55
|
+
*/
|
|
56
|
+
focus(): void;
|
|
57
|
+
}
|
|
58
|
+
declare global {
|
|
59
|
+
interface HTMLElementTagNameMap {
|
|
60
|
+
"u-text-editor": UTextEditor;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { __decorateMetadata } from "../../_virtual/_@oxc-project_runtime@0.130.0/helpers/decorateMetadata.js";
|
|
2
|
+
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.130.0/helpers/decorate.js";
|
|
3
|
+
import { styles } from "./UTextEditor.styles.js";
|
|
4
|
+
import { html, unsafeCSS } from "lit";
|
|
5
|
+
import { customElement, property } from "lit/decorators.js";
|
|
6
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
7
|
+
import { Theme } from "@iyulab/components/dist/utilities/Theme.js";
|
|
8
|
+
import { UElement } from "@iyulab/components/dist/components/UElement.js";
|
|
9
|
+
import Quill from "quill";
|
|
10
|
+
import quillStyles from "quill/dist/quill.snow.css?inline";
|
|
11
|
+
//#region src/components/text-editor/UTextEditor.ts
|
|
12
|
+
var UTextEditor = class UTextEditor extends UElement {
|
|
13
|
+
constructor(..._args) {
|
|
14
|
+
super(..._args);
|
|
15
|
+
this.headless = false;
|
|
16
|
+
this.label = "Rich Text Editor";
|
|
17
|
+
this.theme = "light";
|
|
18
|
+
this.readOnly = false;
|
|
19
|
+
this.placeholder = "Start writing...";
|
|
20
|
+
this.value = "";
|
|
21
|
+
this.height = 300;
|
|
22
|
+
this.container = createRef();
|
|
23
|
+
this.observer = new MutationObserver(() => {
|
|
24
|
+
const theme = Theme.get();
|
|
25
|
+
if (this.theme !== theme) this.theme = theme;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
static {
|
|
29
|
+
this.styles = [
|
|
30
|
+
super.styles,
|
|
31
|
+
unsafeCSS(quillStyles),
|
|
32
|
+
styles
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
connectedCallback() {
|
|
36
|
+
super.connectedCallback();
|
|
37
|
+
this.theme = Theme.get();
|
|
38
|
+
this.observer.observe(document.documentElement, {
|
|
39
|
+
attributes: true,
|
|
40
|
+
attributeFilter: ["data-theme"]
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
disconnectedCallback() {
|
|
44
|
+
if (this.quill) this.quill = null;
|
|
45
|
+
this.observer.disconnect();
|
|
46
|
+
super.disconnectedCallback();
|
|
47
|
+
}
|
|
48
|
+
async firstUpdated(changedProperties) {
|
|
49
|
+
super.firstUpdated(changedProperties);
|
|
50
|
+
await this.updateComplete;
|
|
51
|
+
const defaultToolbar = [
|
|
52
|
+
[
|
|
53
|
+
"bold",
|
|
54
|
+
"italic",
|
|
55
|
+
"underline",
|
|
56
|
+
"strike"
|
|
57
|
+
],
|
|
58
|
+
["blockquote", "code-block"],
|
|
59
|
+
[{ "header": 1 }, { "header": 2 }],
|
|
60
|
+
[{ "list": "ordered" }, { "list": "bullet" }],
|
|
61
|
+
[{ "script": "sub" }, { "script": "super" }],
|
|
62
|
+
[{ "indent": "-1" }, { "indent": "+1" }],
|
|
63
|
+
[{ "direction": "rtl" }],
|
|
64
|
+
[{ "size": [
|
|
65
|
+
"small",
|
|
66
|
+
false,
|
|
67
|
+
"large",
|
|
68
|
+
"huge"
|
|
69
|
+
] }],
|
|
70
|
+
[{ "header": [
|
|
71
|
+
1,
|
|
72
|
+
2,
|
|
73
|
+
3,
|
|
74
|
+
4,
|
|
75
|
+
5,
|
|
76
|
+
6,
|
|
77
|
+
false
|
|
78
|
+
] }],
|
|
79
|
+
[{ "color": [] }, { "background": [] }],
|
|
80
|
+
[{ "font": [] }],
|
|
81
|
+
[{ "align": [] }],
|
|
82
|
+
["clean"],
|
|
83
|
+
["link", "image"]
|
|
84
|
+
];
|
|
85
|
+
this.quill = new Quill(this.container.value, {
|
|
86
|
+
theme: "snow",
|
|
87
|
+
readOnly: this.readOnly,
|
|
88
|
+
placeholder: this.placeholder,
|
|
89
|
+
modules: { toolbar: this.toolbar || defaultToolbar }
|
|
90
|
+
});
|
|
91
|
+
if (this.value) this.quill.root.innerHTML = this.value;
|
|
92
|
+
this.quill.on("text-change", () => {
|
|
93
|
+
const html = this.quill.root.innerHTML;
|
|
94
|
+
this.dispatchEvent(new CustomEvent("change", { detail: {
|
|
95
|
+
html,
|
|
96
|
+
text: this.quill.getText(),
|
|
97
|
+
delta: this.quill.getContents()
|
|
98
|
+
} }));
|
|
99
|
+
});
|
|
100
|
+
this.updateEditorHeight();
|
|
101
|
+
}
|
|
102
|
+
async updated(changedProperties) {
|
|
103
|
+
super.updated(changedProperties);
|
|
104
|
+
await this.updateComplete;
|
|
105
|
+
if (this.quill) {
|
|
106
|
+
if (changedProperties.has("value") && this.value !== this.quill.root.innerHTML) this.quill.root.innerHTML = this.value;
|
|
107
|
+
if (changedProperties.has("readOnly")) this.quill.enable(!this.readOnly);
|
|
108
|
+
if (changedProperties.has("placeholder")) this.quill.root.dataset.placeholder = this.placeholder;
|
|
109
|
+
if (changedProperties.has("height")) this.updateEditorHeight();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
render() {
|
|
113
|
+
return html`
|
|
114
|
+
<div class="header" ?hidden=${this.headless}>
|
|
115
|
+
<div class="title">${this.label}</div>
|
|
116
|
+
<div class="flex"></div>
|
|
117
|
+
<slot name="header-actions"></slot>
|
|
118
|
+
</div>
|
|
119
|
+
<div class="editor" style="height: ${this.height}px;">
|
|
120
|
+
<div class="quill-container" ${ref(this.container)}></div>
|
|
121
|
+
</div>
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
updateEditorHeight() {
|
|
125
|
+
if (this.quill) {
|
|
126
|
+
const editorElement = this.shadowRoot?.querySelector(".ql-editor");
|
|
127
|
+
if (editorElement) editorElement.style.height = `${this.height - 42}px`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get the current content as HTML
|
|
132
|
+
*/
|
|
133
|
+
getHTML() {
|
|
134
|
+
return this.quill ? this.quill.root.innerHTML : "";
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get the current content as plain text
|
|
138
|
+
*/
|
|
139
|
+
getText() {
|
|
140
|
+
return this.quill ? this.quill.getText() : "";
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get the current content as Quill Delta
|
|
144
|
+
*/
|
|
145
|
+
getDelta() {
|
|
146
|
+
return this.quill ? this.quill.getContents() : null;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Set content from HTML
|
|
150
|
+
*/
|
|
151
|
+
setHTML(html) {
|
|
152
|
+
if (this.quill) this.quill.root.innerHTML = html;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Set content from Quill Delta
|
|
156
|
+
*/
|
|
157
|
+
setDelta(delta) {
|
|
158
|
+
if (this.quill) this.quill.setContents(delta);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Clear all content
|
|
162
|
+
*/
|
|
163
|
+
clear() {
|
|
164
|
+
if (this.quill) this.quill.setText("");
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Focus the editor
|
|
168
|
+
*/
|
|
169
|
+
focus() {
|
|
170
|
+
if (this.quill) this.quill.focus();
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
__decorate([property({
|
|
174
|
+
type: Boolean,
|
|
175
|
+
reflect: true
|
|
176
|
+
}), __decorateMetadata("design:type", Boolean)], UTextEditor.prototype, "headless", void 0);
|
|
177
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UTextEditor.prototype, "label", void 0);
|
|
178
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UTextEditor.prototype, "theme", void 0);
|
|
179
|
+
__decorate([property({ type: Boolean }), __decorateMetadata("design:type", Boolean)], UTextEditor.prototype, "readOnly", void 0);
|
|
180
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UTextEditor.prototype, "placeholder", void 0);
|
|
181
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UTextEditor.prototype, "value", void 0);
|
|
182
|
+
__decorate([property({ type: Number }), __decorateMetadata("design:type", Number)], UTextEditor.prototype, "height", void 0);
|
|
183
|
+
__decorate([property({ type: Array }), __decorateMetadata("design:type", Array)], UTextEditor.prototype, "toolbar", void 0);
|
|
184
|
+
UTextEditor = __decorate([customElement("u-text-editor")], UTextEditor);
|
|
185
|
+
//#endregion
|
|
186
|
+
export { UTextEditor };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import('lit').CSSResult;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
//#region src/components/text-editor/UTextEditor.styles.ts
|
|
3
|
+
var styles = css`
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
border: 1px solid var(--u-neutral-300);
|
|
7
|
+
border-radius: 4px;
|
|
8
|
+
background: var(--u-neutral-0);
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
:host([theme="dark"]) {
|
|
13
|
+
border-color: var(--u-neutral-700);
|
|
14
|
+
background: var(--u-neutral-900);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.header {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
padding: 12px 20px;
|
|
21
|
+
border-bottom: 1px solid var(--u-neutral-200);
|
|
22
|
+
background: var(--u-neutral-50);
|
|
23
|
+
min-height: 48px;
|
|
24
|
+
gap: 12px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:host([theme="dark"]) .header {
|
|
28
|
+
border-bottom-color: var(--u-neutral-700);
|
|
29
|
+
background: var(--u-neutral-800);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.title {
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
color: var(--u-neutral-700);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:host([theme="dark"]) .title {
|
|
38
|
+
color: var(--u-neutral-300);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.flex {
|
|
42
|
+
flex: 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.editor {
|
|
46
|
+
position: relative;
|
|
47
|
+
height: 300px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.quill-container {
|
|
51
|
+
height: 100%;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.ql-editor {
|
|
55
|
+
font-family: var(--u-font-base);
|
|
56
|
+
font-size: 24px;
|
|
57
|
+
line-height: 1.6;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.ql-toolbar {
|
|
61
|
+
border: none !important;
|
|
62
|
+
border-bottom: 1px solid var(--u-neutral-200) !important;
|
|
63
|
+
background: var(--u-neutral-50);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
:host([theme="dark"]) .ql-toolbar {
|
|
67
|
+
border-bottom-color: var(--u-neutral-700) !important;
|
|
68
|
+
background: var(--u-neutral-800);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.ql-container {
|
|
72
|
+
border: none !important;
|
|
73
|
+
font-family: var(--u-font-base);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.ql-editor {
|
|
77
|
+
padding: 20px;
|
|
78
|
+
color: var(--u-neutral-700);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
:host([theme="dark"]) .ql-editor {
|
|
82
|
+
color: var(--u-neutral-300);
|
|
83
|
+
background: var(--u-neutral-900);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ql-toolbar .ql-stroke {
|
|
87
|
+
stroke: var(--u-neutral-600);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.ql-toolbar .ql-fill {
|
|
91
|
+
fill: var(--u-neutral-600);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
:host([theme="dark"]) .ql-toolbar .ql-stroke {
|
|
95
|
+
stroke: var(--u-neutral-400);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
:host([theme="dark"]) .ql-toolbar .ql-fill {
|
|
99
|
+
fill: var(--u-neutral-400);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.ql-toolbar button:hover .ql-stroke {
|
|
103
|
+
stroke: var(--u-blue-600);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.ql-toolbar button:hover .ql-fill {
|
|
107
|
+
fill: var(--u-blue-600);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.ql-toolbar button.ql-active .ql-stroke {
|
|
111
|
+
stroke: var(--u-blue-600);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.ql-toolbar button.ql-active .ql-fill {
|
|
115
|
+
fill: var(--u-blue-600);
|
|
116
|
+
}
|
|
117
|
+
`;
|
|
118
|
+
//#endregion
|
|
119
|
+
export { styles };
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { UCodeEditor as UCodeEditorElement } from '../components/code-editor/UCodeEditor';
|
|
3
|
+
|
|
4
|
+
export declare const UCodeEditor: React.ForwardRefExoticComponent<
|
|
5
|
+
Partial<UCodeEditorElement> & React.HTMLAttributes<UCodeEditorElement> & {
|
|
6
|
+
}
|
|
7
|
+
>;
|
|
8
|
+
|
|
9
|
+
export type UCodeEditorProps = React.ComponentProps<typeof UCodeEditor>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createComponent } from '@lit/react';
|
|
3
|
+
import { UCodeEditor } from '../components/code-editor/UCodeEditor.js';
|
|
4
|
+
|
|
5
|
+
export const UCodeEditor = createComponent({
|
|
6
|
+
react: React,
|
|
7
|
+
tagName: 'u-code-editor',
|
|
8
|
+
elementClass: UCodeEditor,
|
|
9
|
+
events: {},
|
|
10
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { UTextEditor as UTextEditorElement } from '../components/text-editor/UTextEditor';
|
|
3
|
+
|
|
4
|
+
export declare const UTextEditor: React.ForwardRefExoticComponent<
|
|
5
|
+
Partial<UTextEditorElement> & React.HTMLAttributes<UTextEditorElement> & {
|
|
6
|
+
}
|
|
7
|
+
>;
|
|
8
|
+
|
|
9
|
+
export type UTextEditorProps = React.ComponentProps<typeof UTextEditor>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createComponent } from '@lit/react';
|
|
3
|
+
import { UTextEditor } from '../components/text-editor/UTextEditor.js';
|
|
4
|
+
|
|
5
|
+
export const UTextEditor = createComponent({
|
|
6
|
+
react: React,
|
|
7
|
+
tagName: 'u-text-editor',
|
|
8
|
+
elementClass: UTextEditor,
|
|
9
|
+
events: {},
|
|
10
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iyulab/editor-components",
|
|
3
|
+
"description": "various editor components by iyulab",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"iyulab",
|
|
7
|
+
"web-components",
|
|
8
|
+
"lit-element"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"author": "iyulab",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/iyulab/node-editor-components.git"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"README.md",
|
|
23
|
+
"CHANGELOG.md",
|
|
24
|
+
"package.json"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./dist/*": {
|
|
34
|
+
"types": "./dist/*.d.ts",
|
|
35
|
+
"import": "./dist/*"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"test": "vite",
|
|
40
|
+
"build": "eslint && vite build"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@iyulab/components": "^1.0.6",
|
|
44
|
+
"lit": "^3.3.3",
|
|
45
|
+
"monaco-editor": "^0.55.1",
|
|
46
|
+
"quill": "^2.0.3"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@lit/react": ">=1.0.8",
|
|
50
|
+
"react": ">=18.0.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"@lit/react": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"react": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@eslint/js": "^9.39.4",
|
|
62
|
+
"@types/node": "^25.8.0",
|
|
63
|
+
"eslint": "^9.39.4",
|
|
64
|
+
"glob": "^13.0.6",
|
|
65
|
+
"globals": "^17.6.0",
|
|
66
|
+
"typescript": "^5.9.3",
|
|
67
|
+
"typescript-eslint": "^8.59.3",
|
|
68
|
+
"vite": "^8.0.13",
|
|
69
|
+
"vite-plugin-dts": "^5.0.0"
|
|
70
|
+
}
|
|
71
|
+
}
|