@designbasekorea/ui-wc 0.13.0 → 0.15.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 +16 -0
- package/LICENSE +21 -0
- package/README.en.md +45 -0
- package/README.md +13 -5
- package/THIRD-PARTY-NOTICES.md +5 -0
- package/dist/esm/components/db-ad-banner.d.ts +6 -0
- package/dist/esm/components/db-ad-banner.js +1 -1
- package/dist/esm/components/db-animation-background.d.ts +7 -0
- package/dist/esm/components/db-animation-background.js +1 -1
- package/dist/esm/components/db-avatar.d.ts +2 -0
- package/dist/esm/components/db-avatar.js +1 -1
- package/dist/esm/components/db-banner.d.ts +2 -0
- package/dist/esm/components/db-banner.js +1 -1
- package/dist/esm/components/db-bottom-navigation.d.ts +2 -0
- package/dist/esm/components/db-bottom-navigation.js +1 -1
- package/dist/esm/components/db-card.d.ts +2 -0
- package/dist/esm/components/db-card.js +1 -1
- package/dist/esm/components/db-carousel.d.ts +2 -0
- package/dist/esm/components/db-carousel.js +1 -1
- package/dist/esm/components/db-code-block.d.ts +3 -0
- package/dist/esm/components/db-code-block.js +1 -1
- package/dist/esm/components/db-color-picker.d.ts +5 -0
- package/dist/esm/components/db-color-picker.js +1 -1
- package/dist/esm/components/db-color-swatch.d.ts +2 -0
- package/dist/esm/components/db-color-swatch.js +1 -1
- package/dist/esm/components/db-confirm.d.ts +2 -0
- package/dist/esm/components/db-confirm.js +1 -1
- package/dist/esm/components/db-context-menu.d.ts +6 -2
- package/dist/esm/components/db-context-menu.js +1 -1
- package/dist/esm/components/db-cookie-banner.d.ts +2 -0
- package/dist/esm/components/db-cookie-banner.js +1 -1
- package/dist/esm/components/db-drawer.d.ts +2 -0
- package/dist/esm/components/db-drawer.js +1 -1
- package/dist/esm/components/db-dropdown.d.ts +2 -0
- package/dist/esm/components/db-dropdown.js +1 -1
- package/dist/esm/components/db-easing-editor.d.ts +5 -0
- package/dist/esm/components/db-empty-state.d.ts +2 -0
- package/dist/esm/components/db-empty-state.js +1 -1
- package/dist/esm/components/db-file-uploader.d.ts +4 -0
- package/dist/esm/components/db-file-uploader.js +1 -1
- package/dist/esm/components/db-footer.d.ts +2 -0
- package/dist/esm/components/db-footer.js +1 -1
- package/dist/esm/components/db-gantt.d.ts +2 -0
- package/dist/esm/components/db-gantt.js +1 -1
- package/dist/esm/components/db-image-list.d.ts +3 -0
- package/dist/esm/components/db-image-list.js +1 -1
- package/dist/esm/components/db-keyframe-editor.d.ts +5 -0
- package/dist/esm/components/db-layer-list.d.ts +71 -0
- package/dist/esm/components/db-layer-list.js +1 -0
- package/dist/esm/components/db-lightbox.d.ts +4 -0
- package/dist/esm/components/db-lightbox.js +1 -1
- package/dist/esm/components/db-list.d.ts +2 -0
- package/dist/esm/components/db-list.js +1 -1
- package/dist/esm/components/db-menu-item.d.ts +2 -0
- package/dist/esm/components/db-menu-item.js +1 -1
- package/dist/esm/components/db-modal.d.ts +3 -0
- package/dist/esm/components/db-modal.js +1 -1
- package/dist/esm/components/db-navbar.d.ts +5 -0
- package/dist/esm/components/db-navbar.js +1 -1
- package/dist/esm/components/db-onboarding-modal.d.ts +3 -0
- package/dist/esm/components/db-onboarding-modal.js +1 -1
- package/dist/esm/components/db-page-header.d.ts +3 -0
- package/dist/esm/components/db-page-header.js +1 -1
- package/dist/esm/components/db-pagination.d.ts +2 -0
- package/dist/esm/components/db-pagination.js +1 -1
- package/dist/esm/components/db-popover.d.ts +2 -0
- package/dist/esm/components/db-popover.js +1 -1
- package/dist/esm/components/db-search-bar.d.ts +3 -0
- package/dist/esm/components/db-search-bar.js +1 -1
- package/dist/esm/components/db-section-app-download.d.ts +2 -0
- package/dist/esm/components/db-section-app-download.js +1 -1
- package/dist/esm/components/db-section-contact.d.ts +2 -0
- package/dist/esm/components/db-section-contact.js +1 -1
- package/dist/esm/components/db-section-cta.d.ts +3 -0
- package/dist/esm/components/db-section-cta.js +1 -1
- package/dist/esm/components/db-section-faq.d.ts +2 -0
- package/dist/esm/components/db-section-faq.js +1 -1
- package/dist/esm/components/db-section-feature.d.ts +3 -0
- package/dist/esm/components/db-section-feature.js +1 -1
- package/dist/esm/components/db-section-hero.d.ts +2 -0
- package/dist/esm/components/db-section-hero.js +1 -1
- package/dist/esm/components/db-section-logo-cloud.d.ts +2 -0
- package/dist/esm/components/db-section-logo-cloud.js +1 -1
- package/dist/esm/components/db-section-pricing.d.ts +3 -0
- package/dist/esm/components/db-section-pricing.js +1 -1
- package/dist/esm/components/db-section-stats.d.ts +2 -0
- package/dist/esm/components/db-section-stats.js +1 -1
- package/dist/esm/components/db-section-testimonials.d.ts +4 -0
- package/dist/esm/components/db-section-testimonials.js +1 -1
- package/dist/esm/components/db-section.d.ts +2 -0
- package/dist/esm/components/db-section.js +1 -1
- package/dist/esm/components/db-share.d.ts +5 -0
- package/dist/esm/components/db-share.js +1 -1
- package/dist/esm/components/db-sidebar.d.ts +7 -0
- package/dist/esm/components/db-sidebar.js +1 -1
- package/dist/esm/components/db-stat.d.ts +2 -0
- package/dist/esm/components/db-stat.js +1 -1
- package/dist/esm/components/db-table.d.ts +5 -0
- package/dist/esm/components/db-table.js +1 -1
- package/dist/esm/components/db-testimonial.d.ts +3 -0
- package/dist/esm/components/db-testimonial.js +1 -1
- package/dist/esm/components/db-time-picker.d.ts +1 -0
- package/dist/esm/components/db-time-picker.js +1 -1
- package/dist/esm/components/db-top-banner.d.ts +3 -0
- package/dist/esm/components/db-top-banner.js +1 -1
- package/dist/esm/components/db-tutorial.d.ts +3 -0
- package/dist/esm/components/db-tutorial.js +1 -1
- package/dist/esm/components/db-video-timeline.d.ts +34 -2
- package/dist/esm/components/db-video-timeline.js +1 -1
- package/dist/esm/components/db-world-map.d.ts +5 -0
- package/dist/esm/components/media-editor-model.d.ts +10 -1
- package/dist/esm/core/i18n-en.js +1 -1
- package/dist/esm/core/i18n.d.ts +11 -0
- package/dist/esm/core/i18n.js +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +619 -486
- package/dist/index.esm.js +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.d.ts +47 -3
- package/dist/react/index.esm.js +1 -1
- package/docs/ACCORDION.md +17 -0
- package/docs/ACHIEVEMENTS.md +15 -0
- package/docs/AI-ELEMENTS.md +86 -0
- package/docs/APP-UPDATE.md +60 -0
- package/docs/ATTACHMENTS.md +32 -0
- package/docs/BUTTON.md +17 -0
- package/docs/CDN-GUIDE.md +82 -0
- package/docs/COMPONENT-CHECKLIST.md +98 -0
- package/docs/CONTEXT-MENU.md +22 -0
- package/docs/CREDIT-AI.md +136 -0
- package/docs/CREDIT-CARD.md +52 -0
- package/docs/CROSS_PLATFORM_COMPONENT_STRATEGY.md +81 -0
- package/docs/EASING-EDITOR.md +42 -0
- package/docs/EFFECT-KEYFRAMES.md +60 -0
- package/docs/LAYER-LIST.md +31 -0
- package/docs/MEDIA-EDITORS.md +63 -0
- package/docs/MODULE-REUSE.md +43 -0
- package/docs/MULTI-PLATFORM-GUIDE.md +246 -0
- package/docs/NOTIFICATIONS.md +23 -0
- package/docs/PAPER-CARDS.md +61 -0
- package/docs/QUALITY-GUIDELINES.md +196 -0
- package/docs/RANGE-SLIDER.md +29 -0
- package/docs/REACT-WC-INTEGRATION.md +96 -0
- package/docs/RECENT-COMPONENTS-QUALITY.md +96 -0
- package/docs/SELECT.md +26 -0
- package/docs/SELECTION-CONTROLS.md +33 -0
- package/docs/SITE-SHELL.md +37 -0
- package/docs/STYLING.md +60 -0
- package/docs/SUPPORT.md +39 -0
- package/docs/TABS.md +19 -0
- package/docs/TEXT-FIELDS.md +28 -0
- package/docs/TIME-PICKER-DEVELOPMENT.md +115 -0
- package/docs/TOGGLE.md +40 -0
- package/docs/TRAVEL.md +30 -0
- package/docs/UI-NATIVE.md +90 -0
- package/docs/VIDEO-EXPORT.md +12 -0
- package/docs/VOICE-INPUT.md +24 -0
- package/docs/VUE-ANGULAR-INTEGRATION.md +197 -0
- package/docs/WORDPRESS-INTEGRATION.md +250 -0
- package/docs/WORKSPACE.md +27 -0
- package/docs/quality/AXE-REPORT.md +35 -0
- package/docs/quality/CREDIT-AI-RELEASE-2026-10-18.md +34 -0
- package/docs/quality/ENUM-MIGRATION-WORKSHEET.md +288 -0
- package/docs/quality/SCORECARD.md +389 -0
- package/package.json +136 -130
package/docs/STYLING.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# ui-wc 스타일 전략
|
|
2
|
+
|
|
3
|
+
## 결정: Light DOM + 글로벌 CSS 변수
|
|
4
|
+
|
|
5
|
+
`@designbasekorea/ui-wc`는 **Shadow DOM을 사용하지 않습니다.**
|
|
6
|
+
|
|
7
|
+
### 이유
|
|
8
|
+
|
|
9
|
+
| 항목 | Light DOM (채택) | Shadow DOM |
|
|
10
|
+
|------|------------------|------------|
|
|
11
|
+
| `--db-*` 토큰 상속 | `html`/`body`의 theme.css에서 자동 상속 | `:host`·`adoptedStyleSheets` 별도 주입 필요 |
|
|
12
|
+
| 폼 연동 | 네이티브 `<input>`·`<button>` 그대로 | 폼 제출·autofill 이슈 |
|
|
13
|
+
| 기존 React SCSS | 클래스명·토큰 참조 패턴 재사용 용이 | 스타일 전면 이전 |
|
|
14
|
+
| 다크 모드 | `data-theme` 한 곳에서 전체 반영 | WC마다 테마 동기화 필요 |
|
|
15
|
+
|
|
16
|
+
### 구현 방식
|
|
17
|
+
|
|
18
|
+
1. 앱 루트에서 **필수 CSS 3종** 로드:
|
|
19
|
+
- `@designbasekorea/tokens/dist/css/tokens.css`
|
|
20
|
+
- `@designbasekorea/theme/dist/css/theme.css`
|
|
21
|
+
- `@designbasekorea/ui-wc/dist/index.css`
|
|
22
|
+
|
|
23
|
+
2. 각 컴포넌트는 `extends HTMLElement` 로 직접 구현하며, 렌더 결과를 자기 자신(호스트) 아래에
|
|
24
|
+
그대로 붙인다 — Shadow root 를 만들지 않는다. (Lit 은 스토리 작성에만 쓰이며 런타임 의존성이 아니다.)
|
|
25
|
+
|
|
26
|
+
3. 스타일 클래스 접두사: `designbase-wc-*` (React `designbase-*`와 충돌 방지).
|
|
27
|
+
|
|
28
|
+
> Light DOM 이라 캡슐화가 없다. **접두사가 전역 CSS 충돌을 막는 유일한 수단**이므로
|
|
29
|
+
> 예외 없이 지켜야 한다 — 품질 규격의 `STD-LIGHT-DOM` 규칙으로 자동 검사된다.
|
|
30
|
+
|
|
31
|
+
4. 모든 색·간격·타이포는 `var(--db-*)` 참조 — React UI와 동일 토큰.
|
|
32
|
+
|
|
33
|
+
### SCSS 소스
|
|
34
|
+
|
|
35
|
+
- 소스 저장소의 `src/styles/components.scss`에서 컴포넌트별 partial을 통합한다. SCSS 원본은 npm 배포물에 포함하지 않는다.
|
|
36
|
+
- npm에서는 빌드된 [`dist/index.css`](../dist/index.css) 단일 파일을 가져와 사용한다.
|
|
37
|
+
|
|
38
|
+
### Shadow DOM이 필요한 경우 (향후)
|
|
39
|
+
|
|
40
|
+
Popover·Tooltip이 document 전체와 겹칠 때만 **선택적** Shadow DOM 또는 `popover` API 검토. Phase 1 MVP는 Light DOM 유지.
|
|
41
|
+
|
|
42
|
+
### 접근성 관련 CSS 규약
|
|
43
|
+
|
|
44
|
+
품질 규격([QUALITY-GUIDELINES.md](./QUALITY-GUIDELINES.md))이 SCSS 에 요구하는 것:
|
|
45
|
+
|
|
46
|
+
| 규칙 | 내용 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `A11Y-FOCUS-RING` | 포커스 링은 `:focus-visible` 에만 준다. 링 색은 `--db-border-focus` 하나로 통일 |
|
|
49
|
+
| `A11Y-REDUCED-MOTION` | 200ms 이상 움직이는 전환은 `@media (prefers-reduced-motion: reduce)` 에서 축소 |
|
|
50
|
+
| `A11Y-NAME` | 시각적으로 감출 텍스트는 `.designbase-wc-sr-only` (`src/styles/_a11y.scss`) 사용 |
|
|
51
|
+
|
|
52
|
+
공용 믹스인은 `src/styles/_a11y.scss` 에 있다:
|
|
53
|
+
|
|
54
|
+
```scss
|
|
55
|
+
@use '../styles/a11y' as a11y;
|
|
56
|
+
|
|
57
|
+
.designbase-wc-button {
|
|
58
|
+
&:focus-visible { @include a11y.focus-ring; }
|
|
59
|
+
}
|
|
60
|
+
```
|
package/docs/SUPPORT.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Support and verification scope
|
|
2
|
+
|
|
3
|
+
The Web Components and React registration entry points require a browser DOM. They are not server-rendered custom-element implementations. Keep CSS in the framework's global style entry and load component registration only on the client. A React client lifecycle can defer the runtime import:
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
type Controls = typeof import('@designbasekorea/ui-wc/react');
|
|
9
|
+
export function ClientControls() {
|
|
10
|
+
const [controls, setControls] = useState<Controls | null>(null);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
let active = true;
|
|
13
|
+
void import('@designbasekorea/ui-wc/react').then(value => {
|
|
14
|
+
if (active) setControls(value);
|
|
15
|
+
});
|
|
16
|
+
return () => { active = false; };
|
|
17
|
+
}, []);
|
|
18
|
+
if (!controls) return null;
|
|
19
|
+
const { Button } = controls;
|
|
20
|
+
return <Button size="m" variant="secondary">Continue</Button>;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This pattern avoids executing custom-element modules during server rendering. Your framework may impose additional client-boundary and CSS-loading rules. React Native uses a different rendering runtime and cannot render these HTML custom elements as native controls.
|
|
25
|
+
|
|
26
|
+
## Current repository validation
|
|
27
|
+
|
|
28
|
+
| Area | Verified scope |
|
|
29
|
+
|---|---|
|
|
30
|
+
| Browser | Automated Chromium checks; targeted keyboard and interaction flows |
|
|
31
|
+
| React consumer | React 19.2.3 in isolated npm consumers; package peer ranges are not a substitute for testing every framework/version |
|
|
32
|
+
| Tooling | Node 22.22.2 validation; delivered quickstarts require Node 20+ and use Vite 6.4.5 |
|
|
33
|
+
| Accessibility | Light/dark axe fixtures and selected keyboard scenarios; not a WCAG certification of every component/state |
|
|
34
|
+
| Responsive layout | Targeted mobile/desktop and breakpoint checks; complex canvases/tables may intentionally scroll within their own region |
|
|
35
|
+
| SSR | Client-only registration boundary; no full server-rendered custom-element or hydration guarantee |
|
|
36
|
+
| Safari / Firefox / assistive technology | Full release qualification has not been established by the current Chromium suite |
|
|
37
|
+
| Email templates | Browser previews do not establish Gmail/Outlook/Apple Mail rendering equivalence |
|
|
38
|
+
|
|
39
|
+
Consult the shipped accessibility report for its fixture count and date. A native control with a valid label can still need application-specific instructions, contrast, focus order or error association. Test your integrated form and keyboard path, not only isolated tags. Respect reduced-motion preferences and provide meaningful fallback content for WebGL/canvas features.
|
package/docs/TABS.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Tabs: selection, keyboard and projected panels
|
|
2
|
+
|
|
3
|
+
Import tokens, theme and `@designbasekorea/ui-wc/dist/index.css`. React imports `Tabs` from `@designbasekorea/ui-wc/react`; Vanilla imports `DbTabs` from `@designbasekorea/ui-wc/components/db-tabs`.
|
|
4
|
+
|
|
5
|
+
Use items with a label, optional unique stable id, disabled and official icon name. labels also accepts a string array, a JSON array attribute, or the legacy comma-separated string. Array labels preserve embedded commas. Valid items take priority over labels. Invalid/empty entries are ignored. Missing ids fall back to position; duplicate keys receive #2/#3 suffixes. Stable unique ids preserve DOM identity on reorder.
|
|
6
|
+
|
|
7
|
+
activeIndex and selectedId getters describe the actual enabled selection. Native property setters and attribute updates synchronize the two fields; updating activeIndex can override a former selected-id. Initial markup resolves selected-id before index. Invalid/non-finite/fractional/out-of-bounds indices normalize to an enabled entry; no enabled entry yields -1 and an empty selectedId. Programmatic updates emit no change. A user selecting a different tab emits one bubbling/composed db-change with `{ index, label, id }`. Clicking the selected tab emits none. React onChange/onTabChange/onDbChange receive only the Tabs host's own change, not a nested control's event. Use one handler unless multiple aliases are intentional.
|
|
8
|
+
|
|
9
|
+
For React control, pass activeIndex or selectedId and apply change detail to state. When both are supplied, selectedId takes precedence. The ref points to the actual native element, with focus() and refresh(). No server persistence is built in; the host owns saving and async state.
|
|
10
|
+
|
|
11
|
+
Keyboard activation is manual: arrows move focus without selecting; Enter/Space activate the focused native tab. Home/End move to enabled endpoints. Horizontal lists use Left/Right (including RTL); vertical lists use Up/Down. The other axis retains page scrolling. Disabled entries are skipped. Roving tabindex follows the focused tab, and focus/selection are preserved through style/content updates. Only actual selection moves the indicator; focus has its own visible outline. The list scrolls to expose keyboard targets. Scrollable lists also support mouse/pen drag, with a movement threshold that suppresses accidental selection. Touch keeps native panning. Cancel, disconnect and layout updates release pointer capture; the final pointerup position is retained.
|
|
12
|
+
|
|
13
|
+
Supply top-level slot="panel-N" children for panels, where N is zero-based. Legacy data-panel-index also works in Vanilla. Each tab/panel has independent instance IDs and local aria-controls/labelledby. Inactive panels stay in the DOM, hidden and inert, preserving live input state. With no supplied panels, Tabs remains navigation-only without an empty content box or aria-controls. Native unassigned slot nodes are retained in a hidden inert stash. refresh() rechecks slot structure without emitting selection changes.
|
|
14
|
+
|
|
15
|
+
React content is rendered through portals, so React owns children and cleanup while native code owns tab/panel chrome. Target and child updates commit together; keyed child state and inputs survive tab switches and reordering. Fragments containing top-level slotted children are supported. Removing panel children restores navigation-only mode without removeChild errors.
|
|
16
|
+
|
|
17
|
+
Inactive inputs still participate in a surrounding form, as ordinary hidden tab panels do. The host must reveal an invalid panel before focusing its fields, and disable fields that should not participate in validation/submission. Tabs does not decide those business rules.
|
|
18
|
+
|
|
19
|
+
orientation stays horizontal or vertical across screen sizes. Vertical lists adapt their allocated width and wrap labels; horizontal lists can scroll internally. Targets are at least 44px, including pills/small size. Semantic surfaces color the overflow fade in light and dark themes. ResizeObserver, font loading and scroll position update the indicator; observers, font/window listeners and animation frames are cleaned up on disconnect. Reduced motion removes transitions.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Input and Textarea
|
|
2
|
+
|
|
3
|
+
Install `@designbasekorea/ui-wc`, load the shipped token/theme and component styles, then import `Input`/`Textarea` from `@designbasekorea/ui-wc/react`. Vanilla code can import the individual `components/db-input` and `components/db-textarea` exports. No source alias is needed.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Input, Textarea } from '@designbasekorea/ui-wc/react';
|
|
7
|
+
|
|
8
|
+
export function MessageForm() {
|
|
9
|
+
return <form onSubmit={event => {
|
|
10
|
+
event.preventDefault();
|
|
11
|
+
const values = new FormData(event.currentTarget);
|
|
12
|
+
console.log(values.get('email'), values.get('message'));
|
|
13
|
+
}}>
|
|
14
|
+
<Input name="email" label="Email" type="email" autoComplete="email" required />
|
|
15
|
+
<Textarea name="message" label="Message" rows={4} maxLength={500} showCharacterCount />
|
|
16
|
+
<button type="reset">Start over</button>
|
|
17
|
+
<button type="submit">Save</button>
|
|
18
|
+
</form>;
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`value` is the current value. `defaultValue` (`default-value` in HTML) is the reset target. With no explicit default, the first configured `value` before user editing becomes the reset target, including values supplied by React during mount. Changing `defaultValue` after editing preserves the current text. `form.reset()` restores the default, helper/count display and submitted value without dispatching user input/change events. A canceled reset preserves the current value. Disconnecting and reconnecting a field updates its form listener; `form="other-form-id"` also associates the actual native input.
|
|
23
|
+
|
|
24
|
+
For React controlled fields, pass a string `value` and update host state from `onInput`/`onChange`/`onDbInput` (each keystroke), or use `onDbChange` for the committed native change. Standard React DOM handlers such as `onFocus`, `onBlur` and `onKeyDown` retain their usual React event types. A native form reset does not rewrite React state: reset the state in your form's `onReset` handler as well. Event details are `{ value: string, nativeEvent: Event | null }`; clear buttons use `null` for the native event. Custom events bubble and are composed. `focus(options?)`, `name`, `required`, `disabled`, `readOnly`, `value` and `defaultValue` are public properties. Properties assigned before custom-element registration are upgraded on connection.
|
|
25
|
+
|
|
26
|
+
Use `label` for a visible name, or `aria-label`/`aria-labelledby` for a separately provided name. External `aria-describedby` IDs combine with the current helper/error IDs. `autoFocus` focuses once when the field first becomes available. `autoComplete`, `inputMode`, `enterKeyHint`, `spellCheck`, `autoCapitalize` and length limits reach the native control. HTML spellcheck="false" and React spellCheck={false} both disable checking. `disabled` fields are excluded from FormData; `readOnly` fields remain submitted.
|
|
27
|
+
|
|
28
|
+
Invalid sizes resolve to `m`; unsupported input types resolve to `text`. Textarea rows must be a positive integer (fallback 4). Blank, negative or noninteger length limits are ignored rather than throwing. Native email, required, pattern and length constraints perform browser validation; the host owns persistence, authentication, submission errors and backend delivery. The password visibility control changes display only. Search clear resets the text and emits `db-input` (with a null nativeEvent) followed by `db-clear`. A committed native change remains a separate event.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# TimePicker 개발 가이드
|
|
2
|
+
|
|
3
|
+
`db-time-picker`를 직접 수정하고 Storybook에서 확인하는 방법입니다.
|
|
4
|
+
|
|
5
|
+
## 1. 개발 서버 실행
|
|
6
|
+
|
|
7
|
+
프로젝트 루트에서 실행합니다.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd "/Users/designbase/디자인베이스-라이브러리/Designbase Library"
|
|
11
|
+
npm --prefix packages/ui-wc run storybook
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
브라우저에서 다음 주소를 엽니다.
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
http://127.0.0.1:6007/?path=/story/web-components-timepicker--responsive
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
서버를 종료할 때는 터미널에서 `Ctrl + C`를 누릅니다. 파일을 저장하면 Storybook이
|
|
21
|
+
자동으로 다시 반영합니다.
|
|
22
|
+
|
|
23
|
+
포트 6007이 이미 사용 중이면 다음처럼 다른 포트를 사용할 수 있습니다.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd packages/ui-wc
|
|
27
|
+
../../node_modules/.bin/storybook dev -p 6010 --ci --no-version-updates
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 2. 수정할 파일
|
|
31
|
+
|
|
32
|
+
| 파일 | 수정 내용 |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `src/components/db-time-picker.ts` | 시간 파싱, 휠 선택, 열림/닫힘, 드롭다운 위치 계산 |
|
|
35
|
+
| `src/components/db-time-picker.scss` | 트리거, 패널, 휠, 반응형 사이즈 스타일 |
|
|
36
|
+
| `src/components/db-time-picker.stories.ts` | Storybook 예시 추가·수정 |
|
|
37
|
+
| `src/react/index.ts` | React wrapper의 `TimePickerProps` 타입 수정 |
|
|
38
|
+
| `../ui-contracts/src/components.ts` | 공유 API 값 추가·변경 시 수정 |
|
|
39
|
+
|
|
40
|
+
일반적인 시각·동작 수정은 `db-time-picker.ts`와 `db-time-picker.scss`만 수정하면 됩니다.
|
|
41
|
+
Storybook은 `src/styles/components.scss`를 통해 SCSS 원본을 사용합니다.
|
|
42
|
+
|
|
43
|
+
`db-time-picker.css`는 SCSS에서 생성되는 배포용 CSS입니다. 직접 수정하기보다 다음
|
|
44
|
+
명령으로 생성합니다.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx sass --no-source-map \
|
|
48
|
+
packages/ui-wc/src/components/db-time-picker.scss \
|
|
49
|
+
packages/ui-wc/src/components/db-time-picker.css
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 3. Storybook 예시 위치
|
|
53
|
+
|
|
54
|
+
- `Default`: 기본 동작
|
|
55
|
+
- `Placement`: `bottom-start`, `bottom-end` 배치
|
|
56
|
+
- `Responsive`: 좁은 영역과 `auto` 배치
|
|
57
|
+
- `Sizes`: S/M/L 패널 크기
|
|
58
|
+
|
|
59
|
+
직접 만든 예시는 `db-time-picker.stories.ts`에 추가합니다.
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<db-time-picker
|
|
63
|
+
label="예약 시간"
|
|
64
|
+
value="12:00"
|
|
65
|
+
size="m"
|
|
66
|
+
format="24h"
|
|
67
|
+
minute-step="5"
|
|
68
|
+
dropdown-position="auto"
|
|
69
|
+
></db-time-picker>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`dropdown-position` 값은 다음과 같습니다.
|
|
73
|
+
|
|
74
|
+
| 값 | 의미 |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `auto` | 뷰포트 공간을 계산해 위/아래와 좌/우를 자동 선택 |
|
|
77
|
+
| `bottom-start` | 트리거 왼쪽을 기준으로 아래에 표시 |
|
|
78
|
+
| `bottom-end` | 트리거 오른쪽을 기준으로 아래에 표시 |
|
|
79
|
+
| `top-start` | 트리거 왼쪽을 기준으로 위에 표시 |
|
|
80
|
+
| `top-end` | 트리거 오른쪽을 기준으로 위에 표시 |
|
|
81
|
+
|
|
82
|
+
## 4. 수정 후 검사
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# ui-contracts를 수정한 경우
|
|
86
|
+
npm --prefix packages/ui-contracts run build
|
|
87
|
+
|
|
88
|
+
# ui-wc 타입 검사
|
|
89
|
+
npm --prefix packages/ui-wc run typecheck
|
|
90
|
+
|
|
91
|
+
# React Native 공유 계약을 함께 확인
|
|
92
|
+
npm --prefix packages/ui-native run typecheck
|
|
93
|
+
|
|
94
|
+
# 전체 Web Component 품질 검사
|
|
95
|
+
npm --prefix packages/ui-wc run verify:quality
|
|
96
|
+
|
|
97
|
+
# 포맷·공백 검사
|
|
98
|
+
git diff --check
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
API 계약을 변경하지 않았다면 `ui-contracts build`와 `ui-native typecheck`는 생략해도
|
|
102
|
+
됩니다. 다만 `dropdown-position`처럼 공개 속성 값을 추가하거나 변경할 때는 두 패키지를
|
|
103
|
+
함께 확인해야 합니다.
|
|
104
|
+
|
|
105
|
+
## 5. 추천 수정 순서
|
|
106
|
+
|
|
107
|
+
1. `db-time-picker.stories.ts`에 재현 가능한 예시를 먼저 추가합니다.
|
|
108
|
+
2. `db-time-picker.scss`에서 폭·간격·높이를 조정합니다.
|
|
109
|
+
3. `db-time-picker.ts`에서 동작이나 배치 계산을 수정합니다.
|
|
110
|
+
4. 좁은 컬럼, 오른쪽 가장자리, 화면 하단에서 확인합니다.
|
|
111
|
+
5. 타입체크와 품질 검사를 실행합니다.
|
|
112
|
+
|
|
113
|
+
드롭다운은 열릴 때 `body`로 포털되므로 부모 컨테이너의 `overflow: hidden`에 의해
|
|
114
|
+
잘리지 않습니다. 대신 트리거 위치가 바뀌면 다시 계산되므로, 새 배치 로직을 추가할 때는
|
|
115
|
+
`resize`와 스크롤 상황도 함께 확인해야 합니다.
|
package/docs/TOGGLE.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Toggle: forms, state and accessible switches
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
import '@designbasekorea/tokens/dist/css/tokens.css';
|
|
5
|
+
import '@designbasekorea/theme/dist/css/theme.css';
|
|
6
|
+
import '@designbasekorea/ui-wc/dist/index.css';
|
|
7
|
+
import { Toggle } from '@designbasekorea/ui-wc/react';
|
|
8
|
+
|
|
9
|
+
<form onSubmit={event => {
|
|
10
|
+
event.preventDefault();
|
|
11
|
+
console.log(new FormData(event.currentTarget).get('updates'));
|
|
12
|
+
}}>
|
|
13
|
+
<Toggle name="updates" value="yes" defaultChecked>Email updates</Toggle>
|
|
14
|
+
<button type="submit">Save preferences</button>
|
|
15
|
+
</form>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Vanilla imports `DbToggle` from `@designbasekorea/ui-wc/components/db-toggle` to register `db-toggle`. It renders a native button with `role="switch"` and `aria-checked`. Space and Enter change the state; Tab leaves normally. `focus({ preventScroll: true })` targets the real switch. Readonly remains focusable but blocks pointer, keyboard and programmatic button clicks. Disabled controls and disabled fieldsets block interaction and submission. Re-enabling a fieldset preserves the explicit `disabled` attribute.
|
|
19
|
+
|
|
20
|
+
`checked` and its `selected` alias represent current state. Assigning false clears both current aliases, including a `default-checked` initialization. Initial HTML `checked`, `selected`, `default-checked` and `default-selected` are supported. `defaultChecked` / `defaultSelected` set the reset target. Without an explicit default, initial markup or the first configured checked value becomes the reset target. Programmatic state changes after that do not change the reset target. Changing a default updates a pristine switch; after interaction it changes only the reset target. `form.reset()` restores that default without emitting a user event. A canceled reset preserves the current state.
|
|
21
|
+
|
|
22
|
+
A checked, named switch participates in native `FormData`. An omitted value submits `on`; an explicitly empty value submits an empty string. Unchecked, unnamed and disabled switches are excluded. The `form` attribute associates an external form by ID. `required` participates in native validity; `form`, `validity`, `validationMessage`, `willValidate`, `checkValidity()` and `reportValidity()` delegate to ElementInternals. Readonly is excluded from the required-state check. This UI stores no preferences remotely: the host owns persistence, loading, errors and submission results.
|
|
23
|
+
|
|
24
|
+
For a controlled React switch, update your state in `onChange`. A controlled form should restore that state in `onReset` too:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
const [enabled, setEnabled] = useState(false);
|
|
28
|
+
<form onReset={() => setEnabled(false)}>
|
|
29
|
+
<Toggle checked={enabled} onChange={event => setEnabled(event.detail.checked)}>
|
|
30
|
+
Email updates
|
|
31
|
+
</Toggle>
|
|
32
|
+
<button type="reset">Reset</button>
|
|
33
|
+
</form>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`onChange` / `onDbChange` receives one bubbling, composed `db-change` per user action, with `{ checked: boolean, selected: boolean }`. A native bubbling `change` is also emitted. Setting properties, defaults, restoring state or resetting emits neither event. A repeated connection does not add duplicate listeners. Pre-registration own properties are upgraded when the element connects.
|
|
37
|
+
|
|
38
|
+
Visible children label the switch. The React adapter forwards plain text children through `label` so changing the text preserves the native switch; an explicit `label` takes precedence. Noninteractive rich markup stays in a stable React-owned slot across plain/rich format changes and explicit overrides. Vanilla can update the `label` property/attribute after connection without replacing its internal DOM. With `hasLabel={false}`, supply `aria-label` or `aria-labelledby`; hidden label text is a fallback name when no explicit name is given. External `aria-describedby`, `aria-description` and `aria-invalid` reach the actual button. State uses `aria-checked`, not `aria-pressed`. Use noninteractive phrasing content (text, emphasis or icons) for switch labels. Interactive links/buttons do not belong inside the switch.
|
|
39
|
+
|
|
40
|
+
`size` is `s | m | l`, falling back to `m`. `variant` is `default | neutral`, falling back to `default`. Instance CSS variables such as `--db-toggle-track-bg` customize the control. Neutral state uses semantic text/surface tokens and follows the selected theme. Fine pointers have at least 24px targets; coarse pointers have at least 44px targets. Long labels wrap inside the available width, reduced-motion removes transitions and forced-colors retains the track and selected state.
|
package/docs/TRAVEL.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Travel components
|
|
2
|
+
|
|
3
|
+
`FlightStatus` / `db-flight-status` displays caller-supplied flight data in a compact, responsive surface. It does not fetch flight data or run a simulated live clock.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { FlightStatus, BoardingPassStack } from '@designbasekorea/ui-wc/react';
|
|
7
|
+
<FlightStatus airline="Supernormal" serviceNumber="MA 217"
|
|
8
|
+
origin="ICN" destination="NRT" departureTime="09:20" arrivalTime="11:40"
|
|
9
|
+
status="in-flight" progress={56} detail="도착까지 약 1시간" />
|
|
10
|
+
<BoardingPassStack passes={passes} selectedIndex={selected}
|
|
11
|
+
onChange={e => setSelected(e.detail.index)} actionLabel="탑승권 확인"
|
|
12
|
+
onAction={e => openPass(e.detail.reference)} />
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Flight status accepts `airline`, `service-number`, `origin`, `origin-name`, `destination`, `destination-name`, `departure-time`, `arrival-time`, `terminal`, `gate`, `detail`, `label`, `progress` and `status`. Status is `scheduled`, `boarding`, `in-flight`, `landed`, `delayed` or `cancelled`. Progress is clamped to 0–100; invalid numbers fall back to zero. Landed renders 100%, scheduled/cancelled zero. The route exposes progressbar semantics and uses the official Designbase plane icon. All supplied text is plain text. `--db-flight-status-surface` can customize its surface.
|
|
16
|
+
|
|
17
|
+
`BoardingPassStack` / `db-boarding-pass-stack` is a journey wallet built from the real `db-boarding-pass`. Passes have required string fields `id`, `issuer`, `origin`, `destination`; optional fields are `originName`, `destinationName`, `passenger`, `serviceNumber`, `date`, `boardingTime`, `departureTime`, `arrivalTime`, `terminal`, `gate`, `seat`, `travelClass`, `codeImageSrc`. Pass IDs should be unique. Passes may be supplied as the `passes` property or a JSON attribute. Getters return copies. Setting the property to undefined restores the JSON fallback.
|
|
18
|
+
|
|
19
|
+
`selected-index` / `selectedIndex` clamps to available entries. Changing it externally does not emit an event. Clicking a journey or using arrow keys, Home or End emits `db-change` / `onChange` with `{ index, pass }`. The tabs have a linked tabpanel and maintain focus through selection. Empty data shows an empty state. `disabled` disables journey selection and the inner boarding-pass action. The optional `action-label` enables the underlying pass's `db-action` / `onAction` event with `{ reference }`. Caller-owned children survive component updates. Data is never persisted by these display components. Code images must be supplied by the consumer and pass the same safe URL validation as BoardingPass.
|
|
20
|
+
|
|
21
|
+
Both components use system surface/text/border/spacing tokens, fit narrow containers and follow light/dark themes. They introduce no continuous animation. Examples are available at `/components/flightstatus` and `/components/boardingpassstack`.
|
|
22
|
+
|
|
23
|
+
## Interface language
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { dbUseLocale } from '@designbasekorea/ui-wc';
|
|
27
|
+
dbUseLocale('en'); // or 'ko'; apply before mounting the interface
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Flight states, journey progress labels, boarding-pass fields, receipt totals and card accessibility labels follow this setting. Explicit component labels and `dbSetLocale()` overrides take priority. Application-supplied names, identifiers, monetary values and code samples are preserved.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<!-- Generated from packages/ui-native/README.md by scripts/sync-package-docs.mjs. -->
|
|
2
|
+
# @designbasekorea/ui-native
|
|
3
|
+
|
|
4
|
+
Designbase 토큰과 React Native 컴포넌트. 웹의 custom element를 재사용하지 않고 `View`, `Text`, `Pressable`, `TextInput`으로 구현합니다. 공통 variant·size 이름은 `@designbasekorea/ui-contracts`를 사용합니다.
|
|
5
|
+
|
|
6
|
+
## 설치
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install @designbasekorea/ui-native@0.1.1
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
React 18 이상과 React Native 0.72 이상이 peer dependency입니다. 토큰은 의존성으로 설치되므로 앱에서 JSON을 파싱하거나 매핑을 복제할 필요가 없습니다. 이번 변경은 React 19와 RN Web에서 실행 검증했습니다. 모든 지원 버전·기기의 실기기 검증을 의미하지는 않습니다.
|
|
13
|
+
|
|
14
|
+
## 앱에 적용
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { ThemeProvider } from '@designbasekorea/ui-native/theme';
|
|
18
|
+
import { Button, Text, Input, Card } from '@designbasekorea/ui-native';
|
|
19
|
+
import { useTheme } from '@designbasekorea/ui-native/theme';
|
|
20
|
+
import { View } from 'react-native';
|
|
21
|
+
|
|
22
|
+
function Screen() {
|
|
23
|
+
const { colors, space, radii, setMode } = useTheme();
|
|
24
|
+
return (
|
|
25
|
+
<View style={{ backgroundColor: colors.bg01, padding: space.m }}>
|
|
26
|
+
<Card title="프로필">
|
|
27
|
+
<Text tone="secondary">Designbase</Text>
|
|
28
|
+
<Input label="이메일" type="email" />
|
|
29
|
+
</Card>
|
|
30
|
+
<Button variant="accent" onPress={() => setMode('dark')}>다크 모드</Button>
|
|
31
|
+
</View>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
export default function App() {
|
|
35
|
+
return <ThemeProvider defaultMode="system"><Screen /></ThemeProvider>;
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`/theme` 진입점은 컴포넌트 전체를 가져오지 않습니다. `colors.bg01`, `primary`, `text01` 등은 실제 Designbase 앱에서 사용하던 이름의 호환 매핑입니다. 정식 토큰 경로는 `theme.values.color.aliases.surface.base`, `theme.values.color.semantic.button.primary`처럼 접근합니다. `token('spacing.aliases.padding.m')`도 경로별 값 타입을 제공합니다.
|
|
40
|
+
|
|
41
|
+
## 시스템·사용자 선택과 저장
|
|
42
|
+
|
|
43
|
+
`system`은 시스템 변경을 따르고 `light`/`dark`는 시스템 설정을 덮어씁니다. 기존 `auto`도 계속 지원합니다. `defaultMode`와 기존 `theme` prop은 초기값이며, 외부 설정을 복원할 때는 controlled `mode`와 `onModeChange`를 사용합니다.
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<ThemeProvider mode={savedMode} onModeChange={saveMode}>
|
|
47
|
+
<App />
|
|
48
|
+
</ThemeProvider>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
저장소와 초기 복원 로딩 처리는 앱이 담당합니다. 화면에서는 `useTheme().setMode(...)`를 호출합니다. Expo 앱이 네이티브 설정에서 light로 고정되어 있으면 시스템 설정 감지를 위해 `userInterfaceStyle: 'automatic'`과 새 네이티브 빌드가 필요합니다.
|
|
52
|
+
|
|
53
|
+
## 기존 Colors 이전
|
|
54
|
+
|
|
55
|
+
모듈 최상위 `StyleSheet.create({ backgroundColor: Colors.bg01 })`는 고정값입니다. 렌더 함수에서 훅을 사용하고 스타일은 테마를 인자로 생성해야 변경이 반영됩니다.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
const { colors: Colors } = useTheme();
|
|
59
|
+
const styles = useMemo(() => StyleSheet.create({
|
|
60
|
+
screen: { backgroundColor: Colors.bg01 },
|
|
61
|
+
title: { color: Colors.text01 },
|
|
62
|
+
}), [Colors]);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`white`/`black`은 고정 팔레트입니다. 테마별 기본 CTA의 글자는 `ctaText`를 사용하세요. `blue50` 같은 구 이름은 앱의 기존 역할 매핑을 유지하며, 정확한 고정 팔레트가 필요하면 `theme.values.color.foundation`을 사용합니다. `lightTheme`는 정적 값이며 화면에서 자동으로 갱신되지 않습니다.
|
|
66
|
+
|
|
67
|
+
## 비색상 토큰
|
|
68
|
+
|
|
69
|
+
`space`와 `radii`는 숫자입니다. 전체 숫자 척도와 컴포넌트 토큰은 `theme.values`에 있습니다.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { createTextStyle, createShadowStyle } from '@designbasekorea/ui-native/theme';
|
|
73
|
+
const style = createTextStyle(theme.values, 'l', 'semibold');
|
|
74
|
+
const shadow = createShadowStyle(theme.values, 's');
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
웹 폰트 목록은 RN `fontFamily`에 그대로 전달하지 않습니다. `createTextStyle`은 기본 시스템 폰트를 사용하고 lineHeight 배율을 절대 수치로 변환합니다. monospace 선택 시 iOS는 Menlo, Android는 monospace를 사용합니다. 그림자는 offset·elevation으로 변환하며 CSS spread/inset은 근사할 수 없어 지원하지 않습니다. 원본 JSON 모양은 기존 소비자를 위해 유지됩니다.
|
|
78
|
+
|
|
79
|
+
## 기본 컴포넌트 계약
|
|
80
|
+
|
|
81
|
+
| 컴포넌트 | 주요 계약 | 네이티브 동작 |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| Button | primary/secondary/tertiary/destructive/ghost/accent, s/m/l, loading/disabled | onPress, busy/disabled 접근성 상태, 최소 44 터치 높이, 글꼴 확대 시 늘어나는 높이 |
|
|
84
|
+
| Text | 토큰 size, weight, leading, tone | 시스템 폰트·글꼴 확대, RN TextProps 전달 |
|
|
85
|
+
| Input | s/m/l, label, helperText, errorMessage, type | keyboardType/secureTextEntry 매핑, ref로 focus 가능, 명시적 RN props 우선 |
|
|
86
|
+
| Card | default/flat, s/m/l, image/headerRight/footer | clickable+onPress 시 버튼 역할, disabled 상태, 이미지 대체 텍스트 |
|
|
87
|
+
|
|
88
|
+
웹의 hover·DOM 이벤트·CSS 장식 효과와 RN의 press·키보드·스크린리더 API는 플랫폼별로 다릅니다. `accent`는 semantic 단색을 사용하며 gradient/backlight/prism 효과를 제공하지 않습니다. iconOnly 버튼은 접근성 라벨을 지정하세요. 오류 입력은 label과 errorMessage를 제공하세요. Card 안에 여러 상호작용이 있으면 카드 전체 clickable을 중첩하지 마세요.
|
|
89
|
+
|
|
90
|
+
추가로 Badge, Avatar, Chip, Divider, Spinner, Toggle, Alert, Modal, BottomSheet, Tabs, BottomNavigation, Navbar, Checkbox, Radio, SearchBar, Toast, Skeleton, ProgressBar를 기존 API로 제공합니다. 이번 실행 테스트의 핵심 범위는 테마와 Button/Text/Input/Card입니다.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!-- Generated from docs/VIDEO-EXPORT.md by scripts/sync-package-docs.mjs. -->
|
|
2
|
+
# Video export block
|
|
3
|
+
|
|
4
|
+
Canonical source: packages/blocks/src/blocks/video-export.tsx and video-export.meta.ts; styles live in blocks.css. Registered in Tools/design as video-export. Route: /blocks/video-export.
|
|
5
|
+
|
|
6
|
+
Uses actual Modal/ModalBody/ModalFooter, Select, SegmentControl, RangeSlider, Toggle, Input, Badge, Alert, Progress and Button components. Output presets, size, FPS, codec, quality, source fit, range, audio, transparency, loop and filename feed a single VideoExportSettings object. Estimates are approximate; frame preview and Save frame PNG run locally. Work/custom ranges clamp to the provided duration.
|
|
7
|
+
|
|
8
|
+
Connect onExport(settings, {signal, progress}) to the application's encoder. Report progress from 0 to 1, honor AbortSignal, and resolve only when output is delivered. Rejections show retry state. Without an encoder, the block explicitly labels demo progress and does not invent a video download. PNG sequence is an output request for the encoder; Save frame PNG independently captures the current sample composition. Preview assets are generated sample graphics, not user footage.
|
|
9
|
+
|
|
10
|
+
VideoExportProps: title?, duration? (seconds), defaultOpen?, onExport?. Preview copy uses the existing EN/KO locale; generated download source removes locale helpers and dictionaries. Theme surfaces use existing semantic tokens. Modal contents scroll independently at narrow/short screen sizes with footer actions reachable.
|
|
11
|
+
|
|
12
|
+
Validation: blocks TypeScript/build, gallery TypeScript/build; scripts/test-video-export.mjs checks opening, format constraints, progress/completion, cancellation, reopening, Korean switching and 10 light/dark viewport surfaces. Evidence: docs/evidence/video-export-2026-11-06. Download source generated through scripts/generate-registry-json.mjs --only=video-export.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# ADR-044 — 실시간 음성 입력과 제어 가능한 ui-wc 컴포넌트
|
|
2
|
+
|
|
3
|
+
2026-10-20 · 사용자의 요청에 따라 기존 검토 후 삽입 예제를 유지하고, AI 대화/일반 폼의 실시간 입력 경로를 추가한다.
|
|
4
|
+
|
|
5
|
+
## 역할 분리
|
|
6
|
+
|
|
7
|
+
- Starter `createVoiceInput`: 인식 세션/권한 오류/타이머/abort 소유. 브라우저 provider를 기본 사용한다.
|
|
8
|
+
- Starter `bindVoiceText`: UI 없는 입력 트랜잭션. target.read/write만 받아 기존 글 뒤에 최종+중간 결과를 매번 대체한다. append-only 이벤트로 중복 문장을 만들지 않는다. 취소·오류면 원문을 복원한다. 외부 수정 시 수정한 글을 보존하고 EDIT_CONFLICT로 중단한다. 기존 글을 포함한 길이 한도도 검사한다.
|
|
9
|
+
- Library `db-voice-control`/React VoiceControl: 상태/안내와 start/stop/cancel 의도만 제공한다. 마이크·네트워크·엔진·AI 요청을 소유하지 않는다.
|
|
10
|
+
- Library ChatComposer/AiChat: 선택 voiceState/voiceMessage/voiceDisabled 속성을 전달한다. voice-state가 활성 상태일 때 직접 편집과 전송을 막고 Escape 취소 의도를 제공한다. 음성 미사용/기본 idle의 기존 동작은 유지한다.
|
|
11
|
+
|
|
12
|
+
중간 결과는 실시간으로 표시하되 아직 확정된 저장/전송으로 간주하지 않는다. 제품은 성공 후 직접 편집/전송을 제공하고 오류 때 자동 전송·다른 엔진으로 음성 재전송하지 않는다. 전송 중/disable/로그아웃 시 실제 인식 취소는 엔진을 소유한 제품 책임이다.
|
|
13
|
+
|
|
14
|
+
## 공개 API와 패키지 경계
|
|
15
|
+
|
|
16
|
+
VoiceControl은 ui-wc root/React/public component subpath에 추가했다. WC props: state/message/disabled/compact/start-label/stop-label/cancel-label. 이벤트 db-start/db-stop/db-cancel. ChatComposer와 AiChat은 voice-state/voice-message/voice-disabled를 추가하고 기존 db-voice에 action(start/stop/cancel)을 덧붙인다. 기존 이벤트 이름은 유지하며 payload를 소비하지 않던 핸들러도 계속 동작한다. 활성 음성 상태에서만 readOnly/send 방어가 적용된다. 취소 가능한 음성 상태는 owner가 즉시 갱신해야 한다.
|
|
17
|
+
|
|
18
|
+
기존 source 0.8.13을 빌드하고 별도 staging manifest에만 private 프리릴리스 버전을 부여한다. 같은 0.8.13 이름으로 r2와 새 타입을 섞으면 TypeScript package identity가 충돌하므로 음성 시험 앱만 다른 버전을 고정한다. Library 원본 version·기존 소비 앱 r2·공개 npm 버전은 변경하지 않는다. Starter Contracts export/runtime deps/DB는 그대로다. WC는 React/엔진과 독립이며 새 런타임 공급자 의존성은 없다.
|
|
19
|
+
|
|
20
|
+
음성 앱의 Vite 조립부는 자기 설치 패키지의 built React ESM/CSS만 매핑해 기존 디자인 bridge도 같은 custom element 등록을 사용하게 한다. 다른 제품의 package/lock을 자동 갱신하지 않는다. 독립 WC와 React built 소비를 각각 검증한다.
|
|
21
|
+
|
|
22
|
+
## 인식 서비스 오류와 한계
|
|
23
|
+
|
|
24
|
+
NETWORK는 브라우저 인식 서버의 실패다. UI 수정만으로 해결됐다고 보고하지 않는다. 원문 복원과 명시적 재시도, Chrome 직접 사용/네트워크 정책 확인을 안내한다. 다른 provider를 직접 주입할 수 있으나 자동 fallback은 없다. Whisper·자가 운영 STT·on-device 모델·실제 한국어/영어 음성 정확도는 별도 검증이다. 앱을 열었다는 이유로 마이크를 자동 수집하지 않는다.
|