@operato/input 10.0.0 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/STYLING.md +194 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/input-styles.d.ts +38 -0
- package/dist/src/input-styles.js +108 -0
- package/dist/src/input-styles.js.map +1 -0
- package/dist/src/ox-buttons-radio.js +6 -4
- package/dist/src/ox-buttons-radio.js.map +1 -1
- package/dist/src/ox-form-field.d.ts +41 -0
- package/dist/src/ox-form-field.js +68 -1
- package/dist/src/ox-form-field.js.map +1 -1
- package/dist/src/ox-input-3axis.js +3 -1
- package/dist/src/ox-input-3axis.js.map +1 -1
- package/dist/src/ox-input-3dish.js +4 -2
- package/dist/src/ox-input-3dish.js.map +1 -1
- package/dist/src/ox-input-audio.js +4 -2
- package/dist/src/ox-input-audio.js.map +1 -1
- package/dist/src/ox-input-barcode.js +3 -1
- package/dist/src/ox-input-barcode.js.map +1 -1
- package/dist/src/ox-input-code.js +3 -1
- package/dist/src/ox-input-code.js.map +1 -1
- package/dist/src/ox-input-color.js +4 -2
- package/dist/src/ox-input-color.js.map +1 -1
- package/dist/src/ox-input-crontab.d.ts +1 -1
- package/dist/src/ox-input-crontab.js +6 -5
- package/dist/src/ox-input-crontab.js.map +1 -1
- package/dist/src/ox-input-data.js +5 -3
- package/dist/src/ox-input-data.js.map +1 -1
- package/dist/src/ox-input-duration.d.ts +1 -1
- package/dist/src/ox-input-duration.js +5 -4
- package/dist/src/ox-input-duration.js.map +1 -1
- package/dist/src/ox-input-file.js +3 -1
- package/dist/src/ox-input-file.js.map +1 -1
- package/dist/src/ox-input-formula-help-popup.js +30 -28
- package/dist/src/ox-input-formula-help-popup.js.map +1 -1
- package/dist/src/ox-input-formula.js +112 -111
- package/dist/src/ox-input-formula.js.map +1 -1
- package/dist/src/ox-input-hashtags.d.ts +1 -1
- package/dist/src/ox-input-hashtags.js +9 -8
- package/dist/src/ox-input-hashtags.js.map +1 -1
- package/dist/src/ox-input-i18n-label.d.ts +1 -1
- package/dist/src/ox-input-i18n-label.js +5 -4
- package/dist/src/ox-input-i18n-label.js.map +1 -1
- package/dist/src/ox-input-image.js +4 -2
- package/dist/src/ox-input-image.js.map +1 -1
- package/dist/src/ox-input-key-values.d.ts +1 -1
- package/dist/src/ox-input-key-values.js +10 -9
- package/dist/src/ox-input-key-values.js.map +1 -1
- package/dist/src/ox-input-mass-fraction.js +7 -5
- package/dist/src/ox-input-mass-fraction.js.map +1 -1
- package/dist/src/ox-input-multiple-colors.d.ts +1 -1
- package/dist/src/ox-input-multiple-colors.js +5 -4
- package/dist/src/ox-input-multiple-colors.js.map +1 -1
- package/dist/src/ox-input-options.d.ts +1 -1
- package/dist/src/ox-input-options.js +8 -7
- package/dist/src/ox-input-options.js.map +1 -1
- package/dist/src/ox-input-partition-keys.d.ts +1 -1
- package/dist/src/ox-input-partition-keys.js +10 -9
- package/dist/src/ox-input-partition-keys.js.map +1 -1
- package/dist/src/ox-input-privilege.d.ts +1 -1
- package/dist/src/ox-input-privilege.js +9 -8
- package/dist/src/ox-input-privilege.js.map +1 -1
- package/dist/src/ox-input-quantifier.d.ts +1 -1
- package/dist/src/ox-input-quantifier.js +6 -5
- package/dist/src/ox-input-quantifier.js.map +1 -1
- package/dist/src/ox-input-range.js +9 -8
- package/dist/src/ox-input-range.js.map +1 -1
- package/dist/src/ox-input-search.js +3 -1
- package/dist/src/ox-input-search.js.map +1 -1
- package/dist/src/ox-input-secret.js +3 -1
- package/dist/src/ox-input-secret.js.map +1 -1
- package/dist/src/ox-input-select-buttons.d.ts +1 -1
- package/dist/src/ox-input-select-buttons.js +4 -3
- package/dist/src/ox-input-select-buttons.js.map +1 -1
- package/dist/src/ox-input-signature.js +5 -3
- package/dist/src/ox-input-signature.js.map +1 -1
- package/dist/src/ox-input-stack.js +3 -1
- package/dist/src/ox-input-stack.js.map +1 -1
- package/dist/src/ox-input-table-column-config.js +5 -3
- package/dist/src/ox-input-table-column-config.js.map +1 -1
- package/dist/src/ox-input-table.js +5 -3
- package/dist/src/ox-input-table.js.map +1 -1
- package/dist/src/ox-input-unit-number.js +5 -3
- package/dist/src/ox-input-unit-number.js.map +1 -1
- package/dist/src/ox-input-value-map.d.ts +1 -1
- package/dist/src/ox-input-value-map.js +10 -9
- package/dist/src/ox-input-value-map.js.map +1 -1
- package/dist/src/ox-input-value-ranges.d.ts +1 -1
- package/dist/src/ox-input-value-ranges.js +11 -10
- package/dist/src/ox-input-value-ranges.js.map +1 -1
- package/dist/src/ox-input-video.js +4 -2
- package/dist/src/ox-input-video.js.map +1 -1
- package/dist/src/ox-input-work-shift.d.ts +1 -1
- package/dist/src/ox-input-work-shift.js +10 -9
- package/dist/src/ox-input-work-shift.js.map +1 -1
- package/dist/src/ox-select-floor.js +5 -3
- package/dist/src/ox-select-floor.js.map +1 -1
- package/dist/src/ox-select.js +20 -11
- package/dist/src/ox-select.js.map +1 -1
- package/dist/test/ox-form-field.test.d.ts +1 -0
- package/dist/test/ox-form-field.test.js +125 -0
- package/dist/test/ox-form-field.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/test/ox-form-field.test.ts +159 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [10.1.0](https://github.com/hatiolab/operato/compare/v10.0.0...v10.1.0) (2026-07-30)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
* **input:** add state/validation contract to OxFormField with first tests ([8b47552](https://github.com/hatiolab/operato/commit/8b475523d6d3074d69e8a4bb3085489f0f5e65d4))
|
|
12
|
+
* **input:** add styling contract (--ox-input-*) and apply to pilot components ([88b8fe7](https://github.com/hatiolab/operato/commit/88b8fe72afbcbcfe15f99a66a608db3589d95c06))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### :memo: Documentation
|
|
16
|
+
|
|
17
|
+
* **input:** correct test status after adding base contract tests ([537da22](https://github.com/hatiolab/operato/commit/537da223647861233d0c7eaf6bf45475d2f68424))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### :house: Code Refactoring
|
|
21
|
+
|
|
22
|
+
* **input:** roll out styling contract across all input components ([8bf056e](https://github.com/hatiolab/operato/commit/8bf056e3790d405de0e3ab77dc09435a605914a0))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## [10.0.0](https://github.com/hatiolab/operato/compare/v10.0.0-beta.68...v10.0.0) (2026-07-29)
|
|
7
27
|
|
|
8
28
|
**Note:** Version bump only for package @operato/input
|
package/STYLING.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# @operato/input — 스타일링 계약
|
|
2
|
+
|
|
3
|
+
> 입력 컴포넌트의 **인상(색·여백·라운드·타이포·높이·거동)을 앱이 자유롭게 통제**할 수 있게 하는 규약.
|
|
4
|
+
> 원칙: **인상에 영향을 주는 값은 컴포넌트에 고정하지 않는다.**
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 왜 필요했나 (실측 근거)
|
|
9
|
+
|
|
10
|
+
이 패키지 51개 컴포넌트는 같은 의미의 값을 제각각 리터럴로 박아 두었다.
|
|
11
|
+
|
|
12
|
+
| 축 | 서로 다른 값 | 예 |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| border-radius | 8종 | `4px`(15) · `--border-radius`(10) · `--md-sys-shape-corner-small`(9) · `8px` · `5px` · `12px` |
|
|
15
|
+
| font-size | 8종 | 토큰(17) · `14px`(9) · `12px`(8) · `13px`(6) · `11px` · `10px` |
|
|
16
|
+
| padding | 8종 | `--spacing-tiny`(12) · `0` · `--spacing-small` · `12px` |
|
|
17
|
+
| 높이 | 혼재 | `--form-element-height-medium`(5) · `16px` · `7px` |
|
|
18
|
+
| transition | 4종 | `all 0.2s ease`(6) · `0.4s` · `0.3s ease` |
|
|
19
|
+
| box-shadow | 3종 | 팝업 그림자가 서로 다름 + 다크 미대응 |
|
|
20
|
+
|
|
21
|
+
문제는 세 가지였다.
|
|
22
|
+
1. **일관성** — 나란히 놓으면 모서리·글자 크기·정렬이 어긋난다.
|
|
23
|
+
2. **유연성** — `::part()` 0개, 스타일링 훅 11개/3파일 → 앱이 외형을 바꿀 수단이 없었다.
|
|
24
|
+
3. **자립성** — `--spacing-small`(48회) · `--input-font`(17회) 등 **패키지 밖 var 를 정의 없이 참조** →
|
|
25
|
+
호스트 테마가 없으면(Storybook·단독 사용) 무너졌다. 동시에 다크 테마에서 흰 배경·검정 테두리가 그대로 남았다.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 계층 구조
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
L1 --md-sys-* Material 팔레트·shape 소유: 앱/프레임워크 테마
|
|
33
|
+
L2 --ox-input-* 입력 의미 계약(아래 목록) 소유: @operato/input
|
|
34
|
+
L3 --ox-select-* 등 컴포넌트 미세 조정 소유: 각 컴포넌트
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- **L2 기본값은 L1 토큰에서 파생**된다 → 앱이 아무것도 하지 않아도 테마·다크가 자동 정합.
|
|
38
|
+
- **리터럴 fallback 을 함께 둔다** → Material 토큰조차 없는 단독 환경에서도 동작(자립).
|
|
39
|
+
- L3 는 항상 L2 로 폴백한다 → 앱은 **전역(L2)** 또는 **특정 컴포넌트(L3)** 어느 쪽으로든 통제 가능.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## L2 계약 (`--ox-input-*`)
|
|
44
|
+
|
|
45
|
+
### 색
|
|
46
|
+
| var | 기본값 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `--ox-input-color` | `--md-sys-color-on-surface` |
|
|
49
|
+
| `--ox-input-background` | `--md-sys-color-surface` |
|
|
50
|
+
| `--ox-input-border-color` | `--md-sys-color-outline` |
|
|
51
|
+
| `--ox-input-border` | `1px solid var(--ox-input-border-color)` |
|
|
52
|
+
| `--ox-input-focus-color` | `--md-sys-color-primary` |
|
|
53
|
+
| `--ox-input-placeholder-color` | `--md-sys-color-on-surface-variant` |
|
|
54
|
+
|
|
55
|
+
### 형태
|
|
56
|
+
| var | 기본값 |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `--ox-input-radius` | `--md-sys-shape-corner-small` (4px) |
|
|
59
|
+
| `--ox-input-popup-radius` | `--md-sys-shape-corner-medium` (라운드 2배) |
|
|
60
|
+
| `--ox-input-padding` | `4px 9px` |
|
|
61
|
+
| `--ox-input-gap` | `4px` |
|
|
62
|
+
| `--ox-input-height` | `30px` |
|
|
63
|
+
|
|
64
|
+
### 타이포
|
|
65
|
+
| var | 기본값 |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `--ox-input-font-size` | `--md-sys-typescale-label-large-size` (0.875rem) |
|
|
68
|
+
| `--ox-input-font-family` | `inherit` |
|
|
69
|
+
| `--ox-input-font` | `normal <size> <family>` |
|
|
70
|
+
| `--ox-input-line-height` | `1.4` |
|
|
71
|
+
|
|
72
|
+
### 상태
|
|
73
|
+
| var | 기본값 | 쓰임 |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `--ox-input-invalid-color` | `--md-sys-color-error` | 검증 실패 시 테두리·포커스 색 |
|
|
76
|
+
| `--ox-input-required-color` | = invalid-color | 필수 표시(별표 등) |
|
|
77
|
+
| `--ox-input-readonly-background` | `--md-sys-color-surface-container-low` | 읽기전용 배경 |
|
|
78
|
+
| `--ox-input-disabled-opacity` | `0.5` | 비활성 흐림 |
|
|
79
|
+
|
|
80
|
+
`InputStyles` 가 `:host([disabled])` · `:host([readonly])` · `:host([invalid])` 를 함께 정의하므로,
|
|
81
|
+
`OxFormField` 를 상속한 컴포넌트는 **추가 작업 없이 상태 시각을 얻는다**.
|
|
82
|
+
|
|
83
|
+
### 거동·깊이
|
|
84
|
+
| var | 기본값 |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `--ox-input-transition` | `all 0.2s ease` |
|
|
87
|
+
| `--ox-input-shadow` | `0 4px 16px rgba(0,0,0,.2)` |
|
|
88
|
+
| `--ox-input-scrim` | `rgba(0,0,0,.5)` (모달 배경 — 알파가 본질) |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 사용법
|
|
93
|
+
|
|
94
|
+
### 앱: 전체 입력의 인상 바꾸기
|
|
95
|
+
```css
|
|
96
|
+
/* 예: 더 둥글고 여유 있는 폼 */
|
|
97
|
+
body {
|
|
98
|
+
--ox-input-radius: 10px;
|
|
99
|
+
--ox-input-padding: 8px 12px;
|
|
100
|
+
--ox-input-height: 38px;
|
|
101
|
+
--ox-input-font-size: 0.95rem;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 앱: 특정 컴포넌트만
|
|
106
|
+
```css
|
|
107
|
+
ox-select {
|
|
108
|
+
--ox-select-icon-color: tomato;
|
|
109
|
+
--ox-select-border-bottom: 2px solid currentColor;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 컴포넌트 작성자
|
|
114
|
+
```ts
|
|
115
|
+
import { InputStyles } from './input-styles.js'
|
|
116
|
+
|
|
117
|
+
static styles = [
|
|
118
|
+
InputStyles, // ← 배열 맨 앞
|
|
119
|
+
css`
|
|
120
|
+
/* 리터럴 금지. 계약 var 참조 */
|
|
121
|
+
.field {
|
|
122
|
+
padding: var(--ox-input-padding);
|
|
123
|
+
border: var(--ox-input-border);
|
|
124
|
+
border-radius: var(--ox-input-radius);
|
|
125
|
+
transition: var(--ox-input-transition);
|
|
126
|
+
}
|
|
127
|
+
/* 컴포넌트 조정 지점은 2단 폴백으로 */
|
|
128
|
+
:host { --ox-mything-color: var(--ox-input-color); }
|
|
129
|
+
`
|
|
130
|
+
]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 계약 대상이 아닌 것 (그대로 둔다)
|
|
134
|
+
구조적 필연은 인상 조정 대상이 아니다 — 원형 스와치 `border-radius: 50%`,
|
|
135
|
+
탭 상단 `8px 8px 0 0`(단, 값은 `var(--ox-input-popup-radius)` 파생), 아이콘 스프라이트 좌표 등.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 적용 현황
|
|
140
|
+
|
|
141
|
+
**확산 완료** (2026-07-29)
|
|
142
|
+
|
|
143
|
+
| 지표 | 결과 |
|
|
144
|
+
|---|---|
|
|
145
|
+
| 계약 채택 컴포넌트 | **38개** (`InputStyles` 포함) |
|
|
146
|
+
| 계약 var 참조 | **250곳** |
|
|
147
|
+
| 하드코딩 색 | **0** (이전: 흰 배경 14 · 검정 텍스트 8 · 검정 테두리 30) |
|
|
148
|
+
| 외부 미정의 의존 | **0** (이전: `--spacing-*` 48+18 · `--input-font` 17 · `--padding-default` 11 · `--border-radius` 10) |
|
|
149
|
+
| 빌드 · 테스트 | `npm run build` 성공 · 계약 테스트 12/12 통과 |
|
|
150
|
+
|
|
151
|
+
값 보존 매핑을 사용해 **기본 외형은 그대로 유지**했다.
|
|
152
|
+
`--spacing-tiny`(2px) → `calc(var(--ox-input-gap) / 2)` · `--spacing-small`(4px) → `var(--ox-input-gap)` ·
|
|
153
|
+
`--spacing-medium`(8px)·`--padding-default`(8px) → `calc(var(--ox-input-gap) * 2)`.
|
|
154
|
+
→ 같은 값이지만 이제 **`--ox-input-gap` 하나로 전체 여백 리듬을 조정**할 수 있다.
|
|
155
|
+
|
|
156
|
+
흩어져 있던 고정값도 수렴했다: `border-radius` 8종 → `--ox-input-radius`,
|
|
157
|
+
`font-size` 10~14px → `--ox-input-font-size` / `--ox-input-font-size-small` 2단,
|
|
158
|
+
`transition` 4종 → `--ox-input-transition`.
|
|
159
|
+
|
|
160
|
+
계약 대상이 아닌 것으로 남긴 예: 원형 스와치 `50%`, 탭 상단 `8px 8px 0 0`(값은 popup-radius 파생),
|
|
161
|
+
스크림 `rgba(0,0,0,.5)`(알파가 본질), 그림자 알파.
|
|
162
|
+
|
|
163
|
+
## 기능 계약 (`OxFormField`)
|
|
164
|
+
|
|
165
|
+
스타일 계약과 짝을 이루는 **상태·검증 계약**. 기반 1곳 수정으로 51개 컴포넌트에 파급된다.
|
|
166
|
+
|
|
167
|
+
| 멤버 | 설명 |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `disabled` | 상호작용 불가 · 흐리게 · **폼 데이터 제외**(HTML 표준) |
|
|
170
|
+
| `readonly` | 값 표시·선택·복사 가능, 수정 불가 · **폼 데이터 포함** |
|
|
171
|
+
| `required` | `checkValidity()` 의 기준 |
|
|
172
|
+
| `invalid` | 검증 실패 상태 — error 색으로 표시 |
|
|
173
|
+
| `placeholder` | 안내 문구(기반으로 승격, 기존 6개 자체 선언과 호환) |
|
|
174
|
+
| `checkValidity()` | 검증 실행 → `invalid` 갱신, 실패 시 `invalid` 이벤트 |
|
|
175
|
+
| `validateValue()` | 컴포넌트별 규칙 오버라이드 지점(기본 `required` 만) |
|
|
176
|
+
| `isEmpty()` | 문자열·배열·nullish 판정. **`0`·`false` 는 유효한 값** |
|
|
177
|
+
|
|
178
|
+
**네 상태는 모두 attribute 로 reflect** 된다 — 그래야 프로퍼티로 설정(`el.disabled = true`)해도
|
|
179
|
+
`:host([disabled])` 가 반영된다. (기존 `disabled` 는 reflect 가 없어 이 경우 시각이 반영되지 않았다 → 수정됨)
|
|
180
|
+
|
|
181
|
+
### `disabled` vs `readonly`
|
|
182
|
+
조회 화면을 `disabled` 로 잠그면 흐려지고 값도 전송되지 않는다. **`readonly` 를 쓴다.**
|
|
183
|
+
|
|
184
|
+
## 테스트
|
|
185
|
+
|
|
186
|
+
`test/ox-form-field.test.ts` — 기반 계약 **12건**(reflect · isEmpty 경계값 · required 검증 ·
|
|
187
|
+
invalid 이벤트 · 폼 수집 · disabled 제외 · readonly 포함). 실행: `npm test`(tsc → wtr).
|
|
188
|
+
이 패키지의 **첫 실질 테스트**다(이전에는 `.backup` 파일만 존재).
|
|
189
|
+
|
|
190
|
+
## 알려진 사항
|
|
191
|
+
- 이 패키지 `npx tsc`(clean 없이)는 **기존부터** `TS5055`(dist 를 입력으로 잡음) 7건이 난다.
|
|
192
|
+
정상 경로는 `npm run build`(clean → tsc)다. 본 작업과 무관.
|
|
193
|
+
- 기반 계약 테스트 12건은 확보했으나 **개별 컴포넌트 테스트는 아직 없다**(51개 중 0). 스타일 계약 확산 시
|
|
194
|
+
대표 컴포넌트 테스트를 함께 추가하는 것을 권한다. `property-angle.test.ts.backup` 은 비활성 잔재.
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,cAAc,kCAAkC,CAAA;AAChD,cAAc,uBAAuB,CAAA;AACrC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mCAAmC,CAAA;AACjD,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nexport * from './ox-form-field.js'\nexport * from './ox-buttons-radio.js'\nexport * from './ox-checkbox.js'\nexport * from './ox-select.js'\nexport * from './ox-input-switch.js'\nexport * from './ox-input-angle.js'\nexport * from './ox-input-3dish.js'\nexport * from './ox-input-3axis.js'\nexport * from './ox-input-stack.js'\nexport * from './ox-input-barcode.js'\nexport * from './ox-input-code.js'\nexport * from './ox-input-color.js'\nexport * from './ox-input-multiple-colors.js'\nexport * from './ox-input-file.js'\nexport * from './ox-input-image.js'\nexport * from './ox-input-video.js'\nexport * from './ox-input-audio.js'\nexport * from './ox-input-value-ranges.js'\nexport * from './ox-input-value-map.js'\nexport * from './ox-input-scene-component-id.js'\nexport * from './ox-input-crontab.js'\nexport * from './ox-input-partition-keys.js'\nexport * from './ox-input-duration.js'\nexport * from './ox-input-quantifier.js'\nexport * from './ox-input-select-buttons.js'\nexport * from './ox-input-textarea.js'\nexport * from './ox-input-direction.js'\nexport * from './ox-input-table-column-config.js'\nexport * from './ox-input-search.js'\nexport * from './ox-input-secret.js'\nexport * from './ox-input-signature.js'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,cAAc,kCAAkC,CAAA;AAChD,cAAc,uBAAuB,CAAA;AACrC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mCAAmC,CAAA;AACjD,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nexport * from './input-styles.js'\nexport * from './ox-form-field.js'\nexport * from './ox-buttons-radio.js'\nexport * from './ox-checkbox.js'\nexport * from './ox-select.js'\nexport * from './ox-input-switch.js'\nexport * from './ox-input-angle.js'\nexport * from './ox-input-3dish.js'\nexport * from './ox-input-3axis.js'\nexport * from './ox-input-stack.js'\nexport * from './ox-input-barcode.js'\nexport * from './ox-input-code.js'\nexport * from './ox-input-color.js'\nexport * from './ox-input-multiple-colors.js'\nexport * from './ox-input-file.js'\nexport * from './ox-input-image.js'\nexport * from './ox-input-video.js'\nexport * from './ox-input-audio.js'\nexport * from './ox-input-value-ranges.js'\nexport * from './ox-input-value-map.js'\nexport * from './ox-input-scene-component-id.js'\nexport * from './ox-input-crontab.js'\nexport * from './ox-input-partition-keys.js'\nexport * from './ox-input-duration.js'\nexport * from './ox-input-quantifier.js'\nexport * from './ox-input-select-buttons.js'\nexport * from './ox-input-textarea.js'\nexport * from './ox-input-direction.js'\nexport * from './ox-input-table-column-config.js'\nexport * from './ox-input-search.js'\nexport * from './ox-input-secret.js'\nexport * from './ox-input-signature.js'\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 입력 컴포넌트 공통 스타일 계약 (L2).
|
|
3
|
+
*
|
|
4
|
+
* ## 왜 필요한가
|
|
5
|
+
* 이 패키지의 51개 컴포넌트는 여백·라운드·폰트·높이·전환을 각자 리터럴로 박아 두었다
|
|
6
|
+
* (border-radius 8종, font-size 8종, padding 8종, transition 4종 …).
|
|
7
|
+
* 그래서 (a) 나란히 놓으면 어긋나 보이고, (b) 앱이 전체 인상을 바꿀 방법이 없고,
|
|
8
|
+
* (c) `--spacing-small` 같은 **패키지 밖 var 를 정의 없이 참조**해 호스트 테마가 없으면 무너졌다.
|
|
9
|
+
*
|
|
10
|
+
* ## 계층
|
|
11
|
+
* ```
|
|
12
|
+
* L1 --md-sys-* Material 팔레트·shape (앱/프레임워크 테마 소유)
|
|
13
|
+
* L2 --ox-input-* 입력 의미 계약 (이 패키지 소유 ← 여기)
|
|
14
|
+
* L3 --ox-select-* 등 컴포넌트 미세 조정 (각 컴포넌트 소유)
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* ## 규칙
|
|
18
|
+
* 1. **인상에 영향을 주는 값은 고정하지 않는다.** 색·여백·라운드·폰트·높이·전환·그림자는 모두
|
|
19
|
+
* 아래 var 로 노출하며, 앱은 어느 층에서든 덮어쓸 수 있다.
|
|
20
|
+
* 2. **기본값은 L1 토큰에서 파생**한다 → 앱이 아무것도 하지 않아도 테마·다크가 자동 정합된다.
|
|
21
|
+
* 3. **리터럴 fallback 을 함께 둔다** → Material 토큰조차 없는 단독 환경(Storybook 등)에서도 자립.
|
|
22
|
+
* 4. 구조적 필연(원형 스와치 `50%`, 탭 상단 `8px 8px 0 0` 등)은 계약 대상이 아니다.
|
|
23
|
+
*
|
|
24
|
+
* ## 사용법
|
|
25
|
+
* ```ts
|
|
26
|
+
* static styles = [InputStyles, css`...`] // 컴포넌트 styles 배열 맨 앞에
|
|
27
|
+
* ```
|
|
28
|
+
* 컴포넌트 내부에서는 리터럴 대신 계약 var 를 쓴다:
|
|
29
|
+
* ```css
|
|
30
|
+
* padding: var(--ox-input-padding);
|
|
31
|
+
* border-radius: var(--ox-input-radius);
|
|
32
|
+
* ```
|
|
33
|
+
* 컴포넌트별로 달라야 하면 2단 폴백으로 조정 지점을 남긴다:
|
|
34
|
+
* ```css
|
|
35
|
+
* color: var(--ox-select-color, var(--ox-input-color));
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare const InputStyles: import("lit").CSSResult;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* 입력 컴포넌트 공통 스타일 계약 (L2).
|
|
4
|
+
*
|
|
5
|
+
* ## 왜 필요한가
|
|
6
|
+
* 이 패키지의 51개 컴포넌트는 여백·라운드·폰트·높이·전환을 각자 리터럴로 박아 두었다
|
|
7
|
+
* (border-radius 8종, font-size 8종, padding 8종, transition 4종 …).
|
|
8
|
+
* 그래서 (a) 나란히 놓으면 어긋나 보이고, (b) 앱이 전체 인상을 바꿀 방법이 없고,
|
|
9
|
+
* (c) `--spacing-small` 같은 **패키지 밖 var 를 정의 없이 참조**해 호스트 테마가 없으면 무너졌다.
|
|
10
|
+
*
|
|
11
|
+
* ## 계층
|
|
12
|
+
* ```
|
|
13
|
+
* L1 --md-sys-* Material 팔레트·shape (앱/프레임워크 테마 소유)
|
|
14
|
+
* L2 --ox-input-* 입력 의미 계약 (이 패키지 소유 ← 여기)
|
|
15
|
+
* L3 --ox-select-* 등 컴포넌트 미세 조정 (각 컴포넌트 소유)
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* ## 규칙
|
|
19
|
+
* 1. **인상에 영향을 주는 값은 고정하지 않는다.** 색·여백·라운드·폰트·높이·전환·그림자는 모두
|
|
20
|
+
* 아래 var 로 노출하며, 앱은 어느 층에서든 덮어쓸 수 있다.
|
|
21
|
+
* 2. **기본값은 L1 토큰에서 파생**한다 → 앱이 아무것도 하지 않아도 테마·다크가 자동 정합된다.
|
|
22
|
+
* 3. **리터럴 fallback 을 함께 둔다** → Material 토큰조차 없는 단독 환경(Storybook 등)에서도 자립.
|
|
23
|
+
* 4. 구조적 필연(원형 스와치 `50%`, 탭 상단 `8px 8px 0 0` 등)은 계약 대상이 아니다.
|
|
24
|
+
*
|
|
25
|
+
* ## 사용법
|
|
26
|
+
* ```ts
|
|
27
|
+
* static styles = [InputStyles, css`...`] // 컴포넌트 styles 배열 맨 앞에
|
|
28
|
+
* ```
|
|
29
|
+
* 컴포넌트 내부에서는 리터럴 대신 계약 var 를 쓴다:
|
|
30
|
+
* ```css
|
|
31
|
+
* padding: var(--ox-input-padding);
|
|
32
|
+
* border-radius: var(--ox-input-radius);
|
|
33
|
+
* ```
|
|
34
|
+
* 컴포넌트별로 달라야 하면 2단 폴백으로 조정 지점을 남긴다:
|
|
35
|
+
* ```css
|
|
36
|
+
* color: var(--ox-select-color, var(--ox-input-color));
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export const InputStyles = css `
|
|
40
|
+
:host {
|
|
41
|
+
/* ── 색 ─────────────────────────────────────────────── */
|
|
42
|
+
--ox-input-color: var(--md-sys-color-on-surface, #1a1b21);
|
|
43
|
+
--ox-input-background: var(--md-sys-color-surface, #fff);
|
|
44
|
+
--ox-input-border-color: var(--md-sys-color-outline, rgba(0, 0, 0, 0.25));
|
|
45
|
+
--ox-input-border: 1px solid var(--ox-input-border-color);
|
|
46
|
+
--ox-input-focus-color: var(--md-sys-color-primary, #495d92);
|
|
47
|
+
--ox-input-placeholder-color: var(--md-sys-color-on-surface-variant, #45464f);
|
|
48
|
+
/* 상태 색 — 검증 실패 / 필수 표시 */
|
|
49
|
+
--ox-input-invalid-color: var(--md-sys-color-error, #b3261e);
|
|
50
|
+
--ox-input-required-color: var(--ox-input-invalid-color);
|
|
51
|
+
/* readonly 는 흐리게 하지 않는다(값을 읽는 상태이므로). 배경으로만 구분한다. */
|
|
52
|
+
--ox-input-readonly-background: var(--md-sys-color-surface-container-low, rgba(0, 0, 0, 0.03));
|
|
53
|
+
|
|
54
|
+
/* ── 형태 ───────────────────────────────────────────── */
|
|
55
|
+
--ox-input-radius: var(--md-sys-shape-corner-small, 4px);
|
|
56
|
+
/* 드롭다운·피커 등 팝업 표면은 필드보다 큰 라운드를 쓴다(기본 2배, 앱이 조정 가능) */
|
|
57
|
+
--ox-input-popup-radius: var(--md-sys-shape-corner-medium, calc(var(--ox-input-radius) * 2));
|
|
58
|
+
--ox-input-padding: 4px 9px;
|
|
59
|
+
--ox-input-gap: 4px;
|
|
60
|
+
--ox-input-height: 30px;
|
|
61
|
+
|
|
62
|
+
/* ── 타이포 ─────────────────────────────────────────── */
|
|
63
|
+
--ox-input-font-size: var(--md-sys-typescale-label-large-size, 0.875rem);
|
|
64
|
+
/* 보조 텍스트(설명·단위·캡션). 기존에 10~13px 로 흩어져 있던 것을 한 단계로 수렴. */
|
|
65
|
+
--ox-input-font-size-small: var(--md-sys-typescale-label-medium-size, 0.75rem);
|
|
66
|
+
--ox-input-font-family: inherit;
|
|
67
|
+
--ox-input-font: normal var(--ox-input-font-size) var(--ox-input-font-family);
|
|
68
|
+
--ox-input-line-height: 1.4;
|
|
69
|
+
|
|
70
|
+
/* ── 거동·깊이 ──────────────────────────────────────── */
|
|
71
|
+
--ox-input-transition: all 0.2s ease;
|
|
72
|
+
--ox-input-shadow: 0 4px 16px var(--md-sys-color-shadow-alpha, rgba(0, 0, 0, 0.2));
|
|
73
|
+
/* 모달 뒤 배경(스크림) — 알파가 본질이라 토큰 파생이 아닌 반투명 값을 유지하되 조정 가능하게 */
|
|
74
|
+
--ox-input-scrim: rgba(0, 0, 0, 0.5);
|
|
75
|
+
--ox-input-disabled-opacity: 0.5;
|
|
76
|
+
|
|
77
|
+
box-sizing: border-box;
|
|
78
|
+
color: var(--ox-input-color);
|
|
79
|
+
font: var(--ox-input-font);
|
|
80
|
+
line-height: var(--ox-input-line-height);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* disabled — 상호작용 불가 + 흐리게. 폼 데이터 제외는 OxFormField 가 처리. */
|
|
84
|
+
:host([disabled]) {
|
|
85
|
+
opacity: var(--ox-input-disabled-opacity);
|
|
86
|
+
pointer-events: none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* readonly — 값을 읽고 선택·복사할 수 있어야 하므로 흐리게 하지 않고 배경으로만 구분.
|
|
90
|
+
실제 수정 차단은 각 컴포넌트가 입력 요소에 readonly 를 전달해 처리한다. */
|
|
91
|
+
:host([readonly]) {
|
|
92
|
+
--ox-input-background: var(--ox-input-readonly-background);
|
|
93
|
+
cursor: default;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* invalid — 테두리·포커스 색을 error 로 전환. 컴포넌트가 --ox-input-border 를 쓰면 자동 반영. */
|
|
97
|
+
:host([invalid]) {
|
|
98
|
+
--ox-input-border-color: var(--ox-input-invalid-color);
|
|
99
|
+
--ox-input-focus-color: var(--ox-input-invalid-color);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
*,
|
|
103
|
+
*::before,
|
|
104
|
+
*::after {
|
|
105
|
+
box-sizing: border-box;
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
//# sourceMappingURL=input-styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-styles.js","sourceRoot":"","sources":["../../src/input-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoE7B,CAAA","sourcesContent":["import { css } from 'lit'\n\n/**\n * 입력 컴포넌트 공통 스타일 계약 (L2).\n *\n * ## 왜 필요한가\n * 이 패키지의 51개 컴포넌트는 여백·라운드·폰트·높이·전환을 각자 리터럴로 박아 두었다\n * (border-radius 8종, font-size 8종, padding 8종, transition 4종 …).\n * 그래서 (a) 나란히 놓으면 어긋나 보이고, (b) 앱이 전체 인상을 바꿀 방법이 없고,\n * (c) `--spacing-small` 같은 **패키지 밖 var 를 정의 없이 참조**해 호스트 테마가 없으면 무너졌다.\n *\n * ## 계층\n * ```\n * L1 --md-sys-* Material 팔레트·shape (앱/프레임워크 테마 소유)\n * L2 --ox-input-* 입력 의미 계약 (이 패키지 소유 ← 여기)\n * L3 --ox-select-* 등 컴포넌트 미세 조정 (각 컴포넌트 소유)\n * ```\n *\n * ## 규칙\n * 1. **인상에 영향을 주는 값은 고정하지 않는다.** 색·여백·라운드·폰트·높이·전환·그림자는 모두\n * 아래 var 로 노출하며, 앱은 어느 층에서든 덮어쓸 수 있다.\n * 2. **기본값은 L1 토큰에서 파생**한다 → 앱이 아무것도 하지 않아도 테마·다크가 자동 정합된다.\n * 3. **리터럴 fallback 을 함께 둔다** → Material 토큰조차 없는 단독 환경(Storybook 등)에서도 자립.\n * 4. 구조적 필연(원형 스와치 `50%`, 탭 상단 `8px 8px 0 0` 등)은 계약 대상이 아니다.\n *\n * ## 사용법\n * ```ts\n * static styles = [InputStyles, css`...`] // 컴포넌트 styles 배열 맨 앞에\n * ```\n * 컴포넌트 내부에서는 리터럴 대신 계약 var 를 쓴다:\n * ```css\n * padding: var(--ox-input-padding);\n * border-radius: var(--ox-input-radius);\n * ```\n * 컴포넌트별로 달라야 하면 2단 폴백으로 조정 지점을 남긴다:\n * ```css\n * color: var(--ox-select-color, var(--ox-input-color));\n * ```\n */\nexport const InputStyles = css`\n :host {\n /* ── 색 ─────────────────────────────────────────────── */\n --ox-input-color: var(--md-sys-color-on-surface, #1a1b21);\n --ox-input-background: var(--md-sys-color-surface, #fff);\n --ox-input-border-color: var(--md-sys-color-outline, rgba(0, 0, 0, 0.25));\n --ox-input-border: 1px solid var(--ox-input-border-color);\n --ox-input-focus-color: var(--md-sys-color-primary, #495d92);\n --ox-input-placeholder-color: var(--md-sys-color-on-surface-variant, #45464f);\n /* 상태 색 — 검증 실패 / 필수 표시 */\n --ox-input-invalid-color: var(--md-sys-color-error, #b3261e);\n --ox-input-required-color: var(--ox-input-invalid-color);\n /* readonly 는 흐리게 하지 않는다(값을 읽는 상태이므로). 배경으로만 구분한다. */\n --ox-input-readonly-background: var(--md-sys-color-surface-container-low, rgba(0, 0, 0, 0.03));\n\n /* ── 형태 ───────────────────────────────────────────── */\n --ox-input-radius: var(--md-sys-shape-corner-small, 4px);\n /* 드롭다운·피커 등 팝업 표면은 필드보다 큰 라운드를 쓴다(기본 2배, 앱이 조정 가능) */\n --ox-input-popup-radius: var(--md-sys-shape-corner-medium, calc(var(--ox-input-radius) * 2));\n --ox-input-padding: 4px 9px;\n --ox-input-gap: 4px;\n --ox-input-height: 30px;\n\n /* ── 타이포 ─────────────────────────────────────────── */\n --ox-input-font-size: var(--md-sys-typescale-label-large-size, 0.875rem);\n /* 보조 텍스트(설명·단위·캡션). 기존에 10~13px 로 흩어져 있던 것을 한 단계로 수렴. */\n --ox-input-font-size-small: var(--md-sys-typescale-label-medium-size, 0.75rem);\n --ox-input-font-family: inherit;\n --ox-input-font: normal var(--ox-input-font-size) var(--ox-input-font-family);\n --ox-input-line-height: 1.4;\n\n /* ── 거동·깊이 ──────────────────────────────────────── */\n --ox-input-transition: all 0.2s ease;\n --ox-input-shadow: 0 4px 16px var(--md-sys-color-shadow-alpha, rgba(0, 0, 0, 0.2));\n /* 모달 뒤 배경(스크림) — 알파가 본질이라 토큰 파생이 아닌 반투명 값을 유지하되 조정 가능하게 */\n --ox-input-scrim: rgba(0, 0, 0, 0.5);\n --ox-input-disabled-opacity: 0.5;\n\n box-sizing: border-box;\n color: var(--ox-input-color);\n font: var(--ox-input-font);\n line-height: var(--ox-input-line-height);\n }\n\n /* disabled — 상호작용 불가 + 흐리게. 폼 데이터 제외는 OxFormField 가 처리. */\n :host([disabled]) {\n opacity: var(--ox-input-disabled-opacity);\n pointer-events: none;\n }\n\n /* readonly — 값을 읽고 선택·복사할 수 있어야 하므로 흐리게 하지 않고 배경으로만 구분.\n 실제 수정 차단은 각 컴포넌트가 입력 요소에 readonly 를 전달해 처리한다. */\n :host([readonly]) {\n --ox-input-background: var(--ox-input-readonly-background);\n cursor: default;\n }\n\n /* invalid — 테두리·포커스 색을 error 로 전환. 컴포넌트가 --ox-input-border 를 쓰면 자동 반영. */\n :host([invalid]) {\n --ox-input-border-color: var(--ox-input-invalid-color);\n --ox-input-focus-color: var(--ox-input-invalid-color);\n }\n\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n`\n"]}
|
|
@@ -5,6 +5,7 @@ import { __decorate } from "tslib";
|
|
|
5
5
|
import { css, html } from 'lit';
|
|
6
6
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
7
7
|
import { OxFormField } from './ox-form-field.js';
|
|
8
|
+
import { InputStyles } from './input-styles.js';
|
|
8
9
|
/**
|
|
9
10
|
여러 버튼 중에서 하나만 눌리거나, 모두 눌리지 않은 상태만을 갖는 라디오 형태의 버튼이다.
|
|
10
11
|
|
|
@@ -32,6 +33,7 @@ let OxButtonsRadio = class OxButtonsRadio extends OxFormField {
|
|
|
32
33
|
});
|
|
33
34
|
}
|
|
34
35
|
static { this.styles = [
|
|
36
|
+
InputStyles,
|
|
35
37
|
css `
|
|
36
38
|
:host {
|
|
37
39
|
display: inline-block;
|
|
@@ -40,8 +42,8 @@ let OxButtonsRadio = class OxButtonsRadio extends OxFormField {
|
|
|
40
42
|
--md-outlined-button-label-text-size: var(--md-sys-typescale-label-large-size, 0.875rem);
|
|
41
43
|
--md-outlined-button-container-height: var(--form-element-height-medium);
|
|
42
44
|
--md-outlined-button-container-shape: var(--md-sys-shape-corner-small);
|
|
43
|
-
--md-outlined-button-leading-space: var(--
|
|
44
|
-
--md-outlined-button-trailing-space: var(--
|
|
45
|
+
--md-outlined-button-leading-space: calc(var(--ox-input-gap) * 3);
|
|
46
|
+
--md-outlined-button-trailing-space: calc(var(--ox-input-gap) * 3);
|
|
45
47
|
--md-outlined-button-hover-label-text-color: var(--md-sys-color-primary);
|
|
46
48
|
--md-outlined-button-pressed-outline-color: var(--md-sys-color-primary);
|
|
47
49
|
--md-outlined-button-pressed-label-text-color: var(--md-sys-color-primary);
|
|
@@ -51,8 +53,8 @@ let OxButtonsRadio = class OxButtonsRadio extends OxFormField {
|
|
|
51
53
|
--md-filled-button-label-text-size: var(--md-sys-typescale-label-large-size, 0.875rem);
|
|
52
54
|
--md-filled-button-container-height: var(--form-element-height-medium);
|
|
53
55
|
--md-filled-button-container-shape: var(--md-sys-shape-corner-small);
|
|
54
|
-
--md-filled-button-leading-space: var(--
|
|
55
|
-
--md-filled-button-trailing-space: var(--
|
|
56
|
+
--md-filled-button-leading-space: calc(var(--ox-input-gap) * 3);
|
|
57
|
+
--md-filled-button-trailing-space: calc(var(--ox-input-gap) * 3);
|
|
56
58
|
--md-filled-button-hover-label-text-color: var(--md-sys-color-secondary-container);
|
|
57
59
|
--md-filled-button-pressed-label-text-color: var(--md-sys-color-secondary-container);
|
|
58
60
|
--md-filled-button-focus-label-text-color: var(--md-sys-color-secondary-container);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ox-buttons-radio.js","sourceRoot":"","sources":["../../src/ox-buttons-radio.ts"],"names":[],"mappings":"AAAA;;GAEG;;AAEH,OAAO,EAAE,GAAG,EAAE,IAAI,EAAkB,MAAM,KAAK,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"ox-buttons-radio.js","sourceRoot":"","sources":["../../src/ox-buttons-radio.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { css, html, PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { OxFormField } from './ox-form-field.js'\nimport { InputStyles } from './input-styles.js'\n\n/**\n여러 버튼 중에서 하나만 눌리거나, 모두 눌리지 않은 상태만을 갖는 라디오 형태의 버튼이다.\n\nExample:\n\n <ox-buttons-radio\n class=\"light\"\n @change=${(e: CustomEvent) => console.log(e.detail)}\n value=\"1\"\n >\n <md-outlined-button data-value=\"1\" ?active=${value == '1'}>Option 1</md-outlined-button>\n <md-outlined-button data-value=\"2\" ?active=${value == '2'}>Option 2</md-outlined-button>\n <md-outlined-button data-value=\"3\" ?active=${value == '3'}>Option 3</md-outlined-button>\n </ox-buttons-radio>\n*/\n@customElement('ox-buttons-radio')\nexport class OxButtonsRadio extends OxFormField {\n static styles = [\n InputStyles,\n css`\n :host {\n display: inline-block;\n\n --md-outlined-button-label-text-color: var(--md-sys-color-on-surface-variant);\n --md-outlined-button-label-text-size: var(--md-sys-typescale-label-large-size, 0.875rem);\n --md-outlined-button-container-height: var(--form-element-height-medium);\n --md-outlined-button-container-shape: var(--md-sys-shape-corner-small);\n --md-outlined-button-leading-space: calc(var(--ox-input-gap) * 3);\n --md-outlined-button-trailing-space: calc(var(--ox-input-gap) * 3);\n --md-outlined-button-hover-label-text-color: var(--md-sys-color-primary);\n --md-outlined-button-pressed-outline-color: var(--md-sys-color-primary);\n --md-outlined-button-pressed-label-text-color: var(--md-sys-color-primary);\n\n --md-filled-button-container-color: var(--md-sys-color-primary);\n --md-filled-button-label-text-color: var(--md-sys-color-primary-container);\n --md-filled-button-label-text-size: var(--md-sys-typescale-label-large-size, 0.875rem);\n --md-filled-button-container-height: var(--form-element-height-medium);\n --md-filled-button-container-shape: var(--md-sys-shape-corner-small);\n --md-filled-button-leading-space: calc(var(--ox-input-gap) * 3);\n --md-filled-button-trailing-space: calc(var(--ox-input-gap) * 3);\n --md-filled-button-hover-label-text-color: var(--md-sys-color-secondary-container);\n --md-filled-button-pressed-label-text-color: var(--md-sys-color-secondary-container);\n --md-filled-button-focus-label-text-color: var(--md-sys-color-secondary-container);\n\n --md-text-button-container-height: var(--form-element-height-medium);\n --md-text-button-container-shape: var(--md-sys-shape-corner-small);\n\n --md-elevated-button-container-height: var(--form-element-height-medium);\n --md-elevated-button-container-shape: var(--md-sys-shape-corner-small);\n }\n `\n ]\n\n /**\n * `value`는 버튼의 눌린 상태를 값으로 갖는 속성이다.\n */\n @property({ type: Object }) value: Object | null = null\n @property({ type: Boolean }) mandatory!: boolean\n\n @state() _slotObserver: MutationObserver = new MutationObserver(mutations => {\n this._onChanged()\n })\n\n render() {\n return html` <slot @click=${(e: Event) => this._onTapButton(e)}></slot> `\n }\n\n connectedCallback() {\n super.connectedCallback()\n\n this._slotObserver?.observe(this, { childList: true })\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n\n this._slotObserver?.disconnect()\n }\n\n updated(changes: PropertyValues<this>) {\n changes.has('value') && this._onChanged()\n }\n\n get buttons() {\n return Array.from(this.querySelectorAll('*'))\n }\n\n _onChanged() {\n this.buttons.forEach(button => {\n if (this.value === button.getAttribute('data-value')) {\n button.setAttribute('active', '')\n } else {\n button.removeAttribute('active')\n }\n })\n }\n\n _onTapButton(e: Event) {\n var target = e.target as HTMLElement\n target = target.closest('[data-value]') as HTMLElement\n\n if (!target || target === this) {\n return\n }\n\n var old = this.value\n\n if (!this.mandatory) {\n if (!target.getAttribute('active')) {\n this.value = target.getAttribute('data-value')\n target.setAttribute('active', '')\n } else {\n this.value = null\n target.removeAttribute('active')\n }\n } else {\n this.value = target.getAttribute('data-value')\n target.setAttribute('active', '')\n }\n\n if (old !== this.value) {\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: this.value\n })\n )\n }\n }\n}\n"]}
|
|
@@ -1,11 +1,52 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* 모든 입력 컴포넌트의 기반 계약.
|
|
4
|
+
*
|
|
5
|
+
* ## 상태 계약
|
|
6
|
+
* `disabled` / `readonly` / `required` / `invalid` 는 **attribute 로 reflect** 된다.
|
|
7
|
+
* 그래야 `InputStyles` 의 `:host([disabled])` · `:host([readonly])` · `:host([invalid])` 가
|
|
8
|
+
* **마크업으로 설정했을 때뿐 아니라 프로퍼티로 설정했을 때도** 시각에 반영된다.
|
|
9
|
+
* (reflect 가 없으면 `el.disabled = true` 는 화면에 나타나지 않는다.)
|
|
10
|
+
*
|
|
11
|
+
* ## disabled 와 readonly 의 구분
|
|
12
|
+
* - `disabled`: 상호작용 불가 · 흐리게 · **폼 데이터에 포함되지 않음**(HTML 표준 거동)
|
|
13
|
+
* - `readonly`: 값을 **보고 선택·복사할 수 있으나** 수정 불가 · **폼 데이터에는 포함됨**
|
|
14
|
+
* → 조회 화면을 `disabled` 로 잠그면 흐려지고 값도 전송되지 않으므로 `readonly` 를 쓴다.
|
|
15
|
+
*
|
|
16
|
+
* ## 검증
|
|
17
|
+
* `checkValidity()` 가 `invalid` 를 갱신하고 결과를 반환한다.
|
|
18
|
+
* 컴포넌트별 규칙(형식·범위 등)이 있으면 `validateValue()` 를 오버라이드한다(기본은 `required` 만 검사).
|
|
19
|
+
*/
|
|
2
20
|
export declare abstract class OxFormField<T = any> extends LitElement {
|
|
3
21
|
name?: string;
|
|
4
22
|
disabled?: boolean;
|
|
23
|
+
/** 값 표시·선택은 되지만 수정 불가. disabled 와 달리 폼 데이터에 포함된다. */
|
|
24
|
+
readonly?: boolean;
|
|
25
|
+
/** 값이 반드시 있어야 함. checkValidity() 의 기준. */
|
|
26
|
+
required?: boolean;
|
|
27
|
+
/** 검증 실패 상태. InputStyles 가 error 색으로 표시한다. */
|
|
28
|
+
invalid?: boolean;
|
|
29
|
+
/** 비어 있을 때 보여줄 안내 문구(컴포넌트가 지원하는 경우). */
|
|
30
|
+
placeholder?: string;
|
|
5
31
|
value?: T;
|
|
6
32
|
protected _form: HTMLFormElement | null;
|
|
7
33
|
protected _formdataEventHandler: EventListener | null;
|
|
8
34
|
connectedCallback(): void;
|
|
9
35
|
disconnectedCallback(): void;
|
|
36
|
+
/**
|
|
37
|
+
* 값이 비었는지 판단. 문자열·배열·nullish 를 다룬다.
|
|
38
|
+
* 숫자 `0` 과 `false` 는 **유효한 값**이므로 비었다고 보지 않는다.
|
|
39
|
+
*/
|
|
40
|
+
protected isEmpty(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* 자체 검증 규칙. 기본은 `required` 만 검사한다.
|
|
43
|
+
* 컴포넌트별 규칙이 있으면 오버라이드한다.
|
|
44
|
+
*/
|
|
45
|
+
protected validateValue(): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* 검증을 실행해 `invalid` 를 갱신하고 결과를 반환한다.
|
|
48
|
+
* 실패 시 `invalid` 이벤트를 올려 폼이 반응할 수 있게 한다.
|
|
49
|
+
*/
|
|
50
|
+
checkValidity(): boolean;
|
|
10
51
|
protected appendFormData({ formData }: FormDataEvent): void;
|
|
11
52
|
}
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { LitElement } from 'lit';
|
|
3
3
|
import { property } from 'lit/decorators.js';
|
|
4
|
+
/**
|
|
5
|
+
* 모든 입력 컴포넌트의 기반 계약.
|
|
6
|
+
*
|
|
7
|
+
* ## 상태 계약
|
|
8
|
+
* `disabled` / `readonly` / `required` / `invalid` 는 **attribute 로 reflect** 된다.
|
|
9
|
+
* 그래야 `InputStyles` 의 `:host([disabled])` · `:host([readonly])` · `:host([invalid])` 가
|
|
10
|
+
* **마크업으로 설정했을 때뿐 아니라 프로퍼티로 설정했을 때도** 시각에 반영된다.
|
|
11
|
+
* (reflect 가 없으면 `el.disabled = true` 는 화면에 나타나지 않는다.)
|
|
12
|
+
*
|
|
13
|
+
* ## disabled 와 readonly 의 구분
|
|
14
|
+
* - `disabled`: 상호작용 불가 · 흐리게 · **폼 데이터에 포함되지 않음**(HTML 표준 거동)
|
|
15
|
+
* - `readonly`: 값을 **보고 선택·복사할 수 있으나** 수정 불가 · **폼 데이터에는 포함됨**
|
|
16
|
+
* → 조회 화면을 `disabled` 로 잠그면 흐려지고 값도 전송되지 않으므로 `readonly` 를 쓴다.
|
|
17
|
+
*
|
|
18
|
+
* ## 검증
|
|
19
|
+
* `checkValidity()` 가 `invalid` 를 갱신하고 결과를 반환한다.
|
|
20
|
+
* 컴포넌트별 규칙(형식·범위 등)이 있으면 `validateValue()` 를 오버라이드한다(기본은 `required` 만 검사).
|
|
21
|
+
*/
|
|
4
22
|
export class OxFormField extends LitElement {
|
|
5
23
|
constructor() {
|
|
6
24
|
super(...arguments);
|
|
@@ -25,9 +43,46 @@ export class OxFormField extends LitElement {
|
|
|
25
43
|
this._formdataEventHandler = null;
|
|
26
44
|
}
|
|
27
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* 값이 비었는지 판단. 문자열·배열·nullish 를 다룬다.
|
|
48
|
+
* 숫자 `0` 과 `false` 는 **유효한 값**이므로 비었다고 보지 않는다.
|
|
49
|
+
*/
|
|
50
|
+
isEmpty() {
|
|
51
|
+
const value = this.value;
|
|
52
|
+
if (value === undefined || value === null)
|
|
53
|
+
return true;
|
|
54
|
+
if (typeof value === 'string')
|
|
55
|
+
return value.trim().length === 0;
|
|
56
|
+
if (Array.isArray(value))
|
|
57
|
+
return value.length === 0;
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 자체 검증 규칙. 기본은 `required` 만 검사한다.
|
|
62
|
+
* 컴포넌트별 규칙이 있으면 오버라이드한다.
|
|
63
|
+
*/
|
|
64
|
+
validateValue() {
|
|
65
|
+
return !(this.required && this.isEmpty());
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 검증을 실행해 `invalid` 를 갱신하고 결과를 반환한다.
|
|
69
|
+
* 실패 시 `invalid` 이벤트를 올려 폼이 반응할 수 있게 한다.
|
|
70
|
+
*/
|
|
71
|
+
checkValidity() {
|
|
72
|
+
const valid = this.validateValue();
|
|
73
|
+
this.invalid = !valid;
|
|
74
|
+
if (!valid) {
|
|
75
|
+
this.dispatchEvent(new CustomEvent('invalid', { bubbles: true, composed: true, detail: { field: this.name } }));
|
|
76
|
+
}
|
|
77
|
+
return valid;
|
|
78
|
+
}
|
|
28
79
|
appendFormData({ formData }) {
|
|
29
80
|
if (!this.name)
|
|
30
81
|
return;
|
|
82
|
+
/* disabled 컨트롤은 폼 데이터에 포함하지 않는다(HTML 표준 거동).
|
|
83
|
+
readonly 는 수정만 막는 상태이므로 포함한다. */
|
|
84
|
+
if (this.disabled)
|
|
85
|
+
return;
|
|
31
86
|
let values = this.value;
|
|
32
87
|
if (!(values instanceof Array)) {
|
|
33
88
|
values = [this.value];
|
|
@@ -41,8 +96,20 @@ __decorate([
|
|
|
41
96
|
property({ type: String, attribute: true })
|
|
42
97
|
], OxFormField.prototype, "name", void 0);
|
|
43
98
|
__decorate([
|
|
44
|
-
property({ type: Boolean })
|
|
99
|
+
property({ type: Boolean, reflect: true })
|
|
45
100
|
], OxFormField.prototype, "disabled", void 0);
|
|
101
|
+
__decorate([
|
|
102
|
+
property({ type: Boolean, reflect: true })
|
|
103
|
+
], OxFormField.prototype, "readonly", void 0);
|
|
104
|
+
__decorate([
|
|
105
|
+
property({ type: Boolean, reflect: true })
|
|
106
|
+
], OxFormField.prototype, "required", void 0);
|
|
107
|
+
__decorate([
|
|
108
|
+
property({ type: Boolean, reflect: true })
|
|
109
|
+
], OxFormField.prototype, "invalid", void 0);
|
|
110
|
+
__decorate([
|
|
111
|
+
property({ type: String })
|
|
112
|
+
], OxFormField.prototype, "placeholder", void 0);
|
|
46
113
|
__decorate([
|
|
47
114
|
property()
|
|
48
115
|
], OxFormField.prototype, "value", void 0);
|