@iyulab/editor-components 0.0.1 → 0.1.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.
- package/CHANGELOG.md +24 -4
- package/LICENSE +20 -20
- package/README.md +103 -116
- package/dist/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.139.0/helpers/esm}/decorate.js +2 -2
- package/dist/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.139.0/helpers/esm}/decorateMetadata.js +2 -2
- package/dist/components/code-editor/UCodeEditor.d.ts +8 -2
- package/dist/components/code-editor/UCodeEditor.js +9 -3
- package/dist/components/text-editor/UTextEditor.d.ts +24 -7
- package/dist/components/text-editor/UTextEditor.js +34 -14
- package/dist/react/UCodeEditor.d.ts +1 -0
- package/dist/react/UCodeEditor.js +5 -3
- package/dist/react/UTextEditor.d.ts +1 -0
- package/dist/react/UTextEditor.js +5 -3
- package/package.json +9 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## [
|
|
4
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-07-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **`UTextEditor` 가 해제 후 접근에서 깨질 수 있던 타입 거짓말 제거** — `quill` 필드가 `Quill`(non-null 단언)로 선언돼 있는데 `disconnectedCallback` 은 `null as any` 로 null 을 대입하고 있었다. 타입 시스템을 속인 탓에 null 검사 누락 5곳이 가려져 있었다. 필드를 `Quill | null` 로 정직하게 선언하고, `text-change` 핸들러는 생성 시점의 지역 참조를 캡처하도록, `setQuillContents` 는 초기화 전/해제 후 호출을 무시하도록 고쳤다.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **이 패키지의 eslint 가 실제로 동작하기 시작했다.** `files` 패턴의 확장자 누락과 배열 프리셋 객체 스프레드 결함을 수정했다. `build` 스크립트의 `eslint &&` 게이트는 매칭 파일이 0개라 항상 통과하고 있었다. 위 null 결함은 이 복구로 처음 드러났다.
|
|
10
|
+
- `npm run lint` / `npm run lint:fix` 스크립트 추가.
|
|
11
|
+
- **Delta 타입 정밀화(소비자 영향 가능)**: `getDelta()` 가 `any` → `QuillDelta | null`, `setDelta()` 파라미터가 `any` → `QuillDeltaInput` 으로 좁혀졌다. 두 타입은 `quill-delta` 를 새 의존성으로 들이지 않고 Quill 자체 시그니처(`ReturnType<Quill['getContents']>` 등)에서 유도하므로 Quill 버전 변화를 자동으로 따라간다. `getDelta()` 가 `null` 을 반환할 수 있음이 이제 타입에 드러난다.
|
|
12
|
+
- Lit 생명주기 파라미터(`firstUpdated`/`updated`)의 `any` 를 `PropertyValues` 로, Monaco worker 의 `(self as any)` 를 명시적 인터페이스로 교체.
|
|
13
|
+
|
|
14
|
+
## [0.0.2] - 2026-07-17
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `u-code-editor`: 프로그램적 `value` 세팅 시 Monaco `onDidChangeModelContent`가 그대로 `change` 이벤트로 노출되어, controlled 래퍼(React 등)에서 에코 루프·state 오염을 유발하던 문제 수정. 이제 `change`는 사용자 편집에서만 발화한다.
|
|
18
|
+
- `u-text-editor`: 프로그램적 `value` 세팅·초기 콘텐츠 주입이 `quill.root.innerHTML` 직접 변이로 이뤄져 Quill MutationObserver가 이를 사용자 편집(`source='user'`)으로 오인해 `change`가 발화되던 문제 수정. 콘텐츠 주입을 `clipboard.convert()` + `setContents(delta, 'silent')`로 전환하고, `text-change` 리스너는 `source='user'`만 `change`로 노출한다. 사용자 편집 시 `value` 프로퍼티도 동기화된다.
|
|
19
|
+
- `u-text-editor`: 명령형 API도 동일 규약으로 정리 — `setHTML()`이 innerHTML 직접 변이로 change를 위장 발화하던 문제 수정(이제 `value` 프로퍼티 경로 위임), `setDelta()`/`clear()`는 silent source 전환 + `value` 프로퍼티 동기화.
|
|
20
|
+
- README를 실제 구현에 맞게 재작성 — 잘못된 태그명(`<code-editor>`/`<rich-text-editor>` → `u-code-editor`/`u-text-editor`), 잘못된 prop(`noHeader` → `headless`), 존재하지 않는 `copy` 이벤트·`demo.html` 서술 제거, change 의미론·`/react` 서브패스 문서화.
|
|
21
|
+
- 위 수정은 `@iyulab/components` 1.7.0의 "change = 사용자 상호작용 전용" 규약 정합 작업의 일환.
|
|
22
|
+
|
|
23
|
+
## [Unreleased]
|
|
24
|
+
- Initial library version release
|
package/LICENSE
CHANGED
|
@@ -1,21 +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
|
|
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
21
|
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,116 +1,103 @@
|
|
|
1
|
-
# @iyulab/editor-components
|
|
2
|
-
|
|
3
|
-
다양한 에디터 컴포넌트를 제공하는 라이브러리입니다.
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
##
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
- `copy`: 복사 버튼 클릭 시 발생 (detail: string)
|
|
105
|
-
|
|
106
|
-
## 데모
|
|
107
|
-
|
|
108
|
-
프로젝트에 포함된 `demo.html` 파일을 통해 컴포넌트들을 실제로 테스트해볼 수 있습니다.
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
npm run start
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
## 라이선스
|
|
115
|
-
|
|
116
|
-
MIT
|
|
1
|
+
# @iyulab/editor-components
|
|
2
|
+
|
|
3
|
+
다양한 에디터 컴포넌트를 제공하는 라이브러리입니다.
|
|
4
|
+
|
|
5
|
+
## 설치
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @iyulab/editor-components
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
import '@iyulab/editor-components';
|
|
13
|
+
|
|
14
|
+
// 또는 개별 컴포넌트만 import
|
|
15
|
+
import { UCodeEditor, UTextEditor } from '@iyulab/editor-components';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
React 래퍼는 `/react` 서브패스로 제공됩니다:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { UCodeEditor, UTextEditor } from '@iyulab/editor-components/react';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 포함된 컴포넌트
|
|
25
|
+
|
|
26
|
+
### `u-code-editor` (UCodeEditor)
|
|
27
|
+
|
|
28
|
+
Monaco Editor 기반 코드 에디터 컴포넌트입니다.
|
|
29
|
+
|
|
30
|
+
**특징:** 구문 강조 · 자동완성/IntelliSense · 다크/라이트 테마 자동 추적 · 읽기 전용 모드
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<u-code-editor
|
|
34
|
+
label="My Code Editor"
|
|
35
|
+
language="javascript"
|
|
36
|
+
font-size="14"
|
|
37
|
+
value="console.log('Hello World!');">
|
|
38
|
+
</u-code-editor>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Properties:**
|
|
42
|
+
|
|
43
|
+
| Property | Type | Default | 설명 |
|
|
44
|
+
|----------|------|---------|------|
|
|
45
|
+
| `headless` | `boolean` | `false` | 헤더(라벨 영역) 숨김 |
|
|
46
|
+
| `label` | `string` | `"Editor"` | 헤더 라벨 텍스트 |
|
|
47
|
+
| `theme` | `"light" \| "dark"` | 시스템 테마 추적 | 에디터 테마 |
|
|
48
|
+
| `readOnly` | `boolean` | `false` | 읽기 전용 모드 |
|
|
49
|
+
| `language` | `string` | `"json"` | 구문 강조 언어 |
|
|
50
|
+
| `fontSize` | `number` | `14` | 폰트 크기(px) |
|
|
51
|
+
| `value` | `string` | `""` | 에디터 내용 |
|
|
52
|
+
|
|
53
|
+
### `u-text-editor` (UTextEditor)
|
|
54
|
+
|
|
55
|
+
Quill.js 기반 리치 텍스트(WYSIWYG) 에디터 컴포넌트입니다.
|
|
56
|
+
|
|
57
|
+
**특징:** 텍스트 포맷팅 · 목록/헤더/인용구 · 링크/이미지 · 커스텀 툴바 · 다크/라이트 테마 자동 추적
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<u-text-editor
|
|
61
|
+
label="My Rich Text Editor"
|
|
62
|
+
height="300"
|
|
63
|
+
placeholder="Start writing..."
|
|
64
|
+
value="<h2>Hello World!</h2><p>This is <strong>rich text</strong>.</p>">
|
|
65
|
+
</u-text-editor>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Properties:**
|
|
69
|
+
|
|
70
|
+
| Property | Type | Default | 설명 |
|
|
71
|
+
|----------|------|---------|------|
|
|
72
|
+
| `headless` | `boolean` | `false` | 헤더(라벨 영역) 숨김 |
|
|
73
|
+
| `label` | `string` | `"Rich Text Editor"` | 헤더 라벨 텍스트 |
|
|
74
|
+
| `theme` | `"light" \| "dark" \| "system"` | 시스템 테마 추적 | 에디터 테마 |
|
|
75
|
+
| `readOnly` | `boolean` | `false` | 읽기 전용 모드 |
|
|
76
|
+
| `placeholder` | `string` | `"Start writing..."` | 플레이스홀더 텍스트 |
|
|
77
|
+
| `value` | `string` | `""` | 에디터 HTML 내용 |
|
|
78
|
+
| `height` | `number` | `300` | 에디터 높이(px) |
|
|
79
|
+
| `toolbar` | `string[][]` | 기본 툴바 | 커스텀 툴바 설정 |
|
|
80
|
+
|
|
81
|
+
**Methods:**
|
|
82
|
+
|
|
83
|
+
- `getHTML()` / `getText()` / `getDelta()`: HTML / 플레인 텍스트 / Quill Delta 형식으로 내용 반환
|
|
84
|
+
- `setHTML(html)` / `setDelta(delta)`: 내용 설정 (프로그램적 — `change` 미발화)
|
|
85
|
+
- `clear()`: 모든 내용 삭제 (프로그램적 — `change` 미발화)
|
|
86
|
+
- `focus()`: 에디터에 포커스
|
|
87
|
+
|
|
88
|
+
## 이벤트
|
|
89
|
+
|
|
90
|
+
`change`는 네이티브 폼 컨트롤 규약을 따라 **사용자 편집에서만** 발화합니다. 프로그램적 `value` 세팅·`setHTML()`/`setDelta()`/`clear()`는 발화하지 않으므로, React 등 controlled 래퍼에서 에코 루프 없이 안전하게 바인딩할 수 있습니다.
|
|
91
|
+
|
|
92
|
+
### `u-code-editor`
|
|
93
|
+
- `change`: 사용자 편집으로 내용이 변경될 때 발생. 현재 값은 `event.target.value`로 읽습니다.
|
|
94
|
+
|
|
95
|
+
### `u-text-editor`
|
|
96
|
+
- `change`: 사용자 편집으로 내용이 변경될 때 발생 (`detail: { html, text, delta }`)
|
|
97
|
+
|
|
98
|
+
## 개발
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm test # Vite 개발 서버 (컴포넌트 프리뷰)
|
|
102
|
+
npm run build # 프로덕션 빌드 (eslint + vite)
|
|
103
|
+
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorate.js
|
|
2
2
|
function __decorate(decorators, target, key, desc) {
|
|
3
3
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
4
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -6,4 +6,4 @@ function __decorate(decorators, target, key, desc) {
|
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
}
|
|
8
8
|
//#endregion
|
|
9
|
-
export { __decorate };
|
|
9
|
+
export { __decorate as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorateMetadata.js
|
|
2
2
|
function __decorateMetadata(k, v) {
|
|
3
3
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4
4
|
}
|
|
5
5
|
//#endregion
|
|
6
|
-
export { __decorateMetadata };
|
|
6
|
+
export { __decorateMetadata as default };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { PropertyValues } from 'lit';
|
|
1
2
|
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
2
3
|
/**
|
|
3
4
|
* code editor component used by monaco-editor
|
|
5
|
+
*
|
|
6
|
+
* @event change - Fired when the content of the editor changes. The event detail contains the current HTML, text, and delta representation of the content.
|
|
4
7
|
*/
|
|
5
8
|
export declare class UCodeEditor extends UElement {
|
|
6
9
|
static styles: import('lit').CSSResultGroup[];
|
|
@@ -20,11 +23,14 @@ export declare class UCodeEditor extends UElement {
|
|
|
20
23
|
value: string;
|
|
21
24
|
private container;
|
|
22
25
|
private editor?;
|
|
26
|
+
/** 프로그램적 value 동기화 중 여부 — Monaco onDidChangeModelContent는 setValue에서도
|
|
27
|
+
* 발화하므로, 이 플래그로 프로그램적 변경이 change 이벤트로 위장되는 것을 막는다. */
|
|
28
|
+
private syncingValue;
|
|
23
29
|
private observer;
|
|
24
30
|
connectedCallback(): void;
|
|
25
31
|
disconnectedCallback(): void;
|
|
26
|
-
protected firstUpdated(changedProperties:
|
|
27
|
-
protected updated(changedProperties:
|
|
32
|
+
protected firstUpdated(changedProperties: PropertyValues): Promise<void>;
|
|
33
|
+
protected updated(changedProperties: PropertyValues): Promise<void>;
|
|
28
34
|
render(): import('lit-html').TemplateResult<1>;
|
|
29
35
|
}
|
|
30
36
|
declare global {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./UCodeEditor.worker.js";
|
|
2
2
|
import { styles } from "./UCodeEditor.styles.js";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/decorateMetadata.js";
|
|
4
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/decorate.js";
|
|
5
5
|
import { html, unsafeCSS } from "lit";
|
|
6
6
|
import { customElement, property } from "lit/decorators.js";
|
|
7
7
|
import { createRef, ref } from "lit/directives/ref.js";
|
|
@@ -21,6 +21,7 @@ var UCodeEditor = class UCodeEditor extends UElement {
|
|
|
21
21
|
this.fontSize = 14;
|
|
22
22
|
this.value = "";
|
|
23
23
|
this.container = createRef();
|
|
24
|
+
this.syncingValue = false;
|
|
24
25
|
this.observer = new MutationObserver(() => {
|
|
25
26
|
const theme = Theme.get() === "dark" ? "dark" : "light";
|
|
26
27
|
if (this.theme !== theme) this.theme = theme;
|
|
@@ -62,6 +63,7 @@ var UCodeEditor = class UCodeEditor extends UElement {
|
|
|
62
63
|
scrollbar: { alwaysConsumeMouseWheel: false }
|
|
63
64
|
});
|
|
64
65
|
this.editor.onDidChangeModelContent(() => {
|
|
66
|
+
if (this.syncingValue) return;
|
|
65
67
|
this.value = this.editor.getValue();
|
|
66
68
|
this.dispatchEvent(new Event("change", {
|
|
67
69
|
bubbles: true,
|
|
@@ -71,7 +73,11 @@ var UCodeEditor = class UCodeEditor extends UElement {
|
|
|
71
73
|
}
|
|
72
74
|
async updated(changedProperties) {
|
|
73
75
|
super.updated(changedProperties);
|
|
74
|
-
if (changedProperties.has("value") && this.value !== this.editor?.getValue() && !this.editor?.hasWidgetFocus())
|
|
76
|
+
if (changedProperties.has("value") && this.value !== this.editor?.getValue() && !this.editor?.hasWidgetFocus()) {
|
|
77
|
+
this.syncingValue = true;
|
|
78
|
+
this.editor?.setValue(this.value);
|
|
79
|
+
this.syncingValue = false;
|
|
80
|
+
}
|
|
75
81
|
if (changedProperties.has("theme") && this.editor) this.editor.updateOptions({ theme: this.theme === "light" ? "vs-light" : "vs-dark" });
|
|
76
82
|
if (changedProperties.has("language") && this.editor) monaco.editor.setModelLanguage(this.editor.getModel(), this.language);
|
|
77
83
|
}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
import { PropertyValues } from 'lit';
|
|
2
|
+
import { default as Quill } from 'quill';
|
|
1
3
|
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
4
|
+
/** Quill 의 Delta 타입. quill-delta 를 직접 의존하지 않고 Quill 시그니처에서 유도한다. */
|
|
5
|
+
type QuillDelta = ReturnType<Quill["getContents"]>;
|
|
6
|
+
type QuillDeltaInput = Parameters<Quill["setContents"]>[0];
|
|
7
|
+
/**
|
|
8
|
+
* A rich text editor component built using Quill.js, providing a customizable and user-friendly interface for text editing. It supports various formatting options, themes, and can be configured to be read-only or editable.
|
|
9
|
+
*
|
|
10
|
+
* @event change - Fired when the content of the editor changes. The event detail includes the current HTML, plain text, and Quill Delta representation of the content.
|
|
11
|
+
*/
|
|
2
12
|
export declare class UTextEditor extends UElement {
|
|
3
13
|
static styles: import('lit').CSSResultGroup[];
|
|
4
14
|
/** Specifies whether the header should be displayed or not. @default false */
|
|
@@ -22,8 +32,11 @@ export declare class UTextEditor extends UElement {
|
|
|
22
32
|
private observer;
|
|
23
33
|
connectedCallback(): void;
|
|
24
34
|
disconnectedCallback(): void;
|
|
25
|
-
protected firstUpdated(changedProperties:
|
|
26
|
-
protected updated(changedProperties:
|
|
35
|
+
protected firstUpdated(changedProperties: PropertyValues): Promise<void>;
|
|
36
|
+
protected updated(changedProperties: PropertyValues): Promise<void>;
|
|
37
|
+
/** HTML을 Quill Delta로 변환해 silent source로 주입한다 — 프로그램적 콘텐츠 세팅이
|
|
38
|
+
* text-change(→ change 이벤트)로 위장되지 않도록 하기 위함. */
|
|
39
|
+
private setQuillContents;
|
|
27
40
|
render(): import('lit-html').TemplateResult<1>;
|
|
28
41
|
private updateEditorHeight;
|
|
29
42
|
/**
|
|
@@ -37,17 +50,20 @@ export declare class UTextEditor extends UElement {
|
|
|
37
50
|
/**
|
|
38
51
|
* Get the current content as Quill Delta
|
|
39
52
|
*/
|
|
40
|
-
getDelta():
|
|
53
|
+
getDelta(): QuillDelta | null;
|
|
41
54
|
/**
|
|
42
|
-
* Set content from HTML
|
|
55
|
+
* Set content from HTML.
|
|
56
|
+
* 프로그램적 세팅이므로 change 이벤트는 발화하지 않는다 (value 프로퍼티 경로와 동일).
|
|
43
57
|
*/
|
|
44
58
|
setHTML(html: string): void;
|
|
45
59
|
/**
|
|
46
|
-
* Set content from Quill Delta
|
|
60
|
+
* Set content from Quill Delta.
|
|
61
|
+
* 프로그램적 세팅이므로 change 이벤트는 발화하지 않는다.
|
|
47
62
|
*/
|
|
48
|
-
setDelta(delta:
|
|
63
|
+
setDelta(delta: QuillDeltaInput): void;
|
|
49
64
|
/**
|
|
50
|
-
* Clear all content
|
|
65
|
+
* Clear all content.
|
|
66
|
+
* 프로그램적 세팅이므로 change 이벤트는 발화하지 않는다.
|
|
51
67
|
*/
|
|
52
68
|
clear(): void;
|
|
53
69
|
/**
|
|
@@ -60,3 +76,4 @@ declare global {
|
|
|
60
76
|
"u-text-editor": UTextEditor;
|
|
61
77
|
}
|
|
62
78
|
}
|
|
79
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/decorate.js";
|
|
3
3
|
import { styles } from "./UTextEditor.styles.js";
|
|
4
4
|
import { html, unsafeCSS } from "lit";
|
|
5
5
|
import { customElement, property } from "lit/decorators.js";
|
|
@@ -20,6 +20,7 @@ var UTextEditor = class UTextEditor extends UElement {
|
|
|
20
20
|
this.value = "";
|
|
21
21
|
this.height = 300;
|
|
22
22
|
this.container = createRef();
|
|
23
|
+
this.quill = null;
|
|
23
24
|
this.observer = new MutationObserver(() => {
|
|
24
25
|
const theme = Theme.get();
|
|
25
26
|
if (this.theme !== theme) this.theme = theme;
|
|
@@ -88,13 +89,16 @@ var UTextEditor = class UTextEditor extends UElement {
|
|
|
88
89
|
placeholder: this.placeholder,
|
|
89
90
|
modules: { toolbar: this.toolbar || defaultToolbar }
|
|
90
91
|
});
|
|
91
|
-
if (this.value) this.
|
|
92
|
-
this.quill
|
|
93
|
-
|
|
92
|
+
if (this.value) this.setQuillContents(this.value);
|
|
93
|
+
const quill = this.quill;
|
|
94
|
+
quill.on("text-change", (_delta, _oldDelta, source) => {
|
|
95
|
+
if (source !== "user") return;
|
|
96
|
+
const html = quill.root.innerHTML;
|
|
97
|
+
this.value = html;
|
|
94
98
|
this.dispatchEvent(new CustomEvent("change", { detail: {
|
|
95
99
|
html,
|
|
96
|
-
text:
|
|
97
|
-
delta:
|
|
100
|
+
text: quill.getText(),
|
|
101
|
+
delta: quill.getContents()
|
|
98
102
|
} }));
|
|
99
103
|
});
|
|
100
104
|
this.updateEditorHeight();
|
|
@@ -103,12 +107,19 @@ var UTextEditor = class UTextEditor extends UElement {
|
|
|
103
107
|
super.updated(changedProperties);
|
|
104
108
|
await this.updateComplete;
|
|
105
109
|
if (this.quill) {
|
|
106
|
-
if (changedProperties.has("value") && this.value !== this.quill.root.innerHTML) this.
|
|
110
|
+
if (changedProperties.has("value") && this.value !== this.quill.root.innerHTML) this.setQuillContents(this.value);
|
|
107
111
|
if (changedProperties.has("readOnly")) this.quill.enable(!this.readOnly);
|
|
108
112
|
if (changedProperties.has("placeholder")) this.quill.root.dataset.placeholder = this.placeholder;
|
|
109
113
|
if (changedProperties.has("height")) this.updateEditorHeight();
|
|
110
114
|
}
|
|
111
115
|
}
|
|
116
|
+
/** HTML을 Quill Delta로 변환해 silent source로 주입한다 — 프로그램적 콘텐츠 세팅이
|
|
117
|
+
* text-change(→ change 이벤트)로 위장되지 않도록 하기 위함. */
|
|
118
|
+
setQuillContents(htmlValue) {
|
|
119
|
+
if (!this.quill) return;
|
|
120
|
+
const delta = this.quill.clipboard.convert({ html: htmlValue });
|
|
121
|
+
this.quill.setContents(delta, Quill.sources.SILENT);
|
|
122
|
+
}
|
|
112
123
|
render() {
|
|
113
124
|
return html`
|
|
114
125
|
<div class="header" ?hidden=${this.headless}>
|
|
@@ -146,22 +157,31 @@ var UTextEditor = class UTextEditor extends UElement {
|
|
|
146
157
|
return this.quill ? this.quill.getContents() : null;
|
|
147
158
|
}
|
|
148
159
|
/**
|
|
149
|
-
* Set content from HTML
|
|
160
|
+
* Set content from HTML.
|
|
161
|
+
* 프로그램적 세팅이므로 change 이벤트는 발화하지 않는다 (value 프로퍼티 경로와 동일).
|
|
150
162
|
*/
|
|
151
163
|
setHTML(html) {
|
|
152
|
-
|
|
164
|
+
this.value = html;
|
|
153
165
|
}
|
|
154
166
|
/**
|
|
155
|
-
* Set content from Quill Delta
|
|
167
|
+
* Set content from Quill Delta.
|
|
168
|
+
* 프로그램적 세팅이므로 change 이벤트는 발화하지 않는다.
|
|
156
169
|
*/
|
|
157
170
|
setDelta(delta) {
|
|
158
|
-
if (this.quill)
|
|
171
|
+
if (this.quill) {
|
|
172
|
+
this.quill.setContents(delta, Quill.sources.SILENT);
|
|
173
|
+
this.value = this.quill.root.innerHTML;
|
|
174
|
+
}
|
|
159
175
|
}
|
|
160
176
|
/**
|
|
161
|
-
* Clear all content
|
|
177
|
+
* Clear all content.
|
|
178
|
+
* 프로그램적 세팅이므로 change 이벤트는 발화하지 않는다.
|
|
162
179
|
*/
|
|
163
180
|
clear() {
|
|
164
|
-
if (this.quill)
|
|
181
|
+
if (this.quill) {
|
|
182
|
+
this.quill.setText("", Quill.sources.SILENT);
|
|
183
|
+
this.value = this.quill.root.innerHTML;
|
|
184
|
+
}
|
|
165
185
|
}
|
|
166
186
|
/**
|
|
167
187
|
* Focus the editor
|
|
@@ -3,6 +3,7 @@ import { UCodeEditor as UCodeEditorElement } from '../components/code-editor/UCo
|
|
|
3
3
|
|
|
4
4
|
export declare const UCodeEditor: React.ForwardRefExoticComponent<
|
|
5
5
|
Partial<UCodeEditorElement> & React.HTMLAttributes<UCodeEditorElement> & {
|
|
6
|
+
onChange?: (event: CustomEvent) => void;
|
|
6
7
|
}
|
|
7
8
|
>;
|
|
8
9
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UCodeEditor } from '../components/code-editor/UCodeEditor.js';
|
|
3
|
+
import { UCodeEditor as UCodeEditorElement } from '../components/code-editor/UCodeEditor.js';
|
|
4
4
|
|
|
5
5
|
export const UCodeEditor = createComponent({
|
|
6
6
|
react: React,
|
|
7
7
|
tagName: 'u-code-editor',
|
|
8
|
-
elementClass:
|
|
9
|
-
events: {
|
|
8
|
+
elementClass: UCodeEditorElement,
|
|
9
|
+
events: {
|
|
10
|
+
onChange: 'change',
|
|
11
|
+
},
|
|
10
12
|
});
|
|
@@ -3,6 +3,7 @@ import { UTextEditor as UTextEditorElement } from '../components/text-editor/UTe
|
|
|
3
3
|
|
|
4
4
|
export declare const UTextEditor: React.ForwardRefExoticComponent<
|
|
5
5
|
Partial<UTextEditorElement> & React.HTMLAttributes<UTextEditorElement> & {
|
|
6
|
+
onChange?: (event: CustomEvent) => void;
|
|
6
7
|
}
|
|
7
8
|
>;
|
|
8
9
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UTextEditor } from '../components/text-editor/UTextEditor.js';
|
|
3
|
+
import { UTextEditor as UTextEditorElement } from '../components/text-editor/UTextEditor.js';
|
|
4
4
|
|
|
5
5
|
export const UTextEditor = createComponent({
|
|
6
6
|
react: React,
|
|
7
7
|
tagName: 'u-text-editor',
|
|
8
|
-
elementClass:
|
|
9
|
-
events: {
|
|
8
|
+
elementClass: UTextEditorElement,
|
|
9
|
+
events: {
|
|
10
|
+
onChange: 'change',
|
|
11
|
+
},
|
|
10
12
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/editor-components",
|
|
3
3
|
"description": "various editor components by iyulab",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iyulab",
|
|
7
7
|
"web-components",
|
|
@@ -37,10 +37,12 @@
|
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"test": "vite",
|
|
40
|
+
"lint": "eslint src/",
|
|
41
|
+
"lint:fix": "eslint src/ --fix",
|
|
40
42
|
"build": "eslint && vite build"
|
|
41
43
|
},
|
|
42
44
|
"dependencies": {
|
|
43
|
-
"@iyulab/components": "^1.
|
|
45
|
+
"@iyulab/components": "^1.5.1",
|
|
44
46
|
"lit": "^3.3.3",
|
|
45
47
|
"monaco-editor": "^0.55.1",
|
|
46
48
|
"quill": "^2.0.3"
|
|
@@ -59,13 +61,13 @@
|
|
|
59
61
|
},
|
|
60
62
|
"devDependencies": {
|
|
61
63
|
"@eslint/js": "^9.39.4",
|
|
62
|
-
"@types/node": "^
|
|
64
|
+
"@types/node": "^26.1.1",
|
|
63
65
|
"eslint": "^9.39.4",
|
|
64
66
|
"glob": "^13.0.6",
|
|
65
|
-
"globals": "^17.
|
|
67
|
+
"globals": "^17.7.0",
|
|
66
68
|
"typescript": "^5.9.3",
|
|
67
|
-
"typescript-eslint": "^8.
|
|
68
|
-
"vite": "^8.
|
|
69
|
-
"vite-plugin-dts": "^5.0.
|
|
69
|
+
"typescript-eslint": "^8.64.0",
|
|
70
|
+
"vite": "^8.1.4",
|
|
71
|
+
"vite-plugin-dts": "^5.0.3"
|
|
70
72
|
}
|
|
71
73
|
}
|