@iyulab/editor-components 0.3.2 → 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.
- package/CHANGELOG.md +91 -0
- package/README.md +16 -3
- package/dist/_virtual/{_@oxc-project_runtime@0.144.0 → _@oxc-project_runtime@0.149.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.144.0 → _@oxc-project_runtime@0.149.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/components/code-editor/UCodeEditor.js +2 -2
- package/dist/components/code-editor/UCodeEditor.styles.js +11 -7
- package/dist/components/text-editor/UTextEditor.d.ts +10 -2
- package/dist/components/text-editor/UTextEditor.js +2 -10
- package/dist/components/text-editor/UTextEditor.styles.js +55 -3
- package/dist/react/UCodeEditor.d.ts +1 -1
- package/dist/react/UCodeEditor.js +1 -1
- package/dist/react/UTextEditor.d.ts +1 -1
- package/dist/react/UTextEditor.js +1 -1
- package/package.json +12 -7
- package/skills/iyulab-editor-components/SKILL.md +47 -0
- package/skills/iyulab-editor-components/references/components/code-editor.md +70 -0
- package/skills/iyulab-editor-components/references/components/text-editor.md +86 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,96 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0] - 2026-09-10
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **The rich text editor's colour swatches and link tooltip actions are now large enough to hit**
|
|
8
|
+
(WCAG 2.2 SC 2.5.8, 24×24 CSS px). Quill draws them at 16×16 (swatches) and 19px tall (*Edit* and
|
|
9
|
+
*Remove* in the link tooltip); the component now overrides both — swatches are 24×24, which makes
|
|
10
|
+
the colour palette wider (206px), and the tooltip actions are laid out as blocks so their 26px
|
|
11
|
+
line height is their real height. What you see is the editor this package ships, so its targets
|
|
12
|
+
follow this package's rules even where a dependency drew them.
|
|
13
|
+
|
|
14
|
+
- **Fallback literals for `--u-txt-color-weak` follow `@iyulab/components` 1.40.0.** They are
|
|
15
|
+
only used when that stylesheet is not loaded; with it, nothing changes here.
|
|
16
|
+
|
|
17
|
+
- **`monaco-editor` moved from `dependencies` to `peerDependencies`, narrowed to `^0.55.1`.**
|
|
18
|
+
Install it alongside this package:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
npm install monaco-editor@^0.55.1
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Two reasons, and the second one was a live break.
|
|
25
|
+
|
|
26
|
+
**The declared range crossed a breaking change in monaco's own `exports` map.** Up to 0.55.1
|
|
27
|
+
that map was the identity `"./*": "./*"`, so a subpath resolved to the file it names. 0.56.0
|
|
28
|
+
rewrites every subpath to `./esm/vs/<name>.js`, which sends
|
|
29
|
+
`monaco-editor/min/vs/editor/editor.main.css?inline` to a `.js` file that does not exist and
|
|
30
|
+
leaves no reachable path to the stylesheet at all — the `esm/` tree contains no `.css`. This
|
|
31
|
+
package needs the stylesheet as *text*, because the editor renders into a shadow root. The old
|
|
32
|
+
range `^0.55.1 || ^0.56.0` admitted both, and a fresh install picks the highest match, so any
|
|
33
|
+
consumer installing today got 0.56.0 and a build that could not resolve the import. Nothing in
|
|
34
|
+
this package's own build caught it: monaco is external, so the imports are passed through to
|
|
35
|
+
the consumer's bundler untouched.
|
|
36
|
+
|
|
37
|
+
**An editor engine has to be a single copy.** Monaco owns global state — its language and
|
|
38
|
+
theme registries — so two copies in one application do not compose. Declaring it as a peer
|
|
39
|
+
lets the consumer own the version, and makes a conflicting one an install-time error instead
|
|
40
|
+
of a silent second copy. `@iyulab/components` in this package, and `@iyulab/flex-table` and
|
|
41
|
+
`@iyulab/enterprise` in the same repo, already follow that convention.
|
|
42
|
+
|
|
43
|
+
Support for 0.56 and later is not abandoned — it needs a different way to obtain the
|
|
44
|
+
stylesheet text, which is tracked separately.
|
|
45
|
+
|
|
46
|
+
- **`@iyulab/components` moved from `dependencies` to `peerDependencies` (`>=1.15.0`).** Same
|
|
47
|
+
reasoning as above and the same convention as the sibling packages: the component library
|
|
48
|
+
registers custom elements and owns module-level singletons (theme, toasts, overlay
|
|
49
|
+
stacking), so a second copy in one application does not compose — the copy that wins the
|
|
50
|
+
element registry is undefined, and the singletons split in two. Consumers already installing
|
|
51
|
+
`@iyulab/components` are unaffected; the declaration now says out loud that this package uses
|
|
52
|
+
the one they installed rather than fetching its own.
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- **The text editor's editing area no longer slides out of the component when the toolbar
|
|
57
|
+
wraps.** The editing area was sized as `height` minus a fixed 42px for the toolbar, but the
|
|
58
|
+
default toolbar wraps onto two or more rows in narrower editors (88px at 480px wide), which
|
|
59
|
+
pushed the bottom of the editing area — and its scrollbar — outside the component, where it
|
|
60
|
+
was cut off. The toolbar and the editing area now share `height` by layout: the editing area
|
|
61
|
+
is whatever the toolbar leaves, at any width.
|
|
62
|
+
|
|
63
|
+
- **The text editor's dropdowns and link tooltip are no longer cut off at the component's
|
|
64
|
+
edge.** The component clipped everything outside its box, so in a short editor the link
|
|
65
|
+
tooltip (open, Edit, Remove) and the lower rows of the color pickers could be neither seen
|
|
66
|
+
nor clicked. They now extend past the component like any popup. The rounded corners are kept
|
|
67
|
+
by the header — or the toolbar when `headless` — rounding its own top corners.
|
|
68
|
+
|
|
69
|
+
- **The code editor now uses its full height.** The editing area was sized as the component's
|
|
70
|
+
height minus a fixed 32px for the header, but the header is 24px tall, so 8px along the bottom
|
|
71
|
+
was never used. The header and the editing area now divide the height by layout, so the editing
|
|
72
|
+
area is whatever the header leaves — at any header size. The component's overall size is
|
|
73
|
+
unchanged; only the editor inside it grows.
|
|
74
|
+
|
|
75
|
+
## [0.3.3] - 2026-08-25
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
|
|
79
|
+
- Agent-skill reference docs for both components (`skills/iyulab-editor-components/`) —
|
|
80
|
+
properties, slots, events, and methods for `u-code-editor`/`u-text-editor`.
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
|
|
84
|
+
- 🔴**`monaco-editor` bundles a vulnerable `dompurify` (`3.4.8`) with no pin protecting it.**
|
|
85
|
+
Four moderate/low DOMPurify advisories (GHSA-c2j3-45gr-mqc4, GHSA-cmwh-pvxp-8882,
|
|
86
|
+
GHSA-vxr8-fq34-vvx9, GHSA-55q2-fjhq-7xh7) affect `<=3.4.12`; a fresh install of this package
|
|
87
|
+
resolved the vulnerable version with nothing to stop it. Added `overrides: { dompurify:
|
|
88
|
+
"^3.4.13" }` — `dompurify` is a transitive dependency here (bundled via `monaco-editor`), not
|
|
89
|
+
a direct one, so this pins cleanly without touching `dependencies`. Verified with a clean
|
|
90
|
+
install: `npm audit` no longer reports any dompurify advisory, typecheck/lint/test/build all
|
|
91
|
+
pass. (`quill`'s own advisory, GHSA-v3m3-f69x-jf25, has no upstream fix yet and is unrelated
|
|
92
|
+
to this pin — still open, tracked separately.)
|
|
93
|
+
|
|
3
94
|
## [0.3.2] - 2026-08-18
|
|
4
95
|
|
|
5
96
|
### Changed
|
package/README.md
CHANGED
|
@@ -50,6 +50,12 @@ Monaco Editor 기반 코드 에디터 컴포넌트입니다.
|
|
|
50
50
|
| `fontSize` | `number` | `14` | 폰트 크기(px) |
|
|
51
51
|
| `value` | `string` | `""` | 에디터 내용 |
|
|
52
52
|
|
|
53
|
+
**크기:** **호스트 상자가 크기를 정합니다** — `:host` 가 `width: 100%; height: 100%` 이고 편집 영역은 머리글을 뺀 나머지입니다(Monaco 가 `automaticLayout` 으로 따라옵니다). `height` 프로퍼티는 없으니 요소나 부모에 CSS 로 높이를 주세요.
|
|
54
|
+
|
|
55
|
+
⚠**부모에 자기 높이가 없으면 `height: 100%` 가 무효가 되어 편집 영역이 몇 px 로 붕괴합니다** — 오류도 콘솔 경고도 없이 머리글과 빈 띠만 남습니다. 부모에 높이를 주거나 요소에 직접 주세요. `headless` 면 머리글이 없어 편집 영역이 호스트 상자 전체입니다.
|
|
56
|
+
|
|
57
|
+
⚠`u-text-editor` 는 반대입니다 — 거기서는 `height` 프로퍼티가 편집 영역을 정하고 호스트 CSS `height` 는 그것을 바꾸지 않습니다. 한쪽에서 되던 레이아웃이 다른 쪽에 그대로 옮겨지지 않습니다.
|
|
58
|
+
|
|
53
59
|
### `u-text-editor` (UTextEditor)
|
|
54
60
|
|
|
55
61
|
Quill.js 기반 리치 텍스트(WYSIWYG) 에디터 컴포넌트입니다.
|
|
@@ -74,9 +80,15 @@ Quill.js 기반 리치 텍스트(WYSIWYG) 에디터 컴포넌트입니다.
|
|
|
74
80
|
| `readOnly` | `boolean` | `false` | 읽기 전용 모드 |
|
|
75
81
|
| `placeholder` | `string` | `"Start writing..."` | 플레이스홀더 텍스트 |
|
|
76
82
|
| `value` | `string` | `""` | 에디터 HTML 내용 |
|
|
77
|
-
| `height` | `number` | `300` |
|
|
83
|
+
| `height` | `number` | `300` | **편집 영역** 높이(px) — 아래 「크기」 참조 |
|
|
78
84
|
| `toolbar` | `string[][]` | 기본 툴바 | 커스텀 툴바 설정 |
|
|
79
85
|
|
|
86
|
+
**크기:** `height` 는 **편집 영역**의 높이(px)이고, 호스트 상자는 거기에 헤더가 더해진 크기입니다(`height="300"` → 약 349px = 헤더 48 + 300 + 테두리 · `headless` 면 헤더 없음).
|
|
87
|
+
|
|
88
|
+
🔴**호스트에 CSS `height` 를 줘도 편집 영역은 바뀌지 않습니다.** 호스트 상자만 바뀌므로, 작게 주면 편집 영역이 상자 밖으로 넘치고(일부러 자르지 않습니다 — Quill 의 떠 있는 UI 인 선택기 목록·링크 툴팁이 상자를 벗어날 수 있어야 합니다) 크게 주거나 `height: 100%` 로 주면 아래에 빈 공간이 남습니다. 부모 높이를 채우려면 CSS 가 아니라 `height` 프로퍼티로 넘기세요.
|
|
89
|
+
|
|
90
|
+
⚠`u-code-editor` 는 반대입니다 — `height` 프로퍼티가 없고 호스트 상자를 채웁니다(`height: 100%`). 한쪽에서 되던 레이아웃이 다른 쪽에 그대로 옮겨지지 않습니다.
|
|
91
|
+
|
|
80
92
|
**Methods:**
|
|
81
93
|
|
|
82
94
|
- `getHTML()` / `getText()` / `getDelta()`: HTML / 플레인 텍스트 / Quill Delta 형식으로 내용 반환
|
|
@@ -97,6 +109,7 @@ Quill.js 기반 리치 텍스트(WYSIWYG) 에디터 컴포넌트입니다.
|
|
|
97
109
|
## 개발
|
|
98
110
|
|
|
99
111
|
```bash
|
|
100
|
-
npm
|
|
101
|
-
npm
|
|
112
|
+
npm run preview # Vite 개발 서버 (컴포넌트 프리뷰)
|
|
113
|
+
npm test # 테스트 (vitest — 브라우저 프로젝트, Chromium)
|
|
114
|
+
npm run build # 프로덕션 빌드 (typecheck + eslint + vite)
|
|
102
115
|
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.149.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);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.149.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
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./UCodeEditor.worker.js";
|
|
2
2
|
import { styles } from "./UCodeEditor.styles.js";
|
|
3
|
-
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.
|
|
4
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
3
|
+
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
|
|
4
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.149.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";
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { css } from "lit";
|
|
2
2
|
//#region src/components/code-editor/UCodeEditor.styles.ts
|
|
3
3
|
var styles = css`
|
|
4
|
+
/* 크기는 «호스트 상자» 가 정한다 — monaco 는 automaticLayout 으로 컨테이너를 따라온다.
|
|
5
|
+
⚠부모가 auto 높이면 height:100% 가 무효라 편집 영역이 0 이 된다(계약으로 문서에 적었다).
|
|
6
|
+
머리글 높이를 숫자로 가정하지 않는다 — 세로 flex 가 «머리글을 뺀 나머지» 를 편집 영역에 준다.
|
|
7
|
+
종전에는 --header-height: 32px 를 빼는 계산이었는데 실제 머리글은 24px 라 8px 을 못 쓰고 있었다
|
|
8
|
+
(형제 u-text-editor 에서 같은 부류의 «툴바 높이를 숫자로 가정» 을 걷어낸 것과 같은 이유). */
|
|
4
9
|
:host {
|
|
5
10
|
position: relative;
|
|
6
|
-
display:
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
7
13
|
width: 100%;
|
|
8
14
|
height: 100%;
|
|
9
15
|
overflow: hidden;
|
|
10
|
-
--header-height: 32px;
|
|
11
|
-
}
|
|
12
|
-
:host([headless]) {
|
|
13
|
-
--header-height: 0px;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
.header {
|
|
17
19
|
position: relative;
|
|
20
|
+
flex: none;
|
|
18
21
|
display: flex;
|
|
19
22
|
flex-direction: row;
|
|
20
23
|
align-items: center;
|
|
@@ -38,9 +41,10 @@ var styles = css`
|
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
.editor {
|
|
41
|
-
position:
|
|
44
|
+
position: relative;
|
|
45
|
+
flex: 1 1 auto;
|
|
46
|
+
min-height: 0;
|
|
42
47
|
width: 100%;
|
|
43
|
-
height: calc(100% - var(--header-height));
|
|
44
48
|
overflow: hidden;
|
|
45
49
|
|
|
46
50
|
main {
|
|
@@ -21,7 +21,16 @@ export declare class UTextEditor extends UElement {
|
|
|
21
21
|
placeholder: string;
|
|
22
22
|
/** The current HTML content of the rich text editor. @default "" */
|
|
23
23
|
value: string;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* 편집 영역의 높이(px). 호스트 상자는 여기에 머리글이 더해진 크기다(`headless` 면 머리글 없음).
|
|
26
|
+
*
|
|
27
|
+
* 🔴**호스트에 CSS `height` 를 줘도 편집 영역은 바뀌지 않는다** — 호스트 상자만 바뀌어서, 작으면
|
|
28
|
+
* 편집 영역이 상자 밖으로 넘치고(자르지 않는다 — 시트 머리의 `:host` 주석) 크면 아래가 빈다.
|
|
29
|
+
* 부모를 채우려면 CSS 가 아니라 이 프로퍼티로 넘긴다. 계약은
|
|
30
|
+
* `tests/browser/text-editor-layout.browser.test.ts` 가 고정한다.
|
|
31
|
+
*
|
|
32
|
+
* @default 300
|
|
33
|
+
*/
|
|
25
34
|
height: number;
|
|
26
35
|
/** Custom toolbar configuration. If not provided, uses default toolbar. */
|
|
27
36
|
toolbar?: string[][];
|
|
@@ -34,7 +43,6 @@ export declare class UTextEditor extends UElement {
|
|
|
34
43
|
* text-change(→ change 이벤트)로 위장되지 않도록 하기 위함. */
|
|
35
44
|
private setQuillContents;
|
|
36
45
|
render(): import('lit-html').TemplateResult<1>;
|
|
37
|
-
private updateEditorHeight;
|
|
38
46
|
/**
|
|
39
47
|
* Get the current content as HTML
|
|
40
48
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.149.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";
|
|
@@ -90,7 +90,6 @@ var UTextEditor = class UTextEditor extends UElement {
|
|
|
90
90
|
composed: true
|
|
91
91
|
}));
|
|
92
92
|
});
|
|
93
|
-
this.updateEditorHeight();
|
|
94
93
|
}
|
|
95
94
|
async updated(changedProperties) {
|
|
96
95
|
super.updated(changedProperties);
|
|
@@ -99,7 +98,6 @@ var UTextEditor = class UTextEditor extends UElement {
|
|
|
99
98
|
if (changedProperties.has("value") && this.value !== this.quill.root.innerHTML) this.setQuillContents(this.value);
|
|
100
99
|
if (changedProperties.has("readOnly")) this.quill.enable(!this.readOnly);
|
|
101
100
|
if (changedProperties.has("placeholder")) this.quill.root.dataset.placeholder = this.placeholder;
|
|
102
|
-
if (changedProperties.has("height")) this.updateEditorHeight();
|
|
103
101
|
}
|
|
104
102
|
}
|
|
105
103
|
/** HTML을 Quill Delta로 변환해 silent source로 주입한다 — 프로그램적 콘텐츠 세팅이
|
|
@@ -121,12 +119,6 @@ var UTextEditor = class UTextEditor extends UElement {
|
|
|
121
119
|
</div>
|
|
122
120
|
`;
|
|
123
121
|
}
|
|
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
122
|
/**
|
|
131
123
|
* Get the current content as HTML
|
|
132
124
|
*/
|
|
@@ -22,12 +22,14 @@ import { css } from "lit";
|
|
|
22
22
|
* 색 선언 수 25 → 9.
|
|
23
23
|
*/
|
|
24
24
|
var styles = css`
|
|
25
|
+
/* 호스트는 자르지 않는다 — Quill 의 떠 있는 UI(선택기 목록 · 링크 툴팁)는 편집기 상자 밖으로 나갈 수 있어야 한다.
|
|
26
|
+
종전 overflow: hidden 은 짧은 편집기에서 그것들을 잘라 누를 수 없게 만들었다. 둥근 모서리는 배경을 칠하는
|
|
27
|
+
머리글(과 머리글이 없을 때의 툴바)이 자기 위 모서리를 둥글게 해서 지킨다. */
|
|
25
28
|
:host {
|
|
26
29
|
display: block;
|
|
27
30
|
border: 1px solid var(--u-border-color, #E0E0E0);
|
|
28
31
|
border-radius: var(--u-radius-md, 4px);
|
|
29
32
|
background: var(--u-panel-bg-color, #FFFFFF);
|
|
30
|
-
overflow: hidden;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
.header {
|
|
@@ -38,6 +40,11 @@ var styles = css`
|
|
|
38
40
|
background: var(--u-bg-color-hover, #F5F5F5);
|
|
39
41
|
min-height: 48px;
|
|
40
42
|
gap: 12px;
|
|
43
|
+
border-radius: calc(var(--u-radius-md, 4px) - 1px) calc(var(--u-radius-md, 4px) - 1px) 0 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
:host([headless]) .ql-toolbar {
|
|
47
|
+
border-radius: calc(var(--u-radius-md, 4px) - 1px) calc(var(--u-radius-md, 4px) - 1px) 0 0;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
.title {
|
|
@@ -51,6 +58,8 @@ var styles = css`
|
|
|
51
58
|
|
|
52
59
|
.editor {
|
|
53
60
|
position: relative;
|
|
61
|
+
/* 이 값은 «호스트 CSS 높이를 따르는 것» 이 아니라 렌더의 인라인 style 이 항상 덮는 폴백이다 —
|
|
62
|
+
높이의 주인은 height 프로퍼티이고, 그 JSDoc 이 계약을 적는다. (주석에도 백틱을 쓰지 않는다 — css 템플릿이 그 자리에서 끝난다.) */
|
|
54
63
|
height: 300px;
|
|
55
64
|
}
|
|
56
65
|
|
|
@@ -58,6 +67,33 @@ var styles = css`
|
|
|
58
67
|
height: 100%;
|
|
59
68
|
}
|
|
60
69
|
|
|
70
|
+
/* 편집 영역은 «높이에서 툴바를 뺀 나머지» 다 — 툴바 높이를 숫자로 가정하지 않는다(좁으면 툴바가 여러 줄로 접힌다).
|
|
71
|
+
종전에는 편집 영역을 height - 42px 로 줘, 툴바가 접히면 편집 영역이 상자 밖으로 밀려나 아래가 잘렸다.
|
|
72
|
+
⚠Quill 은 넘겨받은 요소(.quill-container)를 .ql-container 로 만들고, 툴바를 그 «앞 형제» 로 .editor 안에 넣는다 —
|
|
73
|
+
그래서 세로 배치의 주인은 .editor 다. */
|
|
74
|
+
.editor {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.editor > .ql-toolbar {
|
|
80
|
+
flex: none;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.editor > .ql-container {
|
|
84
|
+
flex: 1 1 auto;
|
|
85
|
+
min-height: 0;
|
|
86
|
+
height: auto;
|
|
87
|
+
display: flex;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.ql-container > .ql-editor {
|
|
92
|
+
flex: 1 1 auto;
|
|
93
|
+
min-height: 0;
|
|
94
|
+
height: auto;
|
|
95
|
+
}
|
|
96
|
+
|
|
61
97
|
.ql-editor {
|
|
62
98
|
font-family: var(--u-font-base);
|
|
63
99
|
font-size: 24px;
|
|
@@ -80,12 +116,28 @@ var styles = css`
|
|
|
80
116
|
color: var(--u-txt-color, #212121);
|
|
81
117
|
}
|
|
82
118
|
|
|
119
|
+
/* 🔴Quill 이 그리는 타깃의 치수 — 우리가 덮어쓴다(사람 결정 HD-58 ⒜). 소비자가 보는 것은 우리 컴포넌트이고,
|
|
120
|
+
WCAG 2.2 SC 2.5.8 은 «누가 그렸는가» 를 묻지 않는다.
|
|
121
|
+
⑴ 색 견본 16×16(붙어 있어 간격 예외도 서지 않는다) → 24×24 · 한 줄 일곱 칸이라 목록 폭도 함께(7×(24+4)+10).
|
|
122
|
+
⑵ 링크 툴팁의 Edit·Remove 는 줄 높이가 26 인데 인라인이라 실제 높이가 19 였다 → inline-block 으로 26. */
|
|
123
|
+
.ql-snow .ql-color-picker .ql-picker-item {
|
|
124
|
+
width: 24px;
|
|
125
|
+
height: 24px;
|
|
126
|
+
}
|
|
127
|
+
.ql-snow .ql-color-picker .ql-picker-options {
|
|
128
|
+
width: 206px;
|
|
129
|
+
}
|
|
130
|
+
.ql-snow .ql-tooltip a.ql-action,
|
|
131
|
+
.ql-snow .ql-tooltip a.ql-remove {
|
|
132
|
+
display: inline-block;
|
|
133
|
+
}
|
|
134
|
+
|
|
83
135
|
.ql-toolbar .ql-stroke {
|
|
84
|
-
stroke: var(--u-txt-color-weak, #
|
|
136
|
+
stroke: var(--u-txt-color-weak, #616161);
|
|
85
137
|
}
|
|
86
138
|
|
|
87
139
|
.ql-toolbar .ql-fill {
|
|
88
|
-
fill: var(--u-txt-color-weak, #
|
|
140
|
+
fill: var(--u-txt-color-weak, #616161);
|
|
89
141
|
}
|
|
90
142
|
|
|
91
143
|
/* 강조는 역할 토큰 경유 — 소비자가 브랜드를 바꾸면 함께 따라온다. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UCodeEditor as UCodeEditorElement } from '
|
|
2
|
+
import { UCodeEditor as UCodeEditorElement } from '@iyulab/editor-components/dist/components/code-editor/UCodeEditor.js';
|
|
3
3
|
|
|
4
4
|
export declare const UCodeEditor: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UCodeEditorElement>, keyof React.HTMLAttributes<UCodeEditorElement>>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UCodeEditor as UCodeEditorElement } from '
|
|
3
|
+
import { UCodeEditor as UCodeEditorElement } from '@iyulab/editor-components/dist/components/code-editor/UCodeEditor.js';
|
|
4
4
|
|
|
5
5
|
export const UCodeEditor = createComponent({
|
|
6
6
|
react: React,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { UTextEditor as UTextEditorElement } from '
|
|
2
|
+
import { UTextEditor as UTextEditorElement } from '@iyulab/editor-components/dist/components/text-editor/UTextEditor.js';
|
|
3
3
|
|
|
4
4
|
export declare const UTextEditor: React.ForwardRefExoticComponent<
|
|
5
5
|
Omit<Partial<UTextEditorElement>, keyof React.HTMLAttributes<UTextEditorElement>>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createComponent } from '@lit/react';
|
|
3
|
-
import { UTextEditor as UTextEditorElement } from '
|
|
3
|
+
import { UTextEditor as UTextEditorElement } from '@iyulab/editor-components/dist/components/text-editor/UTextEditor.js';
|
|
4
4
|
|
|
5
5
|
export const UTextEditor = createComponent({
|
|
6
6
|
react: React,
|
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.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iyulab",
|
|
7
7
|
"web-components",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
|
+
"skills",
|
|
21
22
|
"LICENSE",
|
|
22
23
|
"README.md",
|
|
23
24
|
"CHANGELOG.md",
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
}
|
|
46
47
|
},
|
|
47
48
|
"scripts": {
|
|
49
|
+
"preversion": "node -e \"if(require('fs').existsSync('../../scripts/preversion-check.mjs'))require('child_process').execFileSync('node',['../../scripts/preversion-check.mjs'],{stdio:'inherit'})\"",
|
|
48
50
|
"preview": "vite",
|
|
49
51
|
"test": "vitest run",
|
|
50
52
|
"test:watch": "vitest",
|
|
@@ -54,14 +56,14 @@
|
|
|
54
56
|
"typecheck": "tsc --noEmit"
|
|
55
57
|
},
|
|
56
58
|
"dependencies": {
|
|
57
|
-
"@iyulab/components": "^1.15.0",
|
|
58
59
|
"lit": "^3.3.3",
|
|
59
|
-
"monaco-editor": "^0.55.1 || ^0.56.0",
|
|
60
60
|
"quill": "^2.0.3"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
+
"@iyulab/components": ">=1.15.0",
|
|
63
64
|
"@lit/react": ">=1.0.8",
|
|
64
|
-
"react": ">=18.0.0"
|
|
65
|
+
"react": ">=18.0.0",
|
|
66
|
+
"monaco-editor": "^0.55.1"
|
|
65
67
|
},
|
|
66
68
|
"peerDependenciesMeta": {
|
|
67
69
|
"@lit/react": {
|
|
@@ -72,16 +74,19 @@
|
|
|
72
74
|
}
|
|
73
75
|
},
|
|
74
76
|
"devDependencies": {
|
|
75
|
-
"@eslint/js": "^
|
|
77
|
+
"@eslint/js": "^10.0.1",
|
|
76
78
|
"@types/node": "^26.1.1",
|
|
77
79
|
"@vitest/browser-playwright": "^4.1.10",
|
|
78
|
-
"eslint": "^
|
|
80
|
+
"eslint": "^10.1.0",
|
|
79
81
|
"glob": "^13.0.6",
|
|
80
82
|
"globals": "^17.7.0",
|
|
81
|
-
"typescript": "^
|
|
83
|
+
"typescript": "^6.0.2",
|
|
82
84
|
"typescript-eslint": "^8.64.0",
|
|
83
85
|
"vite": "^8.1.4",
|
|
84
86
|
"vite-plugin-dts": "^5.0.3",
|
|
85
87
|
"vitest": "^4.1.10"
|
|
88
|
+
},
|
|
89
|
+
"overrides": {
|
|
90
|
+
"dompurify": "^3.4.13"
|
|
86
91
|
}
|
|
87
92
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: iyulab-editor-components
|
|
3
|
+
description: Editor web components — a Monaco-based code editor and a Quill-based rich text editor. Both auto-sync with the surrounding document theme (code editor) or the shared @iyulab/components design tokens (text editor). Use when working with @iyulab/editor-components package.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: iyulab
|
|
7
|
+
version: "0.3.3"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# @iyulab/editor-components
|
|
11
|
+
|
|
12
|
+
Editor web components built on [Lit](https://lit.dev/). Custom elements (`u-*` tags) that work in any framework or vanilla HTML.
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @iyulab/editor-components
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Import all components at once:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import '@iyulab/editor-components';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Import individual components (tree-shakable):
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import '@iyulab/editor-components/dist/components/code-editor/UCodeEditor.js';
|
|
30
|
+
import '@iyulab/editor-components/dist/components/text-editor/UTextEditor.js';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Components
|
|
36
|
+
|
|
37
|
+
- [`u-code-editor`](./references/components/code-editor.md) — Monaco-based code editor. Language-aware syntax highlighting, automatic light/dark theme sync
|
|
38
|
+
- [`u-text-editor`](./references/components/text-editor.md) — Quill-based rich text editor. HTML/plain-text/Delta content access, customizable toolbar
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Events
|
|
43
|
+
|
|
44
|
+
| Event | Source | Detail | Description |
|
|
45
|
+
|-------|--------|--------|--------------|
|
|
46
|
+
| `change` | `u-code-editor` | none (plain `Event`) | User edit in the Monaco editor |
|
|
47
|
+
| `change` | `u-text-editor` | `{ html, text, delta }` | User edit in the Quill editor |
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# u-code-editor
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
import '@iyulab/editor-components/dist/components/code-editor/UCodeEditor.js';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
**Tag:** `u-code-editor`
|
|
8
|
+
|
|
9
|
+
Monaco Editor wrapped as a custom element. Syntax highlighting, per-language configuration, and automatic light/dark sync with the document theme (`Theme.resolved()` from `@iyulab/components`).
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<u-code-editor language="typescript" .value=${"const x: number = 42;"}></u-code-editor>
|
|
13
|
+
|
|
14
|
+
<!-- Without header -->
|
|
15
|
+
<u-code-editor language="json" headless .value=${JSON.stringify({key: 'value'}, null, 2)}></u-code-editor>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Properties
|
|
21
|
+
|
|
22
|
+
| Property | Type | Default | Reflect | Description |
|
|
23
|
+
|----------|------|---------|---------|-------------|
|
|
24
|
+
| `headless` | `boolean` | `false` | ✓ | Hides the header (label + `header-actions` slot) |
|
|
25
|
+
| `label` | `string` | `'Editor'` | — | Header title text |
|
|
26
|
+
| `theme` | `'light' \| 'dark'` | `'light'` | — | Editor color theme. Synced automatically from `Theme.resolved()` on connect and on every `data-theme`/`theme` attribute mutation on `document.documentElement` — the declared default is overwritten before first render, and setting it directly is overwritten by the next sync |
|
|
27
|
+
| `readOnly` | `boolean` | `false` | — | Prevents user input |
|
|
28
|
+
| `language` | `string` | `'json'` | — | Monaco language id (`"javascript"`, `"typescript"`, …) |
|
|
29
|
+
| `fontSize` | `number` | `14` | — | Editor font size in pixels |
|
|
30
|
+
| `value` | `string` | `''` | — | Current text content |
|
|
31
|
+
|
|
32
|
+
## Sizing
|
|
33
|
+
|
|
34
|
+
**The host box owns the size.** `:host` is `width: 100%; height: 100%`, and the editing area is
|
|
35
|
+
whatever the header leaves — Monaco re-layouts into it (`automaticLayout`). There is no `height`
|
|
36
|
+
property: size the element, or give its parent a height.
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<!-- ✓ the parent has a height, so the editor fills it -->
|
|
40
|
+
<div style="height: 400px"><u-code-editor></u-code-editor></div>
|
|
41
|
+
|
|
42
|
+
<!-- ✓ or size the element itself -->
|
|
43
|
+
<u-code-editor style="height: 400px"></u-code-editor>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
⚠ **In a parent that has no height of its own, `height: 100%` resolves to nothing and the editing
|
|
47
|
+
area collapses to a few pixels** — you get the header and an empty strip, with no error and nothing
|
|
48
|
+
in the console. Give the parent a height, or set one on the element.
|
|
49
|
+
|
|
50
|
+
`headless` removes the header, so the editing area is then the whole host box.
|
|
51
|
+
|
|
52
|
+
⚠ `u-text-editor` is the opposite: there, a `height` property sizes the editing area and a CSS
|
|
53
|
+
`height` on the host does *not* change it. A layout that works for one does not transfer to the
|
|
54
|
+
other unchanged.
|
|
55
|
+
|
|
56
|
+
## Slots
|
|
57
|
+
|
|
58
|
+
| Name | Description |
|
|
59
|
+
|------|-------------|
|
|
60
|
+
| `header-actions` | Content placed at the end of the header (e.g. extra toolbar buttons) |
|
|
61
|
+
|
|
62
|
+
## Events
|
|
63
|
+
|
|
64
|
+
| Event | Detail | Description |
|
|
65
|
+
|-------|--------|--------------|
|
|
66
|
+
| `change` | none (plain `Event`) | Fired on user edits (`Monaco.onDidChangeModelContent`). Not fired for programmatic `value` assignment while Monaco already holds that value. Read the new content from `value` |
|
|
67
|
+
|
|
68
|
+
## CSS Custom Properties
|
|
69
|
+
|
|
70
|
+
None. Layout is plain flexbox — the header keeps its own height and the editing area takes the rest — so there is no layout variable to override (see [Sizing](#sizing) for how the element is sized), and colours come from Monaco's own theme (`vs-light`/`vs-dark`) rather than this library's `--u-*` design tokens.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# u-text-editor
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
import '@iyulab/editor-components/dist/components/text-editor/UTextEditor.js';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
**Tag:** `u-text-editor`
|
|
8
|
+
|
|
9
|
+
Rich text editor built on [Quill](https://quilljs.com/). Reads and writes HTML, plain text, or Quill Delta.
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<u-text-editor placeholder="Start writing..." .value=${"<p>Hello</p>"}></u-text-editor>
|
|
13
|
+
|
|
14
|
+
<!-- Without header, custom toolbar -->
|
|
15
|
+
<u-text-editor headless .toolbar=${[['bold', 'italic'], ['link']]}></u-text-editor>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Properties
|
|
21
|
+
|
|
22
|
+
| Property | Type | Default | Reflect | Description |
|
|
23
|
+
|----------|------|---------|---------|-------------|
|
|
24
|
+
| `headless` | `boolean` | `false` | ✓ | Hides the header (label + `header-actions` slot) |
|
|
25
|
+
| `label` | `string` | `'Rich Text Editor'` | — | Header title text |
|
|
26
|
+
| `readOnly` | `boolean` | `false` | — | Prevents user input |
|
|
27
|
+
| `placeholder` | `string` | `'Start writing...'` | — | Placeholder shown when empty |
|
|
28
|
+
| `value` | `string` | `''` | — | Current content as HTML |
|
|
29
|
+
| `height` | `number` | `300` | — | Height of the **editing area** in pixels — see [Sizing](#sizing) |
|
|
30
|
+
| `toolbar` | `string[][]` | `undefined` | — | Custom toolbar configuration. When left unset, falls back at runtime to Quill's default 14-group toolbar (not a declared property default) |
|
|
31
|
+
|
|
32
|
+
## Sizing
|
|
33
|
+
|
|
34
|
+
`height` sets the **editing area**. The host box is that plus the header, so `height="300"` renders
|
|
35
|
+
an element about 349px tall (48px header + 300px + border); `headless` removes the header.
|
|
36
|
+
|
|
37
|
+
**A CSS `height` on the host does not resize the editing area** — it resizes only the host box. Set
|
|
38
|
+
it smaller and the editing area overflows past the box, and is deliberately *not* clipped, because
|
|
39
|
+
Quill's floating UI (picker lists, the link tooltip) has to be able to leave it. Set it larger — or
|
|
40
|
+
use `height: 100%` — and empty space is left below. So express a parent-filling layout through the
|
|
41
|
+
property, not CSS:
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<!-- ✗ the host box becomes 150px; the editing area stays 300px and overflows it -->
|
|
45
|
+
<u-text-editor style="height: 150px" height="300"></u-text-editor>
|
|
46
|
+
|
|
47
|
+
<!-- ✓ -->
|
|
48
|
+
<u-text-editor .height=${containerHeight - 48}></u-text-editor>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
⚠ `u-code-editor` is the opposite: it has no `height` property and fills the host box
|
|
52
|
+
(`height: 100%`). A layout that works for one does not transfer to the other unchanged.
|
|
53
|
+
|
|
54
|
+
## Methods
|
|
55
|
+
|
|
56
|
+
| Method | Returns | Description |
|
|
57
|
+
|--------|---------|--------------|
|
|
58
|
+
| `getHTML()` | `string` | Current content as HTML |
|
|
59
|
+
| `getText()` | `string` | Current content as plain text |
|
|
60
|
+
| `getDelta()` | `QuillDelta \| null` | Current content as a Quill Delta |
|
|
61
|
+
| `setHTML(html)` | `void` | Set content from HTML (same as setting `.value`) |
|
|
62
|
+
| `setDelta(delta)` | `void` | Set content from a Quill Delta |
|
|
63
|
+
| `clear()` | `void` | Clear all content |
|
|
64
|
+
| `focus()` | `void` | Focus the editor |
|
|
65
|
+
|
|
66
|
+
`setHTML`/`setDelta`/`clear` are programmatic — like the `value` property path, they do not fire `change`.
|
|
67
|
+
|
|
68
|
+
## Slots
|
|
69
|
+
|
|
70
|
+
| Name | Description |
|
|
71
|
+
|------|-------------|
|
|
72
|
+
| `header-actions` | Content placed at the end of the header (e.g. extra toolbar buttons) |
|
|
73
|
+
|
|
74
|
+
## Events
|
|
75
|
+
|
|
76
|
+
| Event | Detail | Description |
|
|
77
|
+
|-------|--------|--------------|
|
|
78
|
+
| `change` | `{ html: string, text: string, delta: QuillDelta }` | Fired only for user edits (Quill `source === 'user'`) — composed so it crosses the shadow boundary |
|
|
79
|
+
|
|
80
|
+
## CSS Custom Properties
|
|
81
|
+
|
|
82
|
+
`UTextEditor` doesn't declare any custom properties of its own — its styles read the shared
|
|
83
|
+
`@iyulab/components` design tokens directly (`--u-border-color`, `--u-panel-bg-color`,
|
|
84
|
+
`--u-txt-color`, `--u-txt-color-weak`, `--u-bg-color-hover`, `--u-radius-md`, `--u-primary-color`,
|
|
85
|
+
`--u-font-base`), so it re-themes automatically with the rest of the library and has nothing
|
|
86
|
+
component-specific to override.
|