@haiyue/ui 0.2.1 → 0.2.2
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 +27 -0
- package/README.md +201 -3
- package/dist/adapters/web/controls.js +28 -5
- package/dist/adapters/web/renderer.js +10 -2
- package/dist/adapters/web/styles.d.ts +2 -2
- package/dist/adapters/web/styles.js +26 -5
- package/dist/carousel.d.ts +92 -0
- package/dist/carousel.js +509 -0
- package/dist/collapse.d.ts +13 -0
- package/dist/collapse.js +94 -7
- package/dist/core/types.d.ts +22 -3
- package/dist/core/validation.js +9 -4
- package/dist/index.d.ts +8 -0
- package/dist/index.js +12 -0
- package/dist/internal/qrcodegen.d.ts +94 -0
- package/dist/internal/qrcodegen.js +856 -0
- package/dist/masonry.d.ts +36 -0
- package/dist/masonry.js +171 -0
- package/dist/modal.d.ts +73 -0
- package/dist/modal.js +361 -0
- package/dist/portable/icons.d.ts +5 -0
- package/dist/portable/icons.js +24 -0
- package/dist/portable/index.d.ts +3 -0
- package/dist/portable/index.js +2 -0
- package/dist/portable/responsive.d.ts +20 -0
- package/dist/portable/responsive.js +15 -0
- package/dist/portable/theme.d.ts +6 -1
- package/dist/portable/theme.js +23 -3
- package/dist/qr-code.d.ts +62 -0
- package/dist/qr-code.js +224 -0
- package/package.json +18 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.2.2 - 2026-10-23
|
|
6
|
+
|
|
7
|
+
- 新增 `HYModal` / `<hy-modal>` 及独立 `@haiyue/ui/modal` 入口:支持原生模态层、标题/正文/页脚插槽、确认与取消按钮、可阻止的操作事件、异步确认加载态、独立遮罩/Esc/关闭按钮控制、受控状态、焦点恢复、滚动锁与隐藏内容卸载。
|
|
8
|
+
|
|
9
|
+
- 新增 `HYQRCode` / `<hy-qr-code>` 二维码组件及独立 `@haiyue/ui/qr-code` 入口,支持本地 SVG 编码、Unicode、尺寸/颜色/纠错等级、中央图片或 icon 插槽及留白配置;带图标时默认采用 H 级纠错,允许显式选择其他等级并限制遮挡面积,提供空值/超容量/图标加载失败状态。修复示例中带图标时纠错等级下拉框无法选择的问题。
|
|
10
|
+
|
|
11
|
+
- Collapse 新增 `animated` 与 `animation-duration` / `animationDuration`,支持可配置时长和缓动的展开/收起动画、快速反向切换、内容尺寸变化、减少动态效果偏好与断开清理;收起时立即同步无障碍状态,`destroyOnHidden` 延迟到动画完成后销毁内容。
|
|
12
|
+
|
|
13
|
+
- 新增 `HYCarousel` / `<hy-carousel>` 轮播组件及独立 `@haiyue/ui/carousel` 入口:支持自定义指示点样式/位置/渲染、自动播放与暂停、拖拽、循环、水平/垂直切换、键盘导航、非活动面板 inert、减少动态效果偏好与完整生命周期清理。
|
|
14
|
+
|
|
15
|
+
- 新增可按需导入的 `HYMasonry` / `<hy-masonry>` 瀑布流组件:支持最短列布局、自动或固定列数、最小列宽、间距、动态内容与容器尺寸响应、RTL、布局事件及断开清理;组件示例按类别内字母顺序排列。
|
|
16
|
+
|
|
17
|
+
- 扩展 portable/core 与 Web 适配器的呈现契约:按钮样式/尺寸/矢量图标、文字样式与色调、Tabs 分段模式、Select 呈现配置及字体缩放主题;新增 FormGroup 帮助文字、响应式布局辅助函数,并保持控件焦点、输入与选择状态。
|
|
18
|
+
|
|
3
19
|
## 0.2.1 - 2026-10-20
|
|
4
20
|
|
|
5
21
|
- 新增可按需导入的 `HYTag` / `<hy-tag>` 标签组件,支持语义与自定义配色、三种尺寸、文字或 icon 插槽、可取消的关闭事件、可选择状态、键盘操作和禁用状态;新增 `HYToast` / `<hy-toast>` 轻提示组件,支持五种状态、上/中/下位置、自动或持久显示、最大数量、手动关闭/清空、生命周期事件与完整计时器清理。
|
package/LICENSE
CHANGED
|
@@ -19,3 +19,30 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
Vendored QR Code encoder: src/internal/qrcodegen.ts
|
|
25
|
+
Source: https://github.com/nayuki/QR-Code-generator/blob/master/typescript-javascript/qrcodegen.ts
|
|
26
|
+
Upstream Git blob: c4191ac0b5f8a24d87fd9340ceaa2d3b07d0231a
|
|
27
|
+
Local changes: ES module export, strict indexed-access assertions, and formatting.
|
|
28
|
+
|
|
29
|
+
QR Code generator library (TypeScript)
|
|
30
|
+
|
|
31
|
+
Copyright (c) Project Nayuki. (MIT License)
|
|
32
|
+
https://www.nayuki.io/page/qr-code-generator-library
|
|
33
|
+
|
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
35
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
36
|
+
the Software without restriction, including without limitation the rights to
|
|
37
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
38
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
39
|
+
subject to the following conditions:
|
|
40
|
+
- The above copyright notice and this permission notice shall be included in
|
|
41
|
+
all copies or substantial portions of the Software.
|
|
42
|
+
- The Software is provided "as is", without warranty of any kind, express or
|
|
43
|
+
implied, including but not limited to the warranties of merchantability,
|
|
44
|
+
fitness for a particular purpose and noninfringement. In no event shall the
|
|
45
|
+
authors or copyright holders be liable for any claim, damages or other
|
|
46
|
+
liability, whether in an action of contract, tort or otherwise, arising from,
|
|
47
|
+
out of or in connection with the Software or the use or other dealings in the
|
|
48
|
+
Software.
|
package/README.md
CHANGED
|
@@ -48,6 +48,168 @@ defineHaiyueUI();
|
|
|
48
48
|
Because `defineHaiyueUI()` references the complete component set, prefer focused subpaths in bundle-sensitive
|
|
49
49
|
product entry points.
|
|
50
50
|
|
|
51
|
+
## Modal
|
|
52
|
+
|
|
53
|
+
Import `defineModalComponents` from `@haiyue/ui/modal` and register once. `HYModal` is also exported by the root
|
|
54
|
+
and included in `defineHaiyueUI()`. It provides default confirmation/cancel actions around a native modal
|
|
55
|
+
`<dialog>`, with top-layer rendering, background inertness, focus containment and return focus through shadow roots.
|
|
56
|
+
The existing Dialog entry remains available for surfaces with application-supplied footer actions.
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<hy-modal id="publish" heading="Publish project" width="560" ok-text="Publish">
|
|
60
|
+
<p>Review the project before publishing.</p>
|
|
61
|
+
</hy-modal>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Call `showModal()` / `close(reason?)`, or set the reflected boolean `open` property/attribute.
|
|
65
|
+
`heading` defaults to “Modal”; the `heading` slot overrides its content and the default slot supplies the body.
|
|
66
|
+
`width` defaults to 520 CSS pixels (240–2000), constrained to the viewport. `centered`, `closable`,
|
|
67
|
+
`mask-closable` / `maskClosable`, `keyboard` (Escape), and `show-footer` / `showFooter` all default to true;
|
|
68
|
+
set their attributes to `"false"` or properties to false to disable them independently.
|
|
69
|
+
`ok-text` / `okText` and `cancel-text` / `cancelText` default to “OK” and “Cancel”.
|
|
70
|
+
Use the `footer` slot for application-owned custom actions, replacing both default buttons.
|
|
71
|
+
|
|
72
|
+
Bubbling/composed events are `modal-open`, `modal-ok`, `modal-cancel` and `modal-close`.
|
|
73
|
+
The cancelable `modal-ok` and `modal-cancel` events carry `{ reason }`. Preventing either event keeps the modal open.
|
|
74
|
+
Close reasons are `ok`, `cancel`, `close-button`, `mask`, `escape` and `programmatic`; `close(reason)` also accepts
|
|
75
|
+
an application-specific string. `modal-close` fires once after an actual close, with `{ reason }`.
|
|
76
|
+
With `controlled`, default user actions only emit requests; the application updates `open` or calls `close()`.
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
publish.addEventListener('modal-ok', async event => {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
publish.confirmLoading = true;
|
|
82
|
+
try {
|
|
83
|
+
await saveProject();
|
|
84
|
+
publish.close('ok');
|
|
85
|
+
} finally {
|
|
86
|
+
publish.confirmLoading = false;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
publish.showModal();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`confirm-loading` / `confirmLoading` disables the default actions and user dismissal, shows a spinner and marks the
|
|
93
|
+
surface busy; application `close()` remains available. `confirm-disabled` / `confirmDisabled` only disables confirmation.
|
|
94
|
+
`destroy-on-hidden` / `destroyOnHidden` unmounts light DOM content while closed, retaining the same nodes, values and
|
|
95
|
+
listeners for reopening. It defaults to false. Disconnecting removes modality and scroll locks without emitting a
|
|
96
|
+
close event; a retained `open` attribute reopens on reconnection. Nested modals retain scroll locking until the last closes.
|
|
97
|
+
|
|
98
|
+
Style parts `panel`, `header`, `heading`, `body`, `footer`, `actions`, `ok-button`, `cancel-button`,
|
|
99
|
+
`close-button`, `spinner`. Tokens include `--hy-modal-width`, `--hy-modal-radius`, `--hy-modal-body-padding`,
|
|
100
|
+
`--hy-modal-duration`, `--hy-modal-bg-color`, `--hy-modal-mask-color`, `--hy-modal-mask-blur`, `--hy-modal-shadow-color`,
|
|
101
|
+
`--hy-modal-footer-bg-color` and `--hy-modal-button-radius`; colors inherit semantic theme tokens. Reduced-motion
|
|
102
|
+
preferences suppress the entrance and spinner animations. No sibling component import or automatic registration is needed.
|
|
103
|
+
|
|
104
|
+
## QR Code
|
|
105
|
+
|
|
106
|
+
Import `defineQRCodeComponents` from `@haiyue/ui/qr-code` and call it once. The root registration path also includes
|
|
107
|
+
`<hy-qr-code>`. Values are encoded locally into SVG; no runtime dependency or remote QR generation service is used.
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<hy-qr-code value="https://haiyuestudio.github.io/UI/" size="220"
|
|
111
|
+
icon="/brand.svg" icon-size="32" icon-margin="2" aria-label="Open Haiyue UI"></hy-qr-code>
|
|
112
|
+
|
|
113
|
+
<hy-qr-code value="https://haiyuestudio.github.io/UI/">
|
|
114
|
+
<img slot="icon" src="/brand.svg" alt="" />
|
|
115
|
+
</hy-qr-code>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Set `value` to a URL or text (including UTF-8/Chinese/emoji). `size` defaults to 200 CSS pixels (64–2048).
|
|
119
|
+
`margin` is the quiet zone in modules (default/minimum 4, maximum 16).
|
|
120
|
+
`error-correction-level` / `errorCorrectionLevel` accepts `L`, `M`, `Q`, or `H`. Without an explicit level,
|
|
121
|
+
it defaults to `H` with an icon and `M` otherwise. An explicit level applies with or without an icon.
|
|
122
|
+
An `icon` image URL or content in the `icon` slot adds a center icon; the slot takes precedence over the URL.
|
|
123
|
+
`icon-size` / `iconSize` requests the content size in pixels (default 32), while `icon-margin` / `iconMargin`
|
|
124
|
+
sets its backing padding (default 2). The complete overlay is capped at 20% of the QR matrix width, including padding,
|
|
125
|
+
and scales with responsive sizing. Icon content is decorative and inert. A failed image leaves the plain QR visible.
|
|
126
|
+
|
|
127
|
+
`color` and `background-color` / `backgroundColor` customize the QR colors. The inherited tokens
|
|
128
|
+
`--hy-qr-code-color` and `--hy-qr-code-background-color` default to dark modules on white for both themes.
|
|
129
|
+
Other styling hooks are `--hy-qr-code-icon-radius`, `--hy-qr-code-radius` and parts `frame`, `code`,
|
|
130
|
+
`background`, `modules`, `icon`, `icon-image`, `message`. Keep sufficient contrast and verify your final payload,
|
|
131
|
+
size, colors and icon with the intended scanner; arbitrary custom styling can affect readability.
|
|
132
|
+
|
|
133
|
+
The read-only `status` is `empty`, `ready`, or `error`. Empty values and over-capacity payloads clear the previous code.
|
|
134
|
+
Bubbling, composed events are `qr-render` with `{ value, version, moduleCount, errorCorrectionLevel }` after encoding,
|
|
135
|
+
`qr-error` with `{ message }` on encoding failure, and `qr-icon-error` with `{ src }` on image failure.
|
|
136
|
+
SVG is decorative beneath a labelled image role; set `aria-label` to describe the QR's purpose.
|
|
137
|
+
|
|
138
|
+
The internal encoder is vendored from [Project Nayuki](https://www.nayuki.io/page/qr-code-generator-library),
|
|
139
|
+
upstream Git blob `c4191ac0b5f8a24d87fd9340ceaa2d3b07d0231a`, with its MIT notice retained in source and `LICENSE`.
|
|
140
|
+
Local edits only add an ES module export, strict indexed-access assertions and formatting. Independent decoding tests
|
|
141
|
+
use `jsqr` as a development-only dependency. Internal encoder symbols are excluded from the public component API.
|
|
142
|
+
|
|
143
|
+
## Carousel
|
|
144
|
+
|
|
145
|
+
`@haiyue/ui/carousel` exports `HYCarousel`, `defineCarouselComponents()` and the associated `HYCarousel*` types.
|
|
146
|
+
Registration is explicit; panels are HTML elements in the default slot and stay mounted between changes.
|
|
147
|
+
|
|
148
|
+
```html
|
|
149
|
+
<hy-carousel direction="horizontal" indicator-style="line" indicator-position="bottom"
|
|
150
|
+
autoplay interval="4000" draggable="true" loop="true" aria-label="Featured projects">
|
|
151
|
+
<article>First panel</article>
|
|
152
|
+
<article>Second panel</article>
|
|
153
|
+
<article>Third panel</article>
|
|
154
|
+
</hy-carousel>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
| Attribute / property | Default | Behavior |
|
|
158
|
+
| --- | --- | --- |
|
|
159
|
+
| `index` | `0` | Active panel, zero based |
|
|
160
|
+
| `direction` | `horizontal` | `horizontal` or `vertical`; horizontal supports RTL |
|
|
161
|
+
| `indicator-style` / `indicatorStyle` | `dot` | `dot`, `line`, `number` |
|
|
162
|
+
| `indicator-position` / `indicatorPosition` | `bottom` | `top`, `right`, `bottom`, `left` |
|
|
163
|
+
| `show-indicators` / `showIndicators` | `true` | Set `false` to hide indicators |
|
|
164
|
+
| `show-arrows` / `showArrows` | `true` | Set `false` to hide previous/next buttons |
|
|
165
|
+
| `autoplay` | `false` | Opt-in automatic rotation with a pause/start button |
|
|
166
|
+
| `interval` | `3000` | Milliseconds, normalized to 100–86,400,000 |
|
|
167
|
+
| `draggable` | `true` | Mouse/touch swipe; set `false` to disable |
|
|
168
|
+
| `loop` | `true` | Wrap at the ends; `false` clamps and stops autoplay at the last panel |
|
|
169
|
+
|
|
170
|
+
Use `next()`, `previous()`, `goTo(index)`, `play()` and `pause()` for imperative control.
|
|
171
|
+
The bubbling, composed `change` event includes `{ index, previousIndex, source }`, with sources
|
|
172
|
+
`api`, `indicator`, `arrow`, `keyboard`, `drag`, `autoplay` and `content`.
|
|
173
|
+
Inactive panels are inert and hidden from assistive technology. Arrow keys (matching direction), Home and End
|
|
174
|
+
navigate the focused viewport or indicators; input fields keep their editing keys. Hover temporarily pauses rotation;
|
|
175
|
+
keyboard focus pauses until explicitly resumed. Reduced-motion preferences suppress transitions and initial automatic
|
|
176
|
+
rotation; `play()` permits explicit rotation. Disconnecting releases timers, pointer listeners and animations and restores panel attributes/styles.
|
|
177
|
+
|
|
178
|
+
Customize `::part(indicators)`, `::part(indicator)`, `::part(indicator-active)`, `::part(indicator-content)`,
|
|
179
|
+
`::part(previous)`, `::part(next)`, `::part(playback)` and `::part(viewport)`.
|
|
180
|
+
Tokens include `--hy-carousel-height` (240px), `--hy-carousel-duration` (320ms), `--hy-carousel-indicator-size`,
|
|
181
|
+
`--hy-carousel-indicator-gap`, `--hy-carousel-indicator-offset`, `--hy-carousel-indicator-radius`,
|
|
182
|
+
`--hy-carousel-indicator-color` and `--hy-carousel-indicator-active-color`; other colors inherit semantic theme tokens.
|
|
183
|
+
For arbitrary content, set `indicatorRenderer = (index, active) => Node | string | null` (strings are plain text).
|
|
184
|
+
The component retains the native accessible indicator buttons and click/keyboard behavior around your content.
|
|
185
|
+
Panels should not override the component's positioning, dimensions, visibility or transform.
|
|
186
|
+
|
|
187
|
+
## Masonry layout
|
|
188
|
+
|
|
189
|
+
Import `defineMasonryComponents` from `@haiyue/ui/masonry` and call it once to register `<hy-masonry>`.
|
|
190
|
+
Place HTML elements of varying heights in the default slot:
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<hy-masonry min-column-width="240" gap="16" aria-label="Project gallery">
|
|
194
|
+
<article>First card…</article>
|
|
195
|
+
<article>Another card with more content…</article>
|
|
196
|
+
<article><img src="preview.jpg" alt="Project preview" style="width: 100%" /></article>
|
|
197
|
+
</hy-masonry>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`columns` defaults to `0` (automatic); positive values request a fixed number of columns.
|
|
201
|
+
`min-column-width` (default `240`) controls automatic sizing; `gap` (default `16`) sets both gaps in CSS pixels.
|
|
202
|
+
Columns shrink to fit narrow containers. Each item goes into the shortest column, starting at the inline start,
|
|
203
|
+
including RTL layouts. DOM, screen-reader and keyboard order remain the original slot order.
|
|
204
|
+
Items keep their own content, theme and semantics; the component supplies positioning, border-box width and zero outer margins.
|
|
205
|
+
Use padding inside cards and avoid overriding item positioning/width. Text nodes and named slots are not layout items.
|
|
206
|
+
|
|
207
|
+
Container resizing, item resizing (including image loads), adding/removing items and toggling `hidden` automatically
|
|
208
|
+
schedule a layout. Call `refresh()` after changes outside those observations (for example, a stylesheet rule that
|
|
209
|
+
reveals a previously hidden item). `layout-change` bubbles across shadow roots with `{ columns, itemCount, height }`
|
|
210
|
+
after geometry changes; `itemCount` excludes hidden items. Style the transparent container with `::part(layout)`.
|
|
211
|
+
Observers and queued frames are released on disconnect, and internal item CSS properties are restored.
|
|
212
|
+
|
|
51
213
|
## Tag and toast
|
|
52
214
|
|
|
53
215
|
Tags provide compact semantic labels with optional checked and close interactions. Toasts are an imperative,
|
|
@@ -116,7 +278,7 @@ button, or the application calls `search()`. Its detail contains `{ value, valid
|
|
|
116
278
|
available. Style the `control`, `input`, `prefix`, `suffix`, and `search-button` parts or the
|
|
117
279
|
`--hy-input-affix-*` and `--hy-input-search-*` tokens.
|
|
118
280
|
|
|
119
|
-
## Portable component semantics (M24 G03/G05
|
|
281
|
+
## Portable component semantics (M24 G03/G05)
|
|
120
282
|
|
|
121
283
|
`@haiyue/ui/core` provides platform-independent component types, validation, selection/input/action
|
|
122
284
|
controllers and the `PlatformView` lifetime contract. `@haiyue/ui/portable` provides `HYPage`, selector
|
|
@@ -150,7 +312,7 @@ elements. It introduces no runtime package dependencies or automatic registratio
|
|
|
150
312
|
[Web adapter guide](docs/web-adapter.md) and [interactive example](examples/portable-ui/index.html).
|
|
151
313
|
Run `npm run test:web-adapter` for Chromium/WebKit regression coverage.
|
|
152
314
|
|
|
153
|
-
These APIs are
|
|
315
|
+
These APIs are included in the package and remain under development. Mobile mapping is G06, desktop mapping
|
|
154
316
|
G08, and ShaderLab shared-page migration G07. Existing Web component APIs below remain independent. See the
|
|
155
317
|
[portable UI guide](docs/portable-ui.md) and [typed composition example](test/portable/fixtures/settings-page.ts).
|
|
156
318
|
Run `npm run verify:portable` for semantics, no-DOM types, and an isolated offline tarball consumer.
|
|
@@ -306,6 +468,24 @@ group to one. Configure `iconPosition`, `collapsible`, `bordered`, `ghost`, and
|
|
|
306
468
|
to a labelled region, and user changes emit the composed `collapse-change` event with the active keys and
|
|
307
469
|
changed panel. The component exposes structural shadow parts from `collapse` and `panel` through `body`.
|
|
308
470
|
|
|
471
|
+
Enable `animated` / `animated = true` for smooth opening and closing. Animation is off by default;
|
|
472
|
+
initial rendering stays immediate. Set `animation-duration` / `animationDuration` in milliseconds
|
|
473
|
+
(default 200, range 0–10,000); zero disables animation. Without the attribute, the inherited
|
|
474
|
+
`--hy-collapse-animation-duration` token (for example `250ms`) supplies the duration.
|
|
475
|
+
`--hy-collapse-animation-easing` customizes the easing (default `ease`).
|
|
476
|
+
|
|
477
|
+
```html
|
|
478
|
+
<hy-collapse animated animation-duration="250" destroy-on-hidden></hy-collapse>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Rapid toggles reverse from the current height, and content resizing during expansion updates the target height.
|
|
482
|
+
Closed panels become inert and hidden from assistive technology immediately; focus returns to their header
|
|
483
|
+
when needed. With `destroyOnHidden`, content is removed after the closing animation finishes.
|
|
484
|
+
Disabling animation, setting duration to zero, or enabling reduced-motion preferences settles immediately.
|
|
485
|
+
Animations and content observers are released on disconnect. The existing `collapse-change` event still fires
|
|
486
|
+
when the expanded state changes, rather than waiting for animation completion. These options extend `HYCollapse`;
|
|
487
|
+
no new package exports or registration functions are required.
|
|
488
|
+
|
|
309
489
|
## Affix
|
|
310
490
|
|
|
311
491
|
Import `@haiyue/ui/affix` to pin actions, navigation, or status content after it crosses a viewport threshold.
|
|
@@ -655,7 +835,7 @@ npm run release:check
|
|
|
655
835
|
```
|
|
656
836
|
|
|
657
837
|
Candidates: `.artifacts/packages/`. Review API changes before `npm run api:update`.
|
|
658
|
-
See [release review](review/release-0.
|
|
838
|
+
See [release review](review/release-0.2.2.md).
|
|
659
839
|
|
|
660
840
|
### 共享组件目录(开发中)
|
|
661
841
|
|
|
@@ -674,3 +854,21 @@ See [release review](review/release-0.1.2.md).
|
|
|
674
854
|
Native 仓库的 `examples/ui-validation/mobile` 提供独立「Haiyue UI」组件实验室;折叠内容、步骤导航与按钮组是基础节点组合示例,不是新增协议节点。
|
|
675
855
|
|
|
676
856
|
Web 的 `hy-badge` 新增可选 `literal` 属性(默认为 false):启用后 `count` 按原始文字显示,保留 0、前导零和大数字。普通计数的隐藏零值、截断,以及 status/text 模式保持原行为;共享 Badge 适配显式启用此模式。
|
|
857
|
+
|
|
858
|
+
The portable presentation contract adds optional semantic `Button.variant`
|
|
859
|
+
(`primary`, `secondary`, `quiet`, `danger`), `size` and vector `icon` names;
|
|
860
|
+
`Text.textStyle` (`title`, `body`, `caption`) and `tone`; `Tabs.variant`
|
|
861
|
+
(`underline`, `segmented`); and native `Select.presentation`. Existing nodes stay
|
|
862
|
+
valid. `HYTheme.typography` scales with fontScale, and optional selected, pressed,
|
|
863
|
+
disabled, elevated and onAccent colors map to inherited `--hy-` properties.
|
|
864
|
+
`HYIconName`, `HYButtonVariant`, `HYTextStyle` and `HYTypography` are explicitly
|
|
865
|
+
exported from `@haiyue/ui/core`; `HY_ICONS` / `hyIconSvg` are explicitly exported
|
|
866
|
+
from `@haiyue/ui/portable`, with no platform access or registration at import time.
|
|
867
|
+
|
|
868
|
+
Portable forms accept optional `FormGroup.description`, rendered as visible helper
|
|
869
|
+
text and included in associated controls' accessible descriptions. Updating help
|
|
870
|
+
or validation errors preserves the existing input, selection and composition.
|
|
871
|
+
`@haiyue/ui/portable` explicitly exports `hyResponsiveGrid`,
|
|
872
|
+
`hyResponsiveWorkspace`, `HYResponsiveGridOptions` and
|
|
873
|
+
`HYResponsiveWorkspaceOptions`. These return logical-pixel layout data without
|
|
874
|
+
reading window or platform globals, so hosts can use actual split-window sizes.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hyIconSvg } from '../../portable/icons.js';
|
|
1
2
|
import { HYButtonController, HYToggleController, HYSliderController } from '../../core/actions.js';
|
|
2
3
|
import { HYSelectionController } from '../../core/selection.js';
|
|
3
4
|
import { HYTextController } from '../../core/text.js';
|
|
@@ -29,6 +30,7 @@ export function definePortableWebComponents() {
|
|
|
29
30
|
defineDialogComponents();
|
|
30
31
|
}
|
|
31
32
|
export const componentTags = Object.freeze({ Checkbox: 'hy-checkbox', RadioGroup: 'div', Rate: 'hy-rate', Badge: 'hy-badge', Divider: 'hy-divider', Button: 'hy-button', Toggle: 'hy-checkbox', Select: 'hy-select', Tabs: 'hy-tabs', TextInput: 'hy-input', Slider: 'hy-range', Dialog: 'hy-dialog' });
|
|
33
|
+
const selectionProps = ({ value, options, disabled }) => ({ value, options, ...(disabled === undefined ? {} : { disabled }) });
|
|
32
34
|
let radioGroupSequence = 0;
|
|
33
35
|
function toggleProps(node) { const { checked, disabled, visible } = node.props; return { checked, ...(disabled === undefined ? {} : { disabled }), ...(visible === undefined ? {} : { visible }) }; }
|
|
34
36
|
export function createControl(node, context, revision) {
|
|
@@ -43,7 +45,7 @@ export function createControl(node, context, revision) {
|
|
|
43
45
|
} ; if (next.kind === 'Divider')
|
|
44
46
|
attribute(host, 'label', next.props.text); }, focus() { }, dispose() { } };
|
|
45
47
|
if (node.kind === 'RadioGroup') {
|
|
46
|
-
const controller = new HYSelectionController(node.key, node.props, emit);
|
|
48
|
+
const controller = new HYSelectionController(node.key, selectionProps(node.props), emit);
|
|
47
49
|
let props = node.props;
|
|
48
50
|
const buttons = new Map(), groupName = `hy-portable-radio-${++radioGroupSequence}`;
|
|
49
51
|
host.setAttribute('role', 'radiogroup');
|
|
@@ -57,7 +59,7 @@ export function createControl(node, context, revision) {
|
|
|
57
59
|
if (next.kind !== 'RadioGroup')
|
|
58
60
|
return;
|
|
59
61
|
props = next.props;
|
|
60
|
-
controller.update(props);
|
|
62
|
+
controller.update(selectionProps(props));
|
|
61
63
|
for (const [value, button] of buttons)
|
|
62
64
|
if (!props.options.some(o => o.value === value)) {
|
|
63
65
|
button.remove();
|
|
@@ -106,6 +108,10 @@ export function createControl(node, context, revision) {
|
|
|
106
108
|
const label = document.createElement('span');
|
|
107
109
|
label.dataset.hyButtonLabel = '';
|
|
108
110
|
button.append(label);
|
|
111
|
+
const icon = document.createElement('span');
|
|
112
|
+
icon.slot = 'icon';
|
|
113
|
+
icon.setAttribute('aria-hidden', 'true');
|
|
114
|
+
let lastIcon;
|
|
109
115
|
const controller = new HYButtonController(node.key, node.props, emit);
|
|
110
116
|
listen(button, 'click', () => controller.activate(reason));
|
|
111
117
|
return {
|
|
@@ -115,6 +121,20 @@ export function createControl(node, context, revision) {
|
|
|
115
121
|
controller.update(next.props);
|
|
116
122
|
if (label.textContent !== next.props.text)
|
|
117
123
|
label.textContent = next.props.text;
|
|
124
|
+
attribute(button, 'data-hy-variant', next.props.variant ?? 'secondary');
|
|
125
|
+
attribute(button, 'data-hy-size', next.props.size ?? 'regular');
|
|
126
|
+
if (lastIcon !== next.props.icon) {
|
|
127
|
+
icon.innerHTML = next.props.icon ? hyIconSvg(next.props.icon) : '';
|
|
128
|
+
lastIcon = next.props.icon;
|
|
129
|
+
}
|
|
130
|
+
if (next.props.icon) {
|
|
131
|
+
if (!icon.parentNode)
|
|
132
|
+
button.append(icon);
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
icon.remove();
|
|
136
|
+
flag(button, 'icon-only', !!next.props.icon && !next.props.text);
|
|
137
|
+
flag(button, 'loading', !!next.props.busy);
|
|
118
138
|
flag(button, 'disabled', !!next.props.disabled || !!next.props.busy);
|
|
119
139
|
attribute(button, 'aria-busy', next.props.busy ? 'true' : undefined);
|
|
120
140
|
},
|
|
@@ -144,7 +164,7 @@ export function createControl(node, context, revision) {
|
|
|
144
164
|
const select = host;
|
|
145
165
|
if (select instanceof HYTabs)
|
|
146
166
|
select.controlled = true;
|
|
147
|
-
const controller = new HYSelectionController(node.key, node.props, emit);
|
|
167
|
+
const controller = new HYSelectionController(node.key, selectionProps(node.props), emit);
|
|
148
168
|
let previous;
|
|
149
169
|
controller.attach({
|
|
150
170
|
apply(props) {
|
|
@@ -180,8 +200,11 @@ export function createControl(node, context, revision) {
|
|
|
180
200
|
const target = [...select.shadowRoot.querySelectorAll('button')].find(item => item.dataset.value === select.value);
|
|
181
201
|
target?.focus();
|
|
182
202
|
}, true);
|
|
183
|
-
return { update(next) { if (next.kind === 'Select' || next.kind === 'Tabs')
|
|
184
|
-
|
|
203
|
+
return { update(next) { if (next.kind === 'Select' || next.kind === 'Tabs') {
|
|
204
|
+
if (next.kind === 'Tabs')
|
|
205
|
+
attribute(host, 'data-hy-variant', next.props.variant ?? 'underline');
|
|
206
|
+
controller.update(selectionProps(next.props));
|
|
207
|
+
} },
|
|
185
208
|
focus() { if (select instanceof HYSelect)
|
|
186
209
|
select.shadowRoot.querySelector('select').focus();
|
|
187
210
|
else
|
|
@@ -192,11 +192,15 @@ export class HYWebRenderer {
|
|
|
192
192
|
}
|
|
193
193
|
if (node.kind === 'FormGroup') {
|
|
194
194
|
entry.label = document.createElement('legend');
|
|
195
|
+
entry.help = document.createElement('span');
|
|
196
|
+
entry.help.dataset.hyHelp = '';
|
|
197
|
+
element.append(entry.help);
|
|
195
198
|
entry.error = document.createElement('span');
|
|
196
199
|
entry.error.dataset.hyError = '';
|
|
197
200
|
entry.label.id = `${entry.id}-label`;
|
|
198
201
|
entry.error.id = `${entry.id}-error`;
|
|
199
|
-
element.
|
|
202
|
+
element.prepend(entry.label);
|
|
203
|
+
element.append(entry.error);
|
|
200
204
|
}
|
|
201
205
|
if (node.kind === 'PlatformView') {
|
|
202
206
|
const factory = this.options.platformViews?.[node.props.slot];
|
|
@@ -218,6 +222,8 @@ export class HYWebRenderer {
|
|
|
218
222
|
flag(element, 'hidden', node.props.visible === false);
|
|
219
223
|
entry.control?.update(node, revision);
|
|
220
224
|
if (node.kind === 'Text') {
|
|
225
|
+
attribute(element, 'data-hy-text-style', node.props.textStyle ?? 'body');
|
|
226
|
+
attribute(element, 'data-hy-tone', node.props.tone ?? 'default');
|
|
221
227
|
if (element.textContent !== node.props.text)
|
|
222
228
|
element.textContent = node.props.text;
|
|
223
229
|
element.style.whiteSpace = node.props.wrap === false ? 'pre' : 'pre-wrap';
|
|
@@ -242,6 +248,8 @@ export class HYWebRenderer {
|
|
|
242
248
|
}
|
|
243
249
|
if (node.kind === 'FormGroup') {
|
|
244
250
|
entry.label.textContent = node.props.label;
|
|
251
|
+
entry.help.textContent = node.props.description ?? '';
|
|
252
|
+
entry.help.hidden = !node.props.description;
|
|
245
253
|
entry.error.textContent = node.props.error ?? '';
|
|
246
254
|
entry.error.hidden = !node.props.error;
|
|
247
255
|
}
|
|
@@ -323,7 +331,7 @@ export class HYWebRenderer {
|
|
|
323
331
|
group = group.parent ? this.#nodes.get(group.parent) : undefined;
|
|
324
332
|
const groupProps = group?.node.kind === 'FormGroup' ? group.node.props : undefined;
|
|
325
333
|
const label = node.props.a11y?.label ?? (entry.control || node.kind === 'TextArea' ? groupProps?.label : undefined);
|
|
326
|
-
const description = [node.props.a11y?.description, node.props.a11y?.error ?? groupProps?.error].filter(Boolean).join('. ');
|
|
334
|
+
const description = [node.props.a11y?.description, groupProps?.description, node.props.a11y?.error ?? groupProps?.error].filter(Boolean).join('. ');
|
|
327
335
|
const a11y = label === undefined ? undefined : { ...node.props.a11y, label, ...(groupProps?.error && !node.props.a11y?.error ? { error: groupProps.error } : {}) };
|
|
328
336
|
applyAccessibility(element, a11y);
|
|
329
337
|
const targets = element instanceof HYTabs ? [...element.shadowRoot.querySelectorAll('[role=tablist],button')]
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** Only the renderer-owned wrapper and owned ShadowRoots receive these rules. */
|
|
2
|
-
export declare const rootCSS = "\n[data-hy-portable-root] { box-sizing:border-box; min-width:0; min-height:0; width:100%; height:100%;\n color:var(--hy-text-color,#d8e2f2); background:var(--hy-surface-color,#171d28);\n font:var(--hy-font-size,14px)/var(--hy-line-height,22px) system-ui,sans-serif; }\n[data-hy-portable-root] [data-hy-key] { box-sizing:border-box; min-width:0; min-height:0; }\n[data-hy-portable-root] [data-hy-key][hidden] { display:none !important; }\n[data-hy-portable-root] [data-hy-kind=Row] { display:flex; flex-direction:row; }\n[data-hy-portable-root] [data-hy-kind=RadioGroup] { display:flex; flex-wrap:wrap; gap:var(--hy-gap,8px); }\n[data-hy-portable-root] [data-hy-kind=RadioGroup] hy-radio { font:inherit; min-height:44px; }\n[data-hy-portable-root] [data-hy-kind=Column] { display:flex; flex-direction:column; }\n[data-hy-portable-root] [data-hy-kind=Grid], [data-hy-portable-root] [data-hy-kind=Overlay] { display:grid; }\n[data-hy-portable-root] [data-hy-kind=Text] { display:block; overflow-wrap:anywhere; white-space:pre-wrap; }\n[data-hy-portable-root] [data-hy-kind=Icon] { width:1.25em; height:1.25em; object-fit:contain; }\n[data-hy-portable-root] [data-hy-kind=FormGroup] { border:0; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--hy-gap,8px); }\n[data-hy-portable-root] [data-hy-kind=FormGroup] > legend { padding:0; }\n[data-hy-portable-root] [data-hy-error] { color:var(--hy-invalid-border-color,#d65c7c); font-size:.9em; }\n[data-hy-portable-root] textarea { box-sizing:border-box; width:100%; min-height:5em; resize:vertical; padding:8px;\n color:var(--hy-text-color,#d8e2f2); background:var(--hy-input-bg-color,#121822); border:1px solid var(--hy-border-color,#303746);\n border-radius:var(--hy-radius,4px); font:inherit; }\n[data-hy-portable-root] textarea:focus-visible { outline:2px solid var(--hy-focus-ring-color,#8177f2); outline-offset:1px; }\n[data-hy-portable-root] [data-hy-slot-group] { display:contents; }\n[data-hy-portable-root][data-hy-reduced-motion] { --hy-portable-transition:none; }\n@media(prefers-reduced-motion:reduce) { [data-hy-portable-root] { --hy-portable-transition:none; } }\n";
|
|
3
|
-
export declare const componentCSS = "\n:host { --hy-input-font:var(--hy-font-size,14px)/var(--hy-line-height,22px) system-ui,sans-serif;
|
|
2
|
+
export declare const rootCSS = "\n[data-hy-portable-root] { box-sizing:border-box; min-width:0; min-height:0; width:100%; height:100%;\n color:var(--hy-text-color,#d8e2f2); background:var(--hy-surface-color,#171d28);\n font:var(--hy-font-size,14px)/var(--hy-line-height,22px) system-ui,sans-serif; }\n[data-hy-portable-root] [data-hy-key] { box-sizing:border-box; min-width:0; min-height:0; }\n[data-hy-portable-root] [data-hy-key][hidden] { display:none !important; }\n[data-hy-portable-root] [data-hy-kind=Row] { display:flex; flex-direction:row; }\n[data-hy-portable-root] [data-hy-kind=RadioGroup] { display:flex; flex-wrap:wrap; gap:var(--hy-gap,8px); }\n[data-hy-portable-root] [data-hy-kind=RadioGroup] hy-radio { font:inherit; min-height:44px; }\n[data-hy-portable-root] [data-hy-kind=Column] { display:flex; flex-direction:column; }\n[data-hy-portable-root] [data-hy-kind=Grid], [data-hy-portable-root] [data-hy-kind=Overlay] { display:grid; }\n[data-hy-portable-root] [data-hy-kind=Text] { display:block; overflow-wrap:anywhere; white-space:pre-wrap; }\n[data-hy-portable-root] [data-hy-kind=Icon] { width:1.25em; height:1.25em; object-fit:contain; }\n[data-hy-portable-root] [data-hy-kind=FormGroup] { border:0; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--hy-gap,8px); }\n[data-hy-portable-root] [data-hy-kind=FormGroup] > legend { padding:0; }\n[data-hy-portable-root] [data-hy-help] { color:var(--hy-secondary-text-color); font-size:var(--hy-caption-font-size,12px); line-height:var(--hy-caption-line-height,18px); }\n[data-hy-portable-root] [data-hy-error] { color:var(--hy-invalid-border-color,#d65c7c); font-size:.9em; }\n[data-hy-portable-root] textarea { box-sizing:border-box; width:100%; min-height:5em; resize:vertical; padding:8px;\n color:var(--hy-text-color,#d8e2f2); background:var(--hy-input-bg-color,#121822); border:1px solid var(--hy-border-color,#303746);\n border-radius:var(--hy-radius,4px); font:inherit; }\n[data-hy-portable-root] textarea:focus-visible { outline:2px solid var(--hy-focus-ring-color,#8177f2); outline-offset:1px; }\n[data-hy-portable-root] [data-hy-text-style=title] { font-size:var(--hy-title-font-size,22px); line-height:var(--hy-title-line-height,30px); font-weight:var(--hy-title-font-weight,700); }\n[data-hy-portable-root] [data-hy-text-style=caption] { font-size:var(--hy-caption-font-size,12px); line-height:var(--hy-caption-line-height,18px); font-weight:var(--hy-caption-font-weight,400); }\n[data-hy-portable-root] [data-hy-text-style=body] { font-size:var(--hy-body-font-size,var(--hy-font-size,14px)); line-height:var(--hy-body-line-height,var(--hy-line-height,22px)); font-weight:var(--hy-body-font-weight,400); }\n[data-hy-portable-root] [data-hy-tone=muted] { color:var(--hy-secondary-text-color); }\n[data-hy-portable-root] [data-hy-tone=danger] { color:var(--hy-invalid-border-color); }\n[data-hy-portable-root] [data-hy-kind=Button] [slot=icon] svg { display:block; width:20px; height:20px; }\n[data-hy-portable-root] [data-hy-slot-group] { display:contents; }\n[data-hy-portable-root][data-hy-reduced-motion] { --hy-portable-transition:none; }\n@media(prefers-reduced-motion:reduce) { [data-hy-portable-root] { --hy-portable-transition:none; } }\n";
|
|
3
|
+
export declare const componentCSS = "\n:host { --hy-input-font:var(--hy-font-size,14px)/var(--hy-line-height,22px) system-ui,sans-serif; --hy-input-radius:var(--hy-radius,8px); font:inherit;\n --hy-panel-bg-color:var(--hy-surface-color,#171d28);\n --hy-menu-bg-color:var(--hy-surface-color,#171d28); --hy-dialog-bg-color:var(--hy-surface-color,#171d28);\n --hy-dialog-body-color:var(--hy-text-color,#d8e2f2); --hy-dialog-footer-bg-color:var(--hy-surface-color,#171d28);\n --hy-accent-strong-color:var(--hy-accent-color,#8177f2);\n --hy-active-text-color:var(--hy-text-color,#d8e2f2); --hy-hover-bg-color:var(--hy-input-bg-color,#121822);\n --hy-focus-border-color:var(--hy-focus-ring-color,#8177f2); --hy-dialog-radius:var(--hy-radius,8px); }\nbutton, input, select { font:inherit; min-height:2em; border-radius:var(--hy-radius,4px); }\n/* The semantic host owns the touch target. Its actual interactive element\n must fill it too; otherwise a 44px portable Button still paints at 28px. */\n:host(hy-button), :host(hy-input), :host(hy-select) { display:inline-flex; }\n:host(hy-button) button, :host(hy-input) input, :host(hy-select) select {\n box-sizing:border-box; width:100%; min-height:inherit; height:100%; padding-inline:12px;\n}\n:host(hy-select) .wrap, :host(hy-input) .control { width:100%; min-height:inherit; height:100%; box-sizing:border-box; }\n:host(hy-input) input { min-height:0; }\nselect { height:auto; padding-block:4px; }\n:host(hy-input) input { height:auto; align-self:stretch; padding-block:0; border-radius:0; outline:none; }\n.box, .box::after, .handle { transition:var(--hy-portable-transition,background .12s ease); }\n:host(hy-checkbox) { width:auto; }\n:host(hy-checkbox) label, :host(hy-checkbox) .label { white-space:normal; }\n:host(hy-checkbox) input { min-height:0; }\n:host(hy-tabs) { height:auto; grid-template-rows:auto minmax(0,1fr); }\n:host(hy-tabs) .tabs { align-items:stretch; }\n:host(hy-tabs) button { height:auto; white-space:normal; }\n:host(hy-range) .handle { min-height:0; }\n:host(hy-dialog) .header { padding:16px 20px; }\n:host(hy-dialog) h2 { font-size:calc(var(--hy-font-size,14px) + 6px); }\n:host(hy-dialog) .body { display:flex; flex-direction:column; gap:var(--hy-gap,8px); padding:20px; }\n:host(hy-dialog) .body > slot { display:contents; }\n:host(hy-dialog) .footer { box-sizing:border-box; min-height:0; padding:12px 20px; }\n\n/* Portable variants leave individual Web Component APIs unchanged. */\n:host(hy-button) button { border:1px solid var(--hy-border-color); background:var(--hy-surface-color); color:var(--hy-text-color); border-radius:var(--hy-radius,8px); transition:background .12s ease; }\n:host(hy-button[data-hy-variant=primary]) button { background:var(--hy-accent-color); border-color:transparent; color:var(--hy-on-accent-color,#fff); }\n:host(hy-button[data-hy-variant=danger]) button { background:var(--hy-invalid-border-color); border-color:transparent; color:var(--hy-on-accent-color,#fff); }\n:host(hy-button[data-hy-variant=quiet]) button { background:transparent; border-color:transparent; }\n:host(hy-button) button:hover:not(:disabled), :host(hy-button) button:active:not(:disabled) { background:var(--hy-pressed-bg-color,var(--hy-selected-bg-color)); }\n:host(hy-button) button:disabled { opacity:.45; }\n:host(hy-button[data-hy-size=compact]) button { padding-inline:8px; }\n:host(hy-button[icon-only]) button { padding:0; width:100%; }\n:host(hy-tabs) button { min-height:44px; padding:8px 14px; border:0; border-radius:0; background:transparent; color:var(--hy-secondary-text-color); border-bottom:2px solid transparent; }\n:host(hy-tabs) button[aria-selected=true] { color:var(--hy-text-color); border-bottom-color:var(--hy-accent-color); }\n:host(hy-tabs[data-hy-variant=segmented]) .tabs { padding:4px; gap:4px; border-radius:var(--hy-radius,8px); background:var(--hy-input-bg-color); }\n:host(hy-tabs[data-hy-variant=segmented]) button { border:0; border-radius:var(--hy-radius,8px); }\n:host(hy-tabs[data-hy-variant=segmented]) button[aria-selected=true] { background:var(--hy-selected-bg-color,var(--hy-accent-color)); }\nbutton:focus-visible, select:focus-visible { outline:2px solid var(--hy-focus-ring-color,#8177f2); outline-offset:1px; }\n[aria-invalid=true] { border-color:var(--hy-invalid-border-color,#d65c7c); }\n";
|
|
@@ -14,21 +14,28 @@ export const rootCSS = `
|
|
|
14
14
|
[data-hy-portable-root] [data-hy-kind=Icon] { width:1.25em; height:1.25em; object-fit:contain; }
|
|
15
15
|
[data-hy-portable-root] [data-hy-kind=FormGroup] { border:0; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--hy-gap,8px); }
|
|
16
16
|
[data-hy-portable-root] [data-hy-kind=FormGroup] > legend { padding:0; }
|
|
17
|
+
[data-hy-portable-root] [data-hy-help] { color:var(--hy-secondary-text-color); font-size:var(--hy-caption-font-size,12px); line-height:var(--hy-caption-line-height,18px); }
|
|
17
18
|
[data-hy-portable-root] [data-hy-error] { color:var(--hy-invalid-border-color,#d65c7c); font-size:.9em; }
|
|
18
19
|
[data-hy-portable-root] textarea { box-sizing:border-box; width:100%; min-height:5em; resize:vertical; padding:8px;
|
|
19
20
|
color:var(--hy-text-color,#d8e2f2); background:var(--hy-input-bg-color,#121822); border:1px solid var(--hy-border-color,#303746);
|
|
20
21
|
border-radius:var(--hy-radius,4px); font:inherit; }
|
|
21
22
|
[data-hy-portable-root] textarea:focus-visible { outline:2px solid var(--hy-focus-ring-color,#8177f2); outline-offset:1px; }
|
|
23
|
+
[data-hy-portable-root] [data-hy-text-style=title] { font-size:var(--hy-title-font-size,22px); line-height:var(--hy-title-line-height,30px); font-weight:var(--hy-title-font-weight,700); }
|
|
24
|
+
[data-hy-portable-root] [data-hy-text-style=caption] { font-size:var(--hy-caption-font-size,12px); line-height:var(--hy-caption-line-height,18px); font-weight:var(--hy-caption-font-weight,400); }
|
|
25
|
+
[data-hy-portable-root] [data-hy-text-style=body] { font-size:var(--hy-body-font-size,var(--hy-font-size,14px)); line-height:var(--hy-body-line-height,var(--hy-line-height,22px)); font-weight:var(--hy-body-font-weight,400); }
|
|
26
|
+
[data-hy-portable-root] [data-hy-tone=muted] { color:var(--hy-secondary-text-color); }
|
|
27
|
+
[data-hy-portable-root] [data-hy-tone=danger] { color:var(--hy-invalid-border-color); }
|
|
28
|
+
[data-hy-portable-root] [data-hy-kind=Button] [slot=icon] svg { display:block; width:20px; height:20px; }
|
|
22
29
|
[data-hy-portable-root] [data-hy-slot-group] { display:contents; }
|
|
23
30
|
[data-hy-portable-root][data-hy-reduced-motion] { --hy-portable-transition:none; }
|
|
24
31
|
@media(prefers-reduced-motion:reduce) { [data-hy-portable-root] { --hy-portable-transition:none; } }
|
|
25
32
|
`;
|
|
26
33
|
export const componentCSS = `
|
|
27
|
-
:host { --hy-input-font:var(--hy-font-size,14px)/var(--hy-line-height,22px) system-ui,sans-serif; font:inherit;
|
|
28
|
-
--hy-panel-bg-color:var(--hy-surface-color,#171d28);
|
|
34
|
+
:host { --hy-input-font:var(--hy-font-size,14px)/var(--hy-line-height,22px) system-ui,sans-serif; --hy-input-radius:var(--hy-radius,8px); font:inherit;
|
|
35
|
+
--hy-panel-bg-color:var(--hy-surface-color,#171d28);
|
|
29
36
|
--hy-menu-bg-color:var(--hy-surface-color,#171d28); --hy-dialog-bg-color:var(--hy-surface-color,#171d28);
|
|
30
37
|
--hy-dialog-body-color:var(--hy-text-color,#d8e2f2); --hy-dialog-footer-bg-color:var(--hy-surface-color,#171d28);
|
|
31
|
-
--hy-accent-strong-color:var(--hy-accent-color,#8177f2);
|
|
38
|
+
--hy-accent-strong-color:var(--hy-accent-color,#8177f2);
|
|
32
39
|
--hy-active-text-color:var(--hy-text-color,#d8e2f2); --hy-hover-bg-color:var(--hy-input-bg-color,#121822);
|
|
33
40
|
--hy-focus-border-color:var(--hy-focus-ring-color,#8177f2); --hy-dialog-radius:var(--hy-radius,8px); }
|
|
34
41
|
button, input, select { font:inherit; min-height:2em; border-radius:var(--hy-radius,4px); }
|
|
@@ -41,7 +48,7 @@ button, input, select { font:inherit; min-height:2em; border-radius:var(--hy-rad
|
|
|
41
48
|
:host(hy-select) .wrap, :host(hy-input) .control { width:100%; min-height:inherit; height:100%; box-sizing:border-box; }
|
|
42
49
|
:host(hy-input) input { min-height:0; }
|
|
43
50
|
select { height:auto; padding-block:4px; }
|
|
44
|
-
input { height:
|
|
51
|
+
:host(hy-input) input { height:auto; align-self:stretch; padding-block:0; border-radius:0; outline:none; }
|
|
45
52
|
.box, .box::after, .handle { transition:var(--hy-portable-transition,background .12s ease); }
|
|
46
53
|
:host(hy-checkbox) { width:auto; }
|
|
47
54
|
:host(hy-checkbox) label, :host(hy-checkbox) .label { white-space:normal; }
|
|
@@ -56,7 +63,21 @@ input { height:var(--hy-input-height,auto); padding-block:4px; }
|
|
|
56
63
|
:host(hy-dialog) .body > slot { display:contents; }
|
|
57
64
|
:host(hy-dialog) .footer { box-sizing:border-box; min-height:0; padding:12px 20px; }
|
|
58
65
|
|
|
59
|
-
|
|
66
|
+
/* Portable variants leave individual Web Component APIs unchanged. */
|
|
67
|
+
:host(hy-button) button { border:1px solid var(--hy-border-color); background:var(--hy-surface-color); color:var(--hy-text-color); border-radius:var(--hy-radius,8px); transition:background .12s ease; }
|
|
68
|
+
:host(hy-button[data-hy-variant=primary]) button { background:var(--hy-accent-color); border-color:transparent; color:var(--hy-on-accent-color,#fff); }
|
|
69
|
+
:host(hy-button[data-hy-variant=danger]) button { background:var(--hy-invalid-border-color); border-color:transparent; color:var(--hy-on-accent-color,#fff); }
|
|
70
|
+
:host(hy-button[data-hy-variant=quiet]) button { background:transparent; border-color:transparent; }
|
|
71
|
+
:host(hy-button) button:hover:not(:disabled), :host(hy-button) button:active:not(:disabled) { background:var(--hy-pressed-bg-color,var(--hy-selected-bg-color)); }
|
|
72
|
+
:host(hy-button) button:disabled { opacity:.45; }
|
|
73
|
+
:host(hy-button[data-hy-size=compact]) button { padding-inline:8px; }
|
|
74
|
+
:host(hy-button[icon-only]) button { padding:0; width:100%; }
|
|
75
|
+
:host(hy-tabs) button { min-height:44px; padding:8px 14px; border:0; border-radius:0; background:transparent; color:var(--hy-secondary-text-color); border-bottom:2px solid transparent; }
|
|
76
|
+
:host(hy-tabs) button[aria-selected=true] { color:var(--hy-text-color); border-bottom-color:var(--hy-accent-color); }
|
|
77
|
+
:host(hy-tabs[data-hy-variant=segmented]) .tabs { padding:4px; gap:4px; border-radius:var(--hy-radius,8px); background:var(--hy-input-bg-color); }
|
|
78
|
+
:host(hy-tabs[data-hy-variant=segmented]) button { border:0; border-radius:var(--hy-radius,8px); }
|
|
79
|
+
:host(hy-tabs[data-hy-variant=segmented]) button[aria-selected=true] { background:var(--hy-selected-bg-color,var(--hy-accent-color)); }
|
|
80
|
+
button:focus-visible, select:focus-visible { outline:2px solid var(--hy-focus-ring-color,#8177f2); outline-offset:1px; }
|
|
60
81
|
[aria-invalid=true] { border-color:var(--hy-invalid-border-color,#d65c7c); }
|
|
61
82
|
`;
|
|
62
83
|
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export type HYCarouselDirection = 'horizontal' | 'vertical';
|
|
2
|
+
export type HYCarouselIndicatorPosition = 'top' | 'right' | 'bottom' | 'left';
|
|
3
|
+
export type HYCarouselIndicatorStyle = 'dot' | 'line' | 'number';
|
|
4
|
+
export type HYCarouselChangeSource = 'api' | 'indicator' | 'arrow' | 'keyboard' | 'drag' | 'autoplay' | 'content';
|
|
5
|
+
export interface HYCarouselChangeDetail {
|
|
6
|
+
index: number;
|
|
7
|
+
previousIndex: number;
|
|
8
|
+
source: HYCarouselChangeSource;
|
|
9
|
+
}
|
|
10
|
+
export type HYCarouselIndicatorRenderer = (index: number, active: boolean) => Node | string | null;
|
|
11
|
+
/** A slotted, accessible carousel. Panels stay mounted and inactive panels are inert. */
|
|
12
|
+
export declare class HYCarousel extends HTMLElement {
|
|
13
|
+
private readonly _viewport;
|
|
14
|
+
private readonly _slot;
|
|
15
|
+
private readonly _indicators;
|
|
16
|
+
private readonly _previous;
|
|
17
|
+
private readonly _next;
|
|
18
|
+
private readonly _playback;
|
|
19
|
+
private readonly _motion;
|
|
20
|
+
private readonly _saved;
|
|
21
|
+
private readonly _animations;
|
|
22
|
+
private readonly _hostAttributes;
|
|
23
|
+
private _slides;
|
|
24
|
+
private _index;
|
|
25
|
+
private _reflecting;
|
|
26
|
+
private _connected;
|
|
27
|
+
private _timer;
|
|
28
|
+
private _paused;
|
|
29
|
+
private _hovered;
|
|
30
|
+
private _motionOverride;
|
|
31
|
+
private _drag;
|
|
32
|
+
private _blockClickUntil;
|
|
33
|
+
private _playbackPointerAction;
|
|
34
|
+
private _indicatorRenderer;
|
|
35
|
+
static get observedAttributes(): string[];
|
|
36
|
+
constructor();
|
|
37
|
+
connectedCallback(): void;
|
|
38
|
+
disconnectedCallback(): void;
|
|
39
|
+
attributeChangedCallback(name: string, oldValue: string | null, value: string | null): void;
|
|
40
|
+
get index(): number;
|
|
41
|
+
set index(value: number);
|
|
42
|
+
get direction(): HYCarouselDirection;
|
|
43
|
+
set direction(value: HYCarouselDirection);
|
|
44
|
+
get indicatorPosition(): HYCarouselIndicatorPosition;
|
|
45
|
+
set indicatorPosition(value: HYCarouselIndicatorPosition);
|
|
46
|
+
get indicatorStyle(): HYCarouselIndicatorStyle;
|
|
47
|
+
set indicatorStyle(value: HYCarouselIndicatorStyle);
|
|
48
|
+
get indicatorRenderer(): HYCarouselIndicatorRenderer | null;
|
|
49
|
+
set indicatorRenderer(value: HYCarouselIndicatorRenderer | null);
|
|
50
|
+
get showIndicators(): boolean;
|
|
51
|
+
set showIndicators(value: boolean);
|
|
52
|
+
get showArrows(): boolean;
|
|
53
|
+
set showArrows(value: boolean);
|
|
54
|
+
get autoplay(): boolean;
|
|
55
|
+
set autoplay(value: boolean);
|
|
56
|
+
get interval(): number;
|
|
57
|
+
set interval(value: number);
|
|
58
|
+
get draggable(): boolean;
|
|
59
|
+
set draggable(value: boolean);
|
|
60
|
+
get loop(): boolean;
|
|
61
|
+
set loop(value: boolean);
|
|
62
|
+
next(): void;
|
|
63
|
+
previous(): void;
|
|
64
|
+
goTo(index: number): void;
|
|
65
|
+
play(): void;
|
|
66
|
+
pause(): void;
|
|
67
|
+
private _syncSlides;
|
|
68
|
+
private _releaseSlide;
|
|
69
|
+
private _resolve;
|
|
70
|
+
private _select;
|
|
71
|
+
private _transform;
|
|
72
|
+
private _position;
|
|
73
|
+
private _paint;
|
|
74
|
+
private _render;
|
|
75
|
+
private _animate;
|
|
76
|
+
private _cancelAnimations;
|
|
77
|
+
private _clearTimer;
|
|
78
|
+
private _schedule;
|
|
79
|
+
private _onEnter;
|
|
80
|
+
private _onLeave;
|
|
81
|
+
private _onMotionChange;
|
|
82
|
+
private _onFocus;
|
|
83
|
+
private _onKeyDown;
|
|
84
|
+
private _onNativeDrag;
|
|
85
|
+
private _onClick;
|
|
86
|
+
private _onPointerDown;
|
|
87
|
+
private _onPointerMove;
|
|
88
|
+
private _onPointerUp;
|
|
89
|
+
private _onPointerCancel;
|
|
90
|
+
private _cancelDrag;
|
|
91
|
+
}
|
|
92
|
+
export declare function defineCarouselComponents(): void;
|