@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 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` | 에디터 높이(px) |
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 test # Vite 개발 서버 (컴포넌트 프리뷰)
101
- npm run build # 프로덕션 빌드 (eslint + vite)
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.144.0/helpers/esm/decorate.js
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.144.0/helpers/esm/decorateMetadata.js
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.144.0/helpers/esm/decorateMetadata.js";
4
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.144.0/helpers/esm/decorate.js";
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: block;
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: absolute;
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
- /** The height of the editor in pixels. @default 300 */
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.144.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.144.0/helpers/esm/decorate.js";
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, #757575);
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, #757575);
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 '../components/code-editor/UCodeEditor';
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 '../components/code-editor/UCodeEditor.js';
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 '../components/text-editor/UTextEditor';
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 '../components/text-editor/UTextEditor.js';
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.3.2",
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": "^9.39.4",
77
+ "@eslint/js": "^10.0.1",
76
78
  "@types/node": "^26.1.1",
77
79
  "@vitest/browser-playwright": "^4.1.10",
78
- "eslint": "^9.39.4",
80
+ "eslint": "^10.1.0",
79
81
  "glob": "^13.0.6",
80
82
  "globals": "^17.7.0",
81
- "typescript": "^5.9.3",
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.