@operato/help 10.29.17 → 10.29.20
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 +17 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +146 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +1176 -0
- package/dist/src/components/ox-doc.d.ts +34 -0
- package/dist/src/components/ox-doc.js +361 -0
- package/dist/src/components/ox-doc.js.map +1 -0
- package/dist/src/components/ox-figure.d.ts +14 -0
- package/dist/src/components/ox-figure.js +51 -0
- package/dist/src/components/ox-figure.js.map +1 -0
- package/dist/src/components/ox-help-spot.d.ts +27 -0
- package/dist/src/components/ox-help-spot.js +108 -0
- package/dist/src/components/ox-help-spot.js.map +1 -0
- package/dist/src/components/ox-stage-ladder.d.ts +14 -0
- package/dist/src/components/ox-stage-ladder.js +69 -0
- package/dist/src/components/ox-stage-ladder.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/test/ox-doc.test.d.ts +2 -0
- package/dist/test/ox-doc.test.js +74 -0
- package/dist/test/ox-doc.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -6
- package/test/ox-doc.test.ts +94 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { LitElement, html, css } from 'lit';
|
|
3
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
4
|
+
import './ox-doc.js';
|
|
5
|
+
/*
|
|
6
|
+
* ox-help-spot — 인라인 문맥 도움말(C). 운영 화면 어디서든 `?` 하나로 관련 개념을 "그 자리에서" 연다.
|
|
7
|
+
* 같은 단일 소스(helps/{topic}.html)를 ox-doc 의 section 슬라이스 + compact 로 재사용 → 세 번째 소비 표면.
|
|
8
|
+
* 사용: <ox-help-spot topic="twin/concept" section="entities"></ox-help-spot>
|
|
9
|
+
* full 링크는 앱이 처리하도록 이벤트(ox-help-open-full) 방출.
|
|
10
|
+
*/
|
|
11
|
+
let OxHelpSpot = class OxHelpSpot extends LitElement {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
this.topic = '';
|
|
15
|
+
this.section = '';
|
|
16
|
+
this.label = '';
|
|
17
|
+
this.open = false;
|
|
18
|
+
/**
|
|
19
|
+
* `Escape` 로 닫는다 — **뒷막 누름의 키보드 짝**이다.
|
|
20
|
+
*
|
|
21
|
+
* 이것이 없으면 도움말을 연 사람이 탭으로 그 안을 다 지나야 빠져나온다. 마우스로 열어 보면 안
|
|
22
|
+
* 보이는 부류다(WCAG 2.1 §2.1.1 · §2.1.2).
|
|
23
|
+
*/
|
|
24
|
+
this.onKeydown = (event) => {
|
|
25
|
+
if (!this.open || event.key !== 'Escape') {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
event.stopPropagation();
|
|
30
|
+
this.close();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
toggle(e) { e.stopPropagation(); this.open = !this.open; }
|
|
34
|
+
close() { this.open = false; }
|
|
35
|
+
openFull(e) {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
this.close();
|
|
38
|
+
this.dispatchEvent(new CustomEvent('ox-help-open-full', { detail: { topic: this.topic, section: this.section }, bubbles: true, composed: true }));
|
|
39
|
+
}
|
|
40
|
+
connectedCallback() {
|
|
41
|
+
super.connectedCallback();
|
|
42
|
+
this.addEventListener('keydown', this.onKeydown);
|
|
43
|
+
}
|
|
44
|
+
disconnectedCallback() {
|
|
45
|
+
this.removeEventListener('keydown', this.onKeydown);
|
|
46
|
+
super.disconnectedCallback();
|
|
47
|
+
}
|
|
48
|
+
render() {
|
|
49
|
+
return html `
|
|
50
|
+
<button class="q" title=${this.label || 'help'} aria-label=${this.label || 'help'} @click=${(e) => this.toggle(e)}>?</button>
|
|
51
|
+
${this.open
|
|
52
|
+
? html `
|
|
53
|
+
${ /*
|
|
54
|
+
* 뒷막은 **탭 순서에 넣지 않는다.** 눌러서 닫는 것은 마우스의 편의이고, 키보드로는
|
|
55
|
+
* `Escape` 가 그 일을 한다(아래 `onKeydown`). 뒷막에 `tabindex` 를 주면 탭을 눌렀을 때
|
|
56
|
+
* 아무것도 아닌 빈 면에 초점이 서고, 낭독기는 읽을 것이 없는 것을 읽는다.
|
|
57
|
+
*
|
|
58
|
+
* 그래서 보조기술에서 숨긴다 — 이 면은 그림이지 기능이 아니다.
|
|
59
|
+
*/''}
|
|
60
|
+
<div class="backdrop" aria-hidden="true" @click=${() => this.close()}></div>
|
|
61
|
+
${ /* 안쪽 누름이 뒷막까지 새지 않게만 한다 — 기능이 아니라 막는 것이다. */''}
|
|
62
|
+
<div class="panel" role="dialog" aria-modal="true" @click=${(e) => e.stopPropagation()}>
|
|
63
|
+
<div class="bar">
|
|
64
|
+
<a href="#" @click=${(e) => this.openFull(e)}>open full →</a>
|
|
65
|
+
<button @click=${() => this.close()}>✕ close</button>
|
|
66
|
+
</div>
|
|
67
|
+
<ox-doc compact topic=${this.topic} section=${this.section}></ox-doc>
|
|
68
|
+
</div>
|
|
69
|
+
`
|
|
70
|
+
: ''}
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
OxHelpSpot.styles = css `
|
|
75
|
+
/* 중심 정렬은 담는 쪽 책임 — 제목 행을 flex(align-items:center)로 잡아 광학 중심에 맞춘다.
|
|
76
|
+
inline 으로 쓰일 때의 기본 정렬만 여기서 vertical-align:middle 로 둔다. */
|
|
77
|
+
:host { display: inline-flex; align-items: center; vertical-align: middle; --_accent: var(--ox-doc-accent, var(--ui-accent, #0d8f86)); }
|
|
78
|
+
button.q { width: 17px; height: 17px; border-radius: 50%; border: 1px solid color-mix(in oklab, var(--_accent), transparent 45%); background: color-mix(in oklab, var(--_accent), transparent 88%); color: var(--_accent); font: 600 11px/1 var(--ox-doc-sans, sans-serif); cursor: pointer; padding: 0; }
|
|
79
|
+
button.q:hover { background: color-mix(in oklab, var(--_accent), transparent 75%); }
|
|
80
|
+
.backdrop { position: fixed; inset: 0; background: rgba(10,15,20,.32); z-index: 9998; }
|
|
81
|
+
.panel { position: fixed; z-index: 9999; top: 50%; left: 50%; transform: translate(-50%, -50%); width: min(620px, 92vw); max-height: 82vh; overflow: auto; background: var(--ox-doc-panel, var(--ui-panel, #fff)); border: 1px solid var(--ox-doc-line, var(--ui-line, #dde4ea)); border-radius: 14px; box-shadow: 0 20px 60px rgba(0,0,0,.28); padding: 8px 20px 18px; }
|
|
82
|
+
/* 표준 스크롤바(얇은) — board-placement-tray 와 동일 컨벤션. */
|
|
83
|
+
.panel { scrollbar-width: thin; scrollbar-color: var(--scrollbar-thumb-color, rgba(90, 107, 122, 0.4)) transparent; }
|
|
84
|
+
.panel::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
85
|
+
.panel::-webkit-scrollbar-track { background: transparent; }
|
|
86
|
+
.panel::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb-color, rgba(90, 107, 122, 0.4)); border-radius: 4px; }
|
|
87
|
+
.panel::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover-color, rgba(90, 107, 122, 0.6)); }
|
|
88
|
+
.bar { position: sticky; top: 0; display: flex; justify-content: flex-end; gap: 10px; padding: 8px 0 4px; }
|
|
89
|
+
.bar a, .bar button { font: 600 12px/1 var(--ox-doc-mono, monospace); color: var(--ox-doc-accent, var(--ui-accent, #0d8f86)); background: none; border: 0; cursor: pointer; text-decoration: none; letter-spacing: .04em; }
|
|
90
|
+
:focus-visible { outline: 2px solid var(--_accent); outline-offset: 2px; }
|
|
91
|
+
`;
|
|
92
|
+
__decorate([
|
|
93
|
+
property({ type: String })
|
|
94
|
+
], OxHelpSpot.prototype, "topic", void 0);
|
|
95
|
+
__decorate([
|
|
96
|
+
property({ type: String })
|
|
97
|
+
], OxHelpSpot.prototype, "section", void 0);
|
|
98
|
+
__decorate([
|
|
99
|
+
property({ type: String })
|
|
100
|
+
], OxHelpSpot.prototype, "label", void 0);
|
|
101
|
+
__decorate([
|
|
102
|
+
state()
|
|
103
|
+
], OxHelpSpot.prototype, "open", void 0);
|
|
104
|
+
OxHelpSpot = __decorate([
|
|
105
|
+
customElement('ox-help-spot')
|
|
106
|
+
], OxHelpSpot);
|
|
107
|
+
export { OxHelpSpot };
|
|
108
|
+
//# sourceMappingURL=ox-help-spot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ox-help-spot.js","sourceRoot":"","sources":["../../../src/components/ox-help-spot.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css, type TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport './ox-doc.js'\n\n/*\n * ox-help-spot — 인라인 문맥 도움말(C). 운영 화면 어디서든 `?` 하나로 관련 개념을 \"그 자리에서\" 연다.\n * 같은 단일 소스(helps/{topic}.html)를 ox-doc 의 section 슬라이스 + compact 로 재사용 → 세 번째 소비 표면.\n * 사용: <ox-help-spot topic=\"twin/concept\" section=\"entities\"></ox-help-spot>\n * full 링크는 앱이 처리하도록 이벤트(ox-help-open-full) 방출.\n */\n@customElement('ox-help-spot')\nexport class OxHelpSpot extends LitElement {\n @property({ type: String }) topic = ''\n @property({ type: String }) section = ''\n @property({ type: String }) label = ''\n @state() private open = false\n\n static styles = css`\n /* 중심 정렬은 담는 쪽 책임 — 제목 행을 flex(align-items:center)로 잡아 광학 중심에 맞춘다.\n inline 으로 쓰일 때의 기본 정렬만 여기서 vertical-align:middle 로 둔다. */\n :host { display: inline-flex; align-items: center; vertical-align: middle; --_accent: var(--ox-doc-accent, var(--ui-accent, #0d8f86)); }\n button.q { width: 17px; height: 17px; border-radius: 50%; border: 1px solid color-mix(in oklab, var(--_accent), transparent 45%); background: color-mix(in oklab, var(--_accent), transparent 88%); color: var(--_accent); font: 600 11px/1 var(--ox-doc-sans, sans-serif); cursor: pointer; padding: 0; }\n button.q:hover { background: color-mix(in oklab, var(--_accent), transparent 75%); }\n .backdrop { position: fixed; inset: 0; background: rgba(10,15,20,.32); z-index: 9998; }\n .panel { position: fixed; z-index: 9999; top: 50%; left: 50%; transform: translate(-50%, -50%); width: min(620px, 92vw); max-height: 82vh; overflow: auto; background: var(--ox-doc-panel, var(--ui-panel, #fff)); border: 1px solid var(--ox-doc-line, var(--ui-line, #dde4ea)); border-radius: 14px; box-shadow: 0 20px 60px rgba(0,0,0,.28); padding: 8px 20px 18px; }\n /* 표준 스크롤바(얇은) — board-placement-tray 와 동일 컨벤션. */\n .panel { scrollbar-width: thin; scrollbar-color: var(--scrollbar-thumb-color, rgba(90, 107, 122, 0.4)) transparent; }\n .panel::-webkit-scrollbar { width: 6px; height: 6px; }\n .panel::-webkit-scrollbar-track { background: transparent; }\n .panel::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb-color, rgba(90, 107, 122, 0.4)); border-radius: 4px; }\n .panel::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover-color, rgba(90, 107, 122, 0.6)); }\n .bar { position: sticky; top: 0; display: flex; justify-content: flex-end; gap: 10px; padding: 8px 0 4px; }\n .bar a, .bar button { font: 600 12px/1 var(--ox-doc-mono, monospace); color: var(--ox-doc-accent, var(--ui-accent, #0d8f86)); background: none; border: 0; cursor: pointer; text-decoration: none; letter-spacing: .04em; }\n :focus-visible { outline: 2px solid var(--_accent); outline-offset: 2px; }\n `\n\n private toggle(e: Event) { e.stopPropagation(); this.open = !this.open }\n private close() { this.open = false }\n private openFull(e: Event) {\n e.preventDefault()\n this.close()\n this.dispatchEvent(new CustomEvent('ox-help-open-full', { detail: { topic: this.topic, section: this.section }, bubbles: true, composed: true }))\n }\n\n connectedCallback(): void {\n super.connectedCallback()\n this.addEventListener('keydown', this.onKeydown)\n }\n\n disconnectedCallback(): void {\n this.removeEventListener('keydown', this.onKeydown)\n super.disconnectedCallback()\n }\n\n /**\n * `Escape` 로 닫는다 — **뒷막 누름의 키보드 짝**이다.\n *\n * 이것이 없으면 도움말을 연 사람이 탭으로 그 안을 다 지나야 빠져나온다. 마우스로 열어 보면 안\n * 보이는 부류다(WCAG 2.1 §2.1.1 · §2.1.2).\n */\n private readonly onKeydown = (event: KeyboardEvent) => {\n if (!this.open || event.key !== 'Escape') {\n return\n }\n\n event.preventDefault()\n event.stopPropagation()\n this.close()\n }\n\n render(): TemplateResult {\n return html`\n <button class=\"q\" title=${this.label || 'help'} aria-label=${this.label || 'help'} @click=${(e: Event) => this.toggle(e)}>?</button>\n ${this.open\n ? html`\n ${/*\n * 뒷막은 **탭 순서에 넣지 않는다.** 눌러서 닫는 것은 마우스의 편의이고, 키보드로는\n * `Escape` 가 그 일을 한다(아래 `onKeydown`). 뒷막에 `tabindex` 를 주면 탭을 눌렀을 때\n * 아무것도 아닌 빈 면에 초점이 서고, 낭독기는 읽을 것이 없는 것을 읽는다.\n *\n * 그래서 보조기술에서 숨긴다 — 이 면은 그림이지 기능이 아니다.\n */ ''}\n <div class=\"backdrop\" aria-hidden=\"true\" @click=${() => this.close()}></div>\n ${/* 안쪽 누름이 뒷막까지 새지 않게만 한다 — 기능이 아니라 막는 것이다. */ ''}\n <div class=\"panel\" role=\"dialog\" aria-modal=\"true\" @click=${(e: Event) => e.stopPropagation()}>\n <div class=\"bar\">\n <a href=\"#\" @click=${(e: Event) => this.openFull(e)}>open full →</a>\n <button @click=${() => this.close()}>✕ close</button>\n </div>\n <ox-doc compact topic=${this.topic} section=${this.section}></ox-doc>\n </div>\n `\n : ''}\n `\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap { 'ox-help-spot': OxHelpSpot }\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { LitElement, type TemplateResult } from 'lit';
|
|
2
|
+
export declare class OxStageLadder extends LitElement {
|
|
3
|
+
/** 오름차순 표시(위로 갈수록 높은 단계). 기본 저작 순서 그대로. */
|
|
4
|
+
ascending: boolean;
|
|
5
|
+
private _rungs;
|
|
6
|
+
static styles: import("lit").CSSResult;
|
|
7
|
+
firstUpdated(): void;
|
|
8
|
+
render(): TemplateResult;
|
|
9
|
+
}
|
|
10
|
+
declare global {
|
|
11
|
+
interface HTMLElementTagNameMap {
|
|
12
|
+
'ox-stage-ladder': OxStageLadder;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { LitElement, html, css } from 'lit';
|
|
3
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
4
|
+
let OxStageLadder = class OxStageLadder extends LitElement {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
/** 오름차순 표시(위로 갈수록 높은 단계). 기본 저작 순서 그대로. */
|
|
8
|
+
this.ascending = false;
|
|
9
|
+
this._rungs = [];
|
|
10
|
+
}
|
|
11
|
+
firstUpdated() {
|
|
12
|
+
const items = Array.from(this.querySelectorAll(':scope > li'));
|
|
13
|
+
this._rungs = items.map(li => ({
|
|
14
|
+
text: (li.textContent || '').trim(),
|
|
15
|
+
state: li.dataset.state || '',
|
|
16
|
+
badge: li.dataset.badge || ''
|
|
17
|
+
}));
|
|
18
|
+
if (this.ascending)
|
|
19
|
+
this._rungs = [...this._rungs].reverse();
|
|
20
|
+
}
|
|
21
|
+
render() {
|
|
22
|
+
const total = this._rungs.length;
|
|
23
|
+
return html `
|
|
24
|
+
<ul>
|
|
25
|
+
${this._rungs.map((r, i) => {
|
|
26
|
+
const idx = this.ascending ? total - i : i + 1;
|
|
27
|
+
return html `<li data-state=${r.state}><span class="n">${idx}</span><span class="txt">${r.text}</span>${r.badge ? html `<span class="badge">${r.badge}</span>` : html `<span></span>`}</li>`;
|
|
28
|
+
})}
|
|
29
|
+
</ul>
|
|
30
|
+
<div class="fallback" aria-hidden="true"><slot></slot></div>
|
|
31
|
+
`;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
OxStageLadder.styles = css `
|
|
35
|
+
:host {
|
|
36
|
+
/* 해소 순서: 호스트 오버라이드 → 앱 팔레트 → 리터럴(ox-doc 와 동일 규약). */
|
|
37
|
+
--_ink: var(--ox-doc-ink, var(--ui-ink, #16202b)); --_muted: var(--ox-doc-muted, var(--ui-muted, #566573)); --_faint: var(--ox-doc-faint, var(--ui-faint, #8a99a6));
|
|
38
|
+
--_line: var(--ox-doc-line, var(--ui-line, #dde4ea)); --_panel: var(--ox-doc-panel, var(--ui-panel, #fff));
|
|
39
|
+
--_accent: var(--ox-doc-accent, var(--ui-accent, #0d8f86));
|
|
40
|
+
--_accent-deep: var(--ox-doc-accent-deep, color-mix(in oklab, var(--_accent), var(--_ink) 22%));
|
|
41
|
+
--_accent-wash: var(--ox-doc-accent-wash, var(--ui-accent-wash, #e3f2f0));
|
|
42
|
+
--_amber: var(--ox-doc-amber, var(--ui-warn, #c07f14));
|
|
43
|
+
--_amber-wash: var(--ox-doc-amber-wash, color-mix(in srgb, var(--ui-warn, #c07f14) 16%, transparent));
|
|
44
|
+
--_mono: var(--ox-doc-mono, "SF Mono", ui-monospace, Menlo, monospace);
|
|
45
|
+
display: block; margin: 20px 0;
|
|
46
|
+
}
|
|
47
|
+
ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
|
|
48
|
+
li { display: grid; grid-template-columns: 30px 1fr auto; gap: 13px; align-items: center; background: var(--_panel); border: 1px solid var(--_line); border-radius: 11px; padding: 12px 15px; }
|
|
49
|
+
.n { font-family: var(--_mono); font-weight: 700; color: var(--_muted); font-size: 15px; text-align: center; }
|
|
50
|
+
.txt { font-size: 15px; color: var(--_ink); }
|
|
51
|
+
.badge { font-family: var(--_mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; padding: 4px 9px; border-radius: 999px; background: var(--_line); color: var(--_muted); white-space: nowrap; }
|
|
52
|
+
li[data-state="done"], li[data-state="now"] { border-color: color-mix(in oklab, var(--_accent), var(--_line) 45%); background: color-mix(in oklab, var(--_accent-wash), var(--_panel) 45%); }
|
|
53
|
+
li[data-state="done"] .n, li[data-state="now"] .n { color: var(--_accent); }
|
|
54
|
+
/* 강조색 위에 올리는 글자는 짝 토큰을 쓴다 — 다크의 밝은 초록 위에서 흰 글자는 대비 2.24 로 안 읽힌다. */
|
|
55
|
+
li[data-state="now"] .badge { background: var(--_accent); color: var(--ui-on-accent, #fff); }
|
|
56
|
+
li[data-state="next"] .badge { background: var(--_amber-wash); color: var(--_amber); border: 1px solid color-mix(in oklab, var(--_amber), transparent 65%); }
|
|
57
|
+
.fallback { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
|
|
58
|
+
`;
|
|
59
|
+
__decorate([
|
|
60
|
+
property({ type: Boolean })
|
|
61
|
+
], OxStageLadder.prototype, "ascending", void 0);
|
|
62
|
+
__decorate([
|
|
63
|
+
state()
|
|
64
|
+
], OxStageLadder.prototype, "_rungs", void 0);
|
|
65
|
+
OxStageLadder = __decorate([
|
|
66
|
+
customElement('ox-stage-ladder')
|
|
67
|
+
], OxStageLadder);
|
|
68
|
+
export { OxStageLadder };
|
|
69
|
+
//# sourceMappingURL=ox-stage-ladder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ox-stage-ladder.js","sourceRoot":"","sources":["../../../src/components/ox-stage-ladder.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAuB,MAAM,KAAK,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAe3D,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,UAAU;IAAtC;;QACL,2CAA2C;QACd,cAAS,GAAG,KAAK,CAAA;QAC7B,WAAM,GAAW,EAAE,CAAA;IAkDtC,CAAC;IAtBC,YAAY;QACV,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAkB,CAAA;QAC/E,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAC7B,IAAI,EAAE,CAAC,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;YACnC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC7B,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;SAC9B,CAAC,CAAC,CAAA;QACH,IAAI,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAA;IAC9D,CAAC;IAED,MAAM;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA;QAChC,OAAO,IAAI,CAAA;;UAEL,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;YAC9C,OAAO,IAAI,CAAA,kBAAkB,CAAC,CAAC,KAAK,oBAAoB,GAAG,4BAA4B,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,uBAAuB,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA,eAAe,OAAO,CAAA;QAC3L,CAAC,CAAC;;;KAGL,CAAA;IACH,CAAC;;AA/CM,oBAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBlB,AAxBY,CAwBZ;AA3B4B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gDAAkB;AAC7B;IAAhB,KAAK,EAAE;6CAA4B;AAHzB,aAAa;IADzB,aAAa,CAAC,iBAAiB,CAAC;GACpB,aAAa,CAqDzB","sourcesContent":["import { LitElement, html, css, type TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\n/*\n * ox-stage-ladder — 범용 \"단계 스케일\"(성숙도·레벨·진행 등, 도메인 무관).\n * 콘텐츠는 <li> 자식으로 저작: 텍스트 + data-state(done|now|next|future) + data-badge(선택 라벨).\n * 미등록 시 그냥 목록으로 노출(폴백/AI 안전). 등록 시 자식을 읽어 shadow 에 리치 사다리로 렌더.\n * 예:\n * <ox-stage-ladder>\n * <li data-state=\"now\" data-badge=\"지금\">기술 — 지금 무슨 일?</li>\n * <li data-state=\"next\" data-badge=\"진입\">처방 — 무엇을 해야?</li>\n * </ox-stage-ladder>\n */\ntype Rung = { text: string; state: string; badge: string }\n\n@customElement('ox-stage-ladder')\nexport class OxStageLadder extends LitElement {\n /** 오름차순 표시(위로 갈수록 높은 단계). 기본 저작 순서 그대로. */\n @property({ type: Boolean }) ascending = false\n @state() private _rungs: Rung[] = []\n\n static styles = css`\n :host {\n /* 해소 순서: 호스트 오버라이드 → 앱 팔레트 → 리터럴(ox-doc 와 동일 규약). */\n --_ink: var(--ox-doc-ink, var(--ui-ink, #16202b)); --_muted: var(--ox-doc-muted, var(--ui-muted, #566573)); --_faint: var(--ox-doc-faint, var(--ui-faint, #8a99a6));\n --_line: var(--ox-doc-line, var(--ui-line, #dde4ea)); --_panel: var(--ox-doc-panel, var(--ui-panel, #fff));\n --_accent: var(--ox-doc-accent, var(--ui-accent, #0d8f86));\n --_accent-deep: var(--ox-doc-accent-deep, color-mix(in oklab, var(--_accent), var(--_ink) 22%));\n --_accent-wash: var(--ox-doc-accent-wash, var(--ui-accent-wash, #e3f2f0));\n --_amber: var(--ox-doc-amber, var(--ui-warn, #c07f14));\n --_amber-wash: var(--ox-doc-amber-wash, color-mix(in srgb, var(--ui-warn, #c07f14) 16%, transparent));\n --_mono: var(--ox-doc-mono, \"SF Mono\", ui-monospace, Menlo, monospace);\n display: block; margin: 20px 0;\n }\n ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }\n li { display: grid; grid-template-columns: 30px 1fr auto; gap: 13px; align-items: center; background: var(--_panel); border: 1px solid var(--_line); border-radius: 11px; padding: 12px 15px; }\n .n { font-family: var(--_mono); font-weight: 700; color: var(--_muted); font-size: 15px; text-align: center; }\n .txt { font-size: 15px; color: var(--_ink); }\n .badge { font-family: var(--_mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; padding: 4px 9px; border-radius: 999px; background: var(--_line); color: var(--_muted); white-space: nowrap; }\n li[data-state=\"done\"], li[data-state=\"now\"] { border-color: color-mix(in oklab, var(--_accent), var(--_line) 45%); background: color-mix(in oklab, var(--_accent-wash), var(--_panel) 45%); }\n li[data-state=\"done\"] .n, li[data-state=\"now\"] .n { color: var(--_accent); }\n /* 강조색 위에 올리는 글자는 짝 토큰을 쓴다 — 다크의 밝은 초록 위에서 흰 글자는 대비 2.24 로 안 읽힌다. */\n li[data-state=\"now\"] .badge { background: var(--_accent); color: var(--ui-on-accent, #fff); }\n li[data-state=\"next\"] .badge { background: var(--_amber-wash); color: var(--_amber); border: 1px solid color-mix(in oklab, var(--_amber), transparent 65%); }\n .fallback { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }\n `\n\n firstUpdated() {\n const items = Array.from(this.querySelectorAll(':scope > li')) as HTMLElement[]\n this._rungs = items.map(li => ({\n text: (li.textContent || '').trim(),\n state: li.dataset.state || '',\n badge: li.dataset.badge || ''\n }))\n if (this.ascending) this._rungs = [...this._rungs].reverse()\n }\n\n render(): TemplateResult {\n const total = this._rungs.length\n return html`\n <ul>\n ${this._rungs.map((r, i) => {\n const idx = this.ascending ? total - i : i + 1\n return html`<li data-state=${r.state}><span class=\"n\">${idx}</span><span class=\"txt\">${r.text}</span>${r.badge ? html`<span class=\"badge\">${r.badge}</span>` : html`<span></span>`}</li>`\n })}\n </ul>\n <div class=\"fallback\" aria-hidden=\"true\"><slot></slot></div>\n `\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap { 'ox-stage-ladder': OxStageLadder }\n}\n"]}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -3,3 +3,7 @@ export * from './grist/help-decorated-renderer.js';
|
|
|
3
3
|
export * from './components/ox-inline-help.js';
|
|
4
4
|
export * from './components/ox-title-with-help.js';
|
|
5
5
|
export * from './components/ox-help-icon.js';
|
|
6
|
+
export * from './components/ox-doc.js';
|
|
7
|
+
export * from './components/ox-figure.js';
|
|
8
|
+
export * from './components/ox-stage-ladder.js';
|
|
9
|
+
export * from './components/ox-help-spot.js';
|
package/dist/src/index.js
CHANGED
|
@@ -3,4 +3,8 @@ export * from './grist/help-decorated-renderer.js';
|
|
|
3
3
|
export * from './components/ox-inline-help.js';
|
|
4
4
|
export * from './components/ox-title-with-help.js';
|
|
5
5
|
export * from './components/ox-help-icon.js';
|
|
6
|
+
export * from './components/ox-doc.js';
|
|
7
|
+
export * from './components/ox-figure.js';
|
|
8
|
+
export * from './components/ox-stage-ladder.js';
|
|
9
|
+
export * from './components/ox-help-spot.js';
|
|
6
10
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AACpC,cAAc,oCAAoC,CAAA;AAClD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,oCAAoC,CAAA;AAClD,cAAc,8BAA8B,CAAA","sourcesContent":["export * from './controller/help.js'\nexport * from './grist/help-decorated-renderer.js'\nexport * from './components/ox-inline-help.js'\nexport * from './components/ox-title-with-help.js'\nexport * from './components/ox-help-icon.js'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AACpC,cAAc,oCAAoC,CAAA;AAClD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,oCAAoC,CAAA;AAClD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,2BAA2B,CAAA;AACzC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA","sourcesContent":["export * from './controller/help.js'\nexport * from './grist/help-decorated-renderer.js'\nexport * from './components/ox-inline-help.js'\nexport * from './components/ox-title-with-help.js'\nexport * from './components/ox-help-icon.js'\nexport * from './components/ox-doc.js'\nexport * from './components/ox-figure.js'\nexport * from './components/ox-stage-ladder.js'\nexport * from './components/ox-help-spot.js'\n"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { expect, fixture, html, waitUntil } from '@open-wc/testing';
|
|
2
|
+
import '../src/components/ox-doc.js';
|
|
3
|
+
import '../src/components/ox-help-spot.js';
|
|
4
|
+
/**
|
|
5
|
+
* ox-doc reads /helps/{topic}.html and renders it; the products that used to keep their own copy rely on
|
|
6
|
+
* these behaviours.
|
|
7
|
+
*/
|
|
8
|
+
const DOCS = {
|
|
9
|
+
'a/one': `<h1 id="top">One</h1><p>lead</p><h2 id="part">Part</h2><p>in part</p><table><tr><td>x</td></tr></table><h2 id="next">Next</h2><p>after</p><a data-topic="a/two#end">two</a>`,
|
|
10
|
+
'a/two': `<h1>Two</h1><h2 id="end">End</h2>`
|
|
11
|
+
};
|
|
12
|
+
let requests = [];
|
|
13
|
+
const realFetch = window.fetch;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
requests = [];
|
|
16
|
+
window.fetch = (async (input) => {
|
|
17
|
+
const url = String(input);
|
|
18
|
+
requests.push(url);
|
|
19
|
+
const topic = url.replace(/^\/helps\//, '').replace(/\.html.*$/, '');
|
|
20
|
+
return DOCS[topic] ? new Response(DOCS[topic]) : new Response('', { status: 404 });
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
window.fetch = realFetch;
|
|
25
|
+
});
|
|
26
|
+
const bodyOf = (el) => el.shadowRoot.querySelector('.body');
|
|
27
|
+
describe('ox-doc', () => {
|
|
28
|
+
it('reads /helps/{topic}.html with the language and renders it', async () => {
|
|
29
|
+
const el = await fixture(html `<ox-doc topic="a/one"></ox-doc>`);
|
|
30
|
+
await waitUntil(() => { var _a; return (_a = bodyOf(el)) === null || _a === void 0 ? void 0 : _a.querySelector('h1'); });
|
|
31
|
+
expect(requests[0]).to.match(/^\/helps\/a\/one\.html\?lng=/);
|
|
32
|
+
expect(bodyOf(el).querySelector('h1').textContent).to.equal('One');
|
|
33
|
+
});
|
|
34
|
+
it('wraps tables in a scrolling box so they do not push the page wide', async () => {
|
|
35
|
+
const el = await fixture(html `<ox-doc topic="a/one"></ox-doc>`);
|
|
36
|
+
await waitUntil(() => { var _a; return (_a = bodyOf(el)) === null || _a === void 0 ? void 0 : _a.querySelector('table'); });
|
|
37
|
+
expect(bodyOf(el).querySelector('table').parentElement.classList.contains('tablewrap')).to.equal(true);
|
|
38
|
+
});
|
|
39
|
+
it('shows only the named section, up to the next heading of its level', async () => {
|
|
40
|
+
const el = await fixture(html `<ox-doc topic="a/one" section="part"></ox-doc>`);
|
|
41
|
+
await waitUntil(() => { var _a; return (_a = bodyOf(el)) === null || _a === void 0 ? void 0 : _a.querySelector('h2'); });
|
|
42
|
+
expect(bodyOf(el).textContent).to.contain('in part');
|
|
43
|
+
expect(bodyOf(el).textContent).to.not.contain('lead');
|
|
44
|
+
expect(bodyOf(el).textContent).to.not.contain('after');
|
|
45
|
+
});
|
|
46
|
+
it('follows a topic link in place and tells the host, without keeping the anchor in the file name', async () => {
|
|
47
|
+
const el = await fixture(html `<ox-doc topic="a/one"></ox-doc>`);
|
|
48
|
+
await waitUntil(() => { var _a; return (_a = bodyOf(el)) === null || _a === void 0 ? void 0 : _a.querySelector('a[data-topic]'); });
|
|
49
|
+
let told;
|
|
50
|
+
el.addEventListener('ox-doc-topic', e => (told = e.detail));
|
|
51
|
+
bodyOf(el).querySelector('a[data-topic]').click();
|
|
52
|
+
await waitUntil(() => { var _a, _b; return ((_b = (_a = bodyOf(el)) === null || _a === void 0 ? void 0 : _a.querySelector('h1')) === null || _b === void 0 ? void 0 : _b.textContent) === 'Two'; });
|
|
53
|
+
expect(told).to.deep.equal({ topic: 'a/two', hash: 'end' });
|
|
54
|
+
expect(requests[requests.length - 1]).to.match(/^\/helps\/a\/two\.html\?/);
|
|
55
|
+
});
|
|
56
|
+
it('says the topic is missing instead of showing an empty page', async () => {
|
|
57
|
+
const el = await fixture(html `<ox-doc topic="a/none"></ox-doc>`);
|
|
58
|
+
await waitUntil(() => el.shadowRoot.querySelector('.status.err'));
|
|
59
|
+
expect(el.shadowRoot.querySelector('.status.err').textContent).to.contain('a/none');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('ox-help-spot', () => {
|
|
63
|
+
it('opens the section in place and closes on Escape', async () => {
|
|
64
|
+
const el = await fixture(html `<ox-help-spot topic="a/one" section="part"></ox-help-spot>`);
|
|
65
|
+
el.shadowRoot.querySelector('button.q').click();
|
|
66
|
+
await waitUntil(() => el.shadowRoot.querySelector('ox-doc'));
|
|
67
|
+
const doc = el.shadowRoot.querySelector('ox-doc');
|
|
68
|
+
expect(doc.getAttribute('section')).to.equal('part');
|
|
69
|
+
expect(doc.hasAttribute('compact')).to.equal(true);
|
|
70
|
+
el.shadowRoot.querySelector('.panel').dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, composed: true }));
|
|
71
|
+
await waitUntil(() => !el.shadowRoot.querySelector('.panel'));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=ox-doc.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ox-doc.test.js","sourceRoot":"","sources":["../../test/ox-doc.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, html, waitUntil } from '@open-wc/testing'\n\nimport '../src/components/ox-doc.js'\nimport '../src/components/ox-help-spot.js'\nimport type { OxDoc } from '../src/components/ox-doc.js'\n\n/**\n * ox-doc reads /helps/{topic}.html and renders it; the products that used to keep their own copy rely on\n * these behaviours.\n */\nconst DOCS: Record<string, string> = {\n 'a/one': `<h1 id=\"top\">One</h1><p>lead</p><h2 id=\"part\">Part</h2><p>in part</p><table><tr><td>x</td></tr></table><h2 id=\"next\">Next</h2><p>after</p><a data-topic=\"a/two#end\">two</a>`,\n 'a/two': `<h1>Two</h1><h2 id=\"end\">End</h2>`\n}\n\nlet requests: string[] = []\nconst realFetch = window.fetch\n\nbeforeEach(() => {\n requests = []\n window.fetch = (async (input: RequestInfo | URL) => {\n const url = String(input)\n requests.push(url)\n const topic = url.replace(/^\\/helps\\//, '').replace(/\\.html.*$/, '')\n return DOCS[topic] ? new Response(DOCS[topic]) : new Response('', { status: 404 })\n }) as typeof window.fetch\n})\n\nafterEach(() => {\n window.fetch = realFetch\n})\n\nconst bodyOf = (el: OxDoc) => el.shadowRoot!.querySelector('.body') as HTMLElement\n\ndescribe('ox-doc', () => {\n it('reads /helps/{topic}.html with the language and renders it', async () => {\n const el = await fixture<OxDoc>(html`<ox-doc topic=\"a/one\"></ox-doc>`)\n await waitUntil(() => bodyOf(el)?.querySelector('h1'))\n\n expect(requests[0]).to.match(/^\\/helps\\/a\\/one\\.html\\?lng=/)\n expect(bodyOf(el).querySelector('h1')!.textContent).to.equal('One')\n })\n\n it('wraps tables in a scrolling box so they do not push the page wide', async () => {\n const el = await fixture<OxDoc>(html`<ox-doc topic=\"a/one\"></ox-doc>`)\n await waitUntil(() => bodyOf(el)?.querySelector('table'))\n\n expect(bodyOf(el).querySelector('table')!.parentElement!.classList.contains('tablewrap')).to.equal(true)\n })\n\n it('shows only the named section, up to the next heading of its level', async () => {\n const el = await fixture<OxDoc>(html`<ox-doc topic=\"a/one\" section=\"part\"></ox-doc>`)\n await waitUntil(() => bodyOf(el)?.querySelector('h2'))\n\n expect(bodyOf(el).textContent).to.contain('in part')\n expect(bodyOf(el).textContent).to.not.contain('lead')\n expect(bodyOf(el).textContent).to.not.contain('after')\n })\n\n it('follows a topic link in place and tells the host, without keeping the anchor in the file name', async () => {\n const el = await fixture<OxDoc>(html`<ox-doc topic=\"a/one\"></ox-doc>`)\n await waitUntil(() => bodyOf(el)?.querySelector('a[data-topic]'))\n\n let told: any\n el.addEventListener('ox-doc-topic', e => (told = (e as CustomEvent).detail))\n ;(bodyOf(el).querySelector('a[data-topic]') as HTMLElement).click()\n await waitUntil(() => bodyOf(el)?.querySelector('h1')?.textContent === 'Two')\n\n expect(told).to.deep.equal({ topic: 'a/two', hash: 'end' })\n expect(requests[requests.length - 1]).to.match(/^\\/helps\\/a\\/two\\.html\\?/)\n })\n\n it('says the topic is missing instead of showing an empty page', async () => {\n const el = await fixture<OxDoc>(html`<ox-doc topic=\"a/none\"></ox-doc>`)\n await waitUntil(() => el.shadowRoot!.querySelector('.status.err'))\n\n expect(el.shadowRoot!.querySelector('.status.err')!.textContent).to.contain('a/none')\n })\n})\n\ndescribe('ox-help-spot', () => {\n it('opens the section in place and closes on Escape', async () => {\n const el = await fixture<HTMLElement>(html`<ox-help-spot topic=\"a/one\" section=\"part\"></ox-help-spot>`)\n ;(el.shadowRoot!.querySelector('button.q') as HTMLButtonElement).click()\n await waitUntil(() => el.shadowRoot!.querySelector('ox-doc'))\n\n const doc = el.shadowRoot!.querySelector('ox-doc') as OxDoc\n expect(doc.getAttribute('section')).to.equal('part')\n expect(doc.hasAttribute('compact')).to.equal(true)\n\n el.shadowRoot!.querySelector('.panel')!.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, composed: true }))\n await waitUntil(() => !el.shadowRoot!.querySelector('.panel'))\n })\n})\n"]}
|