ice-web-components 1.0.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/LICENSE +21 -0
- package/README.md +487 -0
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +1 -0
- package/dist/index.umd.js +1 -0
- package/dist/types/components/ICEAlert.d.ts +28 -0
- package/dist/types/components/ICEAnchor.d.ts +69 -0
- package/dist/types/components/ICEAutoComplete.d.ts +71 -0
- package/dist/types/components/ICEAvatar.d.ts +11 -0
- package/dist/types/components/ICEAvatarGroup.d.ts +40 -0
- package/dist/types/components/ICEBackTop.d.ts +39 -0
- package/dist/types/components/ICEBadge.d.ts +17 -0
- package/dist/types/components/ICEBreadcrumb.d.ts +82 -0
- package/dist/types/components/ICEButton.d.ts +25 -0
- package/dist/types/components/ICECalendar.d.ts +84 -0
- package/dist/types/components/ICECard.d.ts +22 -0
- package/dist/types/components/ICECarousel.d.ts +86 -0
- package/dist/types/components/ICECascader.d.ts +84 -0
- package/dist/types/components/ICECheckBox.d.ts +21 -0
- package/dist/types/components/ICECheckboxGroup.d.ts +75 -0
- package/dist/types/components/ICECollapse.d.ts +49 -0
- package/dist/types/components/ICEColorPicker.d.ts +52 -0
- package/dist/types/components/ICEComment.d.ts +36 -0
- package/dist/types/components/ICEDatePicker.d.ts +90 -0
- package/dist/types/components/ICEDescriptions.d.ts +40 -0
- package/dist/types/components/ICEDrawer.d.ts +59 -0
- package/dist/types/components/ICEDropdown.d.ts +63 -0
- package/dist/types/components/ICEEmpty.d.ts +28 -0
- package/dist/types/components/ICEFloatButton.d.ts +63 -0
- package/dist/types/components/ICEForm.d.ts +48 -0
- package/dist/types/components/ICEFormItem.d.ts +65 -0
- package/dist/types/components/ICEGrid.d.ts +59 -0
- package/dist/types/components/ICEIcon.d.ts +9 -0
- package/dist/types/components/ICEIconTile.d.ts +53 -0
- package/dist/types/components/ICEImagePreview.d.ts +91 -0
- package/dist/types/components/ICEImageView.d.ts +62 -0
- package/dist/types/components/ICEInputNumber.d.ts +54 -0
- package/dist/types/components/ICEKanban.d.ts +85 -0
- package/dist/types/components/ICELabel.d.ts +28 -0
- package/dist/types/components/ICEList.d.ts +59 -0
- package/dist/types/components/ICEMenu.d.ts +48 -0
- package/dist/types/components/ICEModal.d.ts +73 -0
- package/dist/types/components/ICEPagination.d.ts +62 -0
- package/dist/types/components/ICEPanel.d.ts +7 -0
- package/dist/types/components/ICEPasswordField.d.ts +18 -0
- package/dist/types/components/ICEPopconfirm.d.ts +21 -0
- package/dist/types/components/ICEPopover.d.ts +48 -0
- package/dist/types/components/ICEProgressBar.d.ts +37 -0
- package/dist/types/components/ICERadioButton.d.ts +21 -0
- package/dist/types/components/ICERadioGroup.d.ts +71 -0
- package/dist/types/components/ICERate.d.ts +43 -0
- package/dist/types/components/ICEResult.d.ts +30 -0
- package/dist/types/components/ICEScrollPane.d.ts +72 -0
- package/dist/types/components/ICESegmented.d.ts +34 -0
- package/dist/types/components/ICESelect.d.ts +85 -0
- package/dist/types/components/ICESeparator.d.ts +7 -0
- package/dist/types/components/ICESkeleton.d.ts +29 -0
- package/dist/types/components/ICESlider.d.ts +63 -0
- package/dist/types/components/ICESpace.d.ts +51 -0
- package/dist/types/components/ICESpin.d.ts +33 -0
- package/dist/types/components/ICESplitter.d.ts +85 -0
- package/dist/types/components/ICEStatCard.d.ts +14 -0
- package/dist/types/components/ICEStatistic.d.ts +78 -0
- package/dist/types/components/ICESteps.d.ts +31 -0
- package/dist/types/components/ICESvgIcon.d.ts +9 -0
- package/dist/types/components/ICESwitch.d.ts +23 -0
- package/dist/types/components/ICETable.d.ts +250 -0
- package/dist/types/components/ICETabs.d.ts +17 -0
- package/dist/types/components/ICETag.d.ts +15 -0
- package/dist/types/components/ICETextArea.d.ts +8 -0
- package/dist/types/components/ICETextField.d.ts +80 -0
- package/dist/types/components/ICETileMap.d.ts +152 -0
- package/dist/types/components/ICETimePicker.d.ts +81 -0
- package/dist/types/components/ICETimeline.d.ts +33 -0
- package/dist/types/components/ICETooltip.d.ts +55 -0
- package/dist/types/components/ICETour.d.ts +97 -0
- package/dist/types/components/ICETransfer.d.ts +68 -0
- package/dist/types/components/ICETree.d.ts +107 -0
- package/dist/types/components/ICETreeSelect.d.ts +64 -0
- package/dist/types/components/ICETypography.d.ts +67 -0
- package/dist/types/components/ICEUpload.d.ts +75 -0
- package/dist/types/components/ICEVirtualList.d.ts +100 -0
- package/dist/types/components/ICEWatermark.d.ts +49 -0
- package/dist/types/components/ICEWindow.d.ts +159 -0
- package/dist/types/core/ICEContainer.d.ts +10 -0
- package/dist/types/core/ICEFocusManager.d.ts +70 -0
- package/dist/types/core/ICEHoverManager.d.ts +26 -0
- package/dist/types/core/ICEManager.d.ts +24 -0
- package/dist/types/core/ICEMessageManager.d.ts +80 -0
- package/dist/types/core/ICEOverlayManager.d.ts +92 -0
- package/dist/types/core/ICEPainter.d.ts +12 -0
- package/dist/types/core/ICEWidget.d.ts +86 -0
- package/dist/types/i18n/ICEI18n.d.ts +35 -0
- package/dist/types/index.d.ts +111 -0
- package/dist/types/model/ICE2048Model.d.ts +90 -0
- package/dist/types/model/ICEBoundedRangeModel.d.ts +20 -0
- package/dist/types/model/ICEButtonModel.d.ts +16 -0
- package/dist/types/model/ICEFormModel.d.ts +105 -0
- package/dist/types/model/ICEHighScoreModel.d.ts +60 -0
- package/dist/types/model/ICEMinesweeperModel.d.ts +102 -0
- package/dist/types/model/ICESelectionModel.d.ts +31 -0
- package/dist/types/model/ICESnakeModel.d.ts +90 -0
- package/dist/types/model/ICETetrisModel.d.ts +118 -0
- package/dist/types/model/ICEToggleModel.d.ts +14 -0
- package/dist/types/theme/ICEArcadeTheme.d.ts +22 -0
- package/dist/types/theme/ICETheme.d.ts +120 -0
- package/dist/types/util/ICEAccessibilityMirror.d.ts +55 -0
- package/dist/types/util/ICEAnimation.d.ts +71 -0
- package/dist/types/util/ICEDragReorder.d.ts +102 -0
- package/dist/types/util/ICENativeInput.d.ts +65 -0
- package/dist/types/util/ICEOverlayPosition.d.ts +52 -0
- package/dist/types/util/ICEStyle.d.ts +60 -0
- package/dist/types/util/ICEWorldBox.d.ts +14 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 大漠穷秋
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
# ice-web-components
|
|
2
|
+
|
|
3
|
+
Canvas-native UI components for [`ice-render`](https://github.com/ice-render/ice-render) —
|
|
4
|
+
Swing-style widgets with a Bootstrap-flavoured look, rendered entirely on a single
|
|
5
|
+
`<canvas>`. No DOM widgets, no CSS framework: every pixel (including popups, focus
|
|
6
|
+
rings and shadows) is drawn by the engine.
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
> ⚠️ **Just for fun.** This project is created purely for fun and exploration. It is
|
|
11
|
+
> not intended as a production-ready or battle-tested UI library.
|
|
12
|
+
|
|
13
|
+
## Highlights
|
|
14
|
+
|
|
15
|
+
- **86 components** — buttons, inputs, selects, tables, trees, menus, modals,
|
|
16
|
+
drawers, notifications, uploads, date/time pickers, cascader, transfer, carousel,
|
|
17
|
+
colour picker… and the small stuff (tags, badges, avatars, skeletons, spins).
|
|
18
|
+
- **One overlay stack for every popup** — Modal / Drawer / Dropdown / Tooltip /
|
|
19
|
+
Popover / Popconfirm / Select / DatePicker / Cascader all go through
|
|
20
|
+
`ICEOverlayManager`: 12 placements, auto flip + clamp to the visible area,
|
|
21
|
+
Esc / outside-click closing, focus trap, enter/exit animation.
|
|
22
|
+
- **Forms with sync + async validation** — `ICEFormModel` (required / min / max /
|
|
23
|
+
length / pattern / custom / **asyncValidator**), `ICEFormItem` shows errors and a
|
|
24
|
+
“validating…” state, `submitAsync()` waits for the async rules.
|
|
25
|
+
- **Keyboard & focus** — Tab / Shift+Tab rotation, Enter/Space activation,
|
|
26
|
+
arrow keys for sliders, menus, tabs and rate; ring drawn above everything.
|
|
27
|
+
- **Bootstrap 5 token theme** (plus a dark theme) — swap with one call.
|
|
28
|
+
- **No name collisions with the engine** — the package’s runtime exports are
|
|
29
|
+
disjoint from `ice-render`’s (there is a regression test for it).
|
|
30
|
+
- **Actually tested** — 705 unit tests (98 suites: form validation, overlay
|
|
31
|
+
positioning, keyboard navigation, sort/hover/focus edge cases, the Minesweeper,
|
|
32
|
+
Tetris and Snake rule models) plus five browser QA suites (`qa:admin`, `qa:gallery`,
|
|
33
|
+
`qa:workbench`, `qa:xp`, `qa:arcade` — 198 assertions) that drive the demo pages with
|
|
34
|
+
real mouse and keyboard events and fail on any console error.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
> **Install**: `1.0.0` is the first tagged release. The engine (`ice-render`) is on npm;
|
|
39
|
+
> the component library ships as a tarball / git dependency for now — use one of the following:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# ① a local path (the usual thing inside a monorepo / workspace)
|
|
43
|
+
npm install /path/to/ice-web-components
|
|
44
|
+
|
|
45
|
+
# ② straight from git
|
|
46
|
+
npm install git+https://github.com/ice-render/ice-web-components.git
|
|
47
|
+
|
|
48
|
+
# ③ pack it, then install the tarball
|
|
49
|
+
(cd /path/to/ice-web-components && npm pack) # produces ice-web-components-0.0.1.tgz
|
|
50
|
+
npm install /path/to/ice-web-components-0.0.1.tgz
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
All three pull the `ice-render@^1.3.0` dependency from npm. The published tarball
|
|
54
|
+
contains `dist/` only (cjs + esm + umd + type declarations).
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { ICE } from 'ice-render';
|
|
58
|
+
import {
|
|
59
|
+
ICEButton,
|
|
60
|
+
ICEHoverManager,
|
|
61
|
+
ICELabel,
|
|
62
|
+
ICEMessage,
|
|
63
|
+
ICEPanel,
|
|
64
|
+
getICEFocusManager,
|
|
65
|
+
} from 'ice-web-components';
|
|
66
|
+
|
|
67
|
+
const ice = new ICE().init('canvas');
|
|
68
|
+
new ICEHoverManager(ice).start(); // canvas has no native hover: opt in
|
|
69
|
+
getICEFocusManager(ice).start(); // Tab / Enter / Esc handling
|
|
70
|
+
|
|
71
|
+
const panel = new ICEPanel({ left: 24, top: 24, width: 372, height: 192 });
|
|
72
|
+
panel.addChild(new ICELabel({ left: 24, top: 20, text: 'Quick start' }));
|
|
73
|
+
|
|
74
|
+
const button = new ICEButton({ left: 24, top: 64, width: 140, text: 'Click me' });
|
|
75
|
+
const hint = new ICELabel({ left: 24, top: 112, text: 'clicked 0 times' });
|
|
76
|
+
let count = 0;
|
|
77
|
+
button.on('click', () => {
|
|
78
|
+
count += 1;
|
|
79
|
+
hint.setText(`clicked ${count} times`);
|
|
80
|
+
ICEMessage.success(ice, `clicked ${count} times`);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
panel.addChildren([button, hint]);
|
|
84
|
+
ice.addChild(panel);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+

|
|
88
|
+
|
|
89
|
+
## Documentation
|
|
90
|
+
|
|
91
|
+
Full docs live in [`docs/`](./docs/README.md):
|
|
92
|
+
|
|
93
|
+
| | |
|
|
94
|
+
|---|---|
|
|
95
|
+
| [Architecture](./docs/architecture.md) | Layers, component model, rendering & repaint, events & hover, overlays / focus / forms / theming, plus a “pitfalls” table |
|
|
96
|
+
| [Component cheat sheet](./docs/components.md) | 85 component classes, one line each, grouped, with links into the API |
|
|
97
|
+
| [API reference](./docs/api/README.md) | Constructor props and public methods for every component (**generated from source**, so it cannot drift) |
|
|
98
|
+
| [Examples & scenarios](./docs/guides/examples.md) | What each of the six demo pages shows, which components it uses, and a checklist for building your own |
|
|
99
|
+
| [Theming & colour](./docs/guides/theming.md) | Token groups, status colours, `*TextEmphasis`, custom themes |
|
|
100
|
+
| [Forms & validation](./docs/guides/forms.md) | The three layers, the rule list, async validation, wiring a custom control |
|
|
101
|
+
| [Overlay guide](./docs/guides/overlays.md) | The three ways to use popups, positioning, close policies, content factories |
|
|
102
|
+
| [Canvas layout](./docs/guides/layout.md) | Coordinates & zIndex, cluster + shelf layout, clipping & scrolling, when sizes are ready |
|
|
103
|
+
| [Writing your own component](./docs/guides/custom-components.md) | Three levels of effort, constructor conventions, interaction / form / overlay / theme hooks, type registration and pitfalls |
|
|
104
|
+
| [Testing](./docs/guides/testing.md) | Unit-test recipes (fake ICE + real components) and the browser QA scripts |
|
|
105
|
+
| [Migration](./docs/guides/migration.md) | `UI*` → `ICE*`, Ant → Bootstrap theming, other breaking changes |
|
|
106
|
+
|
|
107
|
+
> The guide pages themselves are written in Chinese for now; this README is English-only.
|
|
108
|
+
|
|
109
|
+
## Demos
|
|
110
|
+
|
|
111
|
+
All pages under `examples/` are plain HTML — build the package, then open them
|
|
112
|
+
(or serve the folder with any static server).
|
|
113
|
+
|
|
114
|
+
### `gallery.html` — every component in one page
|
|
115
|
+
|
|
116
|
+
Rendered with a small hand-rolled flow layout (clusters keep their internal
|
|
117
|
+
geometry, clusters wrap like shelves), so adding a demo never requires hunting for
|
|
118
|
+
free coordinates.
|
|
119
|
+
|
|
120
|
+

|
|
121
|
+
|
|
122
|
+
### `admin.html` — a six-page back-office
|
|
123
|
+
|
|
124
|
+
A small “ICE Shop” admin: sidebar with submenus, breadcrumb + page search +
|
|
125
|
+
notifications/user menu in the header, a floating action button, a first-run tour,
|
|
126
|
+
and six pages that switch inside a scroll pane.
|
|
127
|
+
|
|
128
|
+
The business flow is deliberately complete: order filtering (keyword / region /
|
|
129
|
+
amount range / abnormal-only) with a batch toolbar, an order drawer with
|
|
130
|
+
fulfilment steps and a service timeline, inventory warnings with pagination,
|
|
131
|
+
product gallery preview, customer insights with satisfaction scoring, a
|
|
132
|
+
splitter-based fulfilment workbench with anchors, and a settings pane whose
|
|
133
|
+
password form validates across fields.
|
|
134
|
+
|
|
135
|
+
| Dashboard | Orders |
|
|
136
|
+
|---|---|
|
|
137
|
+
|  |  |
|
|
138
|
+
|  |  |
|
|
139
|
+
|  |  |
|
|
140
|
+
|
|
141
|
+
Popup layers used by that demo:
|
|
142
|
+
|
|
143
|
+
| Order detail drawer | New-order dialog | Notification dropdown |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
|  |  |  |
|
|
146
|
+
|
|
147
|
+
### `custom-component.html` — write your own component
|
|
148
|
+
|
|
149
|
+
The same “write a component and plug it into ICE” story as
|
|
150
|
+
[`docs/guides/custom-components.md`](./docs/guides/custom-components.md), but
|
|
151
|
+
runnable: a hand-written `ICEMetric` card that reacts to clicks, hover and
|
|
152
|
+
keyboard, and participates in `ICEForm` validation.
|
|
153
|
+
|
|
154
|
+

|
|
155
|
+
|
|
156
|
+
### `workbench.html` — customer-support workbench
|
|
157
|
+
|
|
158
|
+
A second end-to-end scenario (deliberately *not* a dashboard): a three-pane support
|
|
159
|
+
workbench built with `ICESplitter` — ticket queue with filters and skeleton loading,
|
|
160
|
+
conversation pane with reply composer / quick-reply dropdown / attachment upload /
|
|
161
|
+
ticket tags, and a customer profile pane with satisfaction rating, history timeline
|
|
162
|
+
and knowledge base. Session log: ticket selection drives the profile, sending a
|
|
163
|
+
reply appends a message, the floating button opens a 3-step tour, and the
|
|
164
|
+
back-to-top button appears once the conversation scrolls.
|
|
165
|
+
|
|
166
|
+

|
|
167
|
+
|
|
168
|
+
### `windows-xp.html` — a full-screen Windows XP desktop
|
|
169
|
+
|
|
170
|
+
The fun one: a canvas-only XP desktop that **boots**. Turn it on and you get the black
|
|
171
|
+
boot splash (self-drawn four-colour flag + the running progress blocks), then the blue
|
|
172
|
+
welcome screen: pick a user tile, type anything (or nothing) into the password box and
|
|
173
|
+
press Enter — *any* credentials are accepted, this is a toy. Then the desktop fades in
|
|
174
|
+
with a synthesized startup chime.
|
|
175
|
+
|
|
176
|
+
The sound is generated live with WebAudio (startup / logoff / shutdown / click cues) —
|
|
177
|
+
original tones, no audio files, no Microsoft assets. Hover the tray speaker in the
|
|
178
|
+
taskbar to mute it. Log off from the Start menu and you drop back to the welcome
|
|
179
|
+
screen; shut down and you get the black "it is now safe to turn off your computer"
|
|
180
|
+
screen with a power button that boots the machine all over again.
|
|
181
|
+
|
|
182
|
+
The desktop itself: wallpaper, desktop icons, taskbar with a
|
|
183
|
+
working clock, a Start menu, and draggable windows with minimise / maximise / close.
|
|
184
|
+
Seven tiny apps are wired up (My Computer, My Documents, Notepad, Paint, Minesweeper,
|
|
185
|
+
Internet Explorer, Display Properties), and switching the wallpaper in Display
|
|
186
|
+
Properties repaints the desktop immediately.
|
|
187
|
+
|
|
188
|
+
Looks the part too: it switches to the library's built-in `ICE_XP_THEME` (Luna blue +
|
|
189
|
+
classic grey controls), draws every icon with engine primitives (no bitmap assets, no
|
|
190
|
+
Microsoft artwork), and initialises with `dpr` so text stays crisp on Retina screens.
|
|
191
|
+
|
|
192
|
+
Minesweeper is the full game: beginner / intermediate / expert, first-click-safe mine
|
|
193
|
+
placement, flood fill, right-click flag cycle (🚩 / ❓), chord on double click, LED
|
|
194
|
+
counters, a timer that starts on the first click, and per-difficulty best times. Its
|
|
195
|
+
rules live in a tested pure model (`ICEMinesweeperModel`) — the UI only draws it.
|
|
196
|
+
|
|
197
|
+
Internet Explorer is a **real** browser too: the address bar `fetch()`es the URL,
|
|
198
|
+
`DOMParser` parses the HTML, and the title / headings / paragraphs / links / images are
|
|
199
|
+
drawn with canvas components inside a scroll pane (with back / forward / refresh).
|
|
200
|
+
Same-origin pages always work; other sites obey CORS like any browser, and failures
|
|
201
|
+
land on an XP-style error page. Serve the folder over http (`npx serve .`) — `fetch`
|
|
202
|
+
does not work from `file://`.
|
|
203
|
+
Right-click works because `ICE.init()` no longer stops the `contextmenu` event on its
|
|
204
|
+
way to the dispatcher (ice-render 1.4.1).
|
|
205
|
+
|
|
206
|
+
Two new generic components came out of it: `ICEWindow` (window chrome with an XP Luna
|
|
207
|
+
title bar, drag, resize, maximise/restore, activate event) and `ICEIconTile`
|
|
208
|
+
(selectable icon tile that opens on double click).
|
|
209
|
+
|
|
210
|
+
The eighth app is **ICE Arcade** — the handheld console from `arcade.html`, running
|
|
211
|
+
inside an XP window. It reuses the same two pure models and the same `ICETileMap`
|
|
212
|
+
(so the board is still one node), and the desktop routes the keyboard to it only while
|
|
213
|
+
that window is active; closing the window stops its step timer.
|
|
214
|
+
|
|
215
|
+

|
|
216
|
+
|
|
217
|
+
| Boot splash | Welcome screen | Password page |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
|  |  |  |
|
|
220
|
+
|
|
221
|
+

|
|
222
|
+
|
|
223
|
+
### `arcade.html` — ICE Arcade (a handheld console)
|
|
224
|
+
|
|
225
|
+
Not a web page but a **handheld console**: the shell, the screen bezel, the HUD cards,
|
|
226
|
+
the buttons and the sound switch are all ICE components, and there is not a single
|
|
227
|
+
bitmap asset in the picture. Three cartridges are plugged in, and the cartridge row at
|
|
228
|
+
the top switches between them (a fourth slot, Chinese chess, is disabled for now).
|
|
229
|
+
|
|
230
|
+
| | |
|
|
231
|
+
|---|---|
|
|
232
|
+
|  |  |
|
|
233
|
+
|
|
234
|
+
**Cartridge 1 — Tetris** (`ICETetrisModel`, 16 unit tests). Modern-standard rules:
|
|
235
|
+
7-bag fairness, simple wall kicks (0 / ±1 / ±2), a ghost landing preview, soft drop
|
|
236
|
+
+1/cell, hard drop +2/cell, line scores of 100/300/500/800 × level, a level-up every 10
|
|
237
|
+
lines, and a gravity interval that starts at 800 ms and shrinks with the level.
|
|
238
|
+
Keyboard: `←` / `→` move, `↓` soft drop, `Space` hard drop, `↑` / `X` rotate clockwise,
|
|
239
|
+
`Z` rotate counter-clockwise, `P` pause, `R` restart.
|
|
240
|
+
|
|
241
|
+
**Cartridge 2 — Snake** (`ICESnakeModel`, 18 unit tests). Classic rules: the snake grows
|
|
242
|
+
on every meal (+10 points × level), 5 meals per level, an interval that drops from
|
|
243
|
+
170 ms per cell towards 70 ms, a two-deep turn queue that refuses 180° reversals (and
|
|
244
|
+
lets you survive moving into the tail cell that is about to vacate), and walls that
|
|
245
|
+
kill. Keyboard: arrows or `W` / `A` / `S` / `D` to steer, `P` pause, `R` restart.
|
|
246
|
+
Clicking a cell on the board steers towards it — that is the tile map’s `cellclick`,
|
|
247
|
+
i.e. a real hit test inside a single component.
|
|
248
|
+
|
|
249
|
+
**Cartridge 3 — 2048** (`ICE2048Model`, 19 unit tests). The classic rules: two starting
|
|
250
|
+
tiles, merges score their own value, each tile merges at most once per move (so `2 2 2 2`
|
|
251
|
+
becomes `4 4`, not `8`), a move that changes nothing spawns nothing, and filling the board
|
|
252
|
+
without any merge left is game over. Reaching 2048 wins but lets you keep playing.
|
|
253
|
+
Arrows or `W` / `A` / `S` / `D` slide, `P` pauses, `R` restarts. The numbers are drawn by
|
|
254
|
+
the tile map’s **label layer** — the palette entry for each value carries its font size,
|
|
255
|
+
weight and text colour, so a 4×4 board with 16 numbers is still one node.
|
|
256
|
+
|
|
257
|
+

|
|
258
|
+
|
|
259
|
+
Both games are pure models that never touch the canvas; the page only reads the model
|
|
260
|
+
and paints cells. Switching a cartridge tears the old board down, builds the new one
|
|
261
|
+
and re-captions the HUD, so a third game is a registry entry plus a `mount()`.
|
|
262
|
+
Switching away from the tab pauses whatever is running.
|
|
263
|
+
|
|
264
|
+
Under the hood this page is where the engine work happens:
|
|
265
|
+
|
|
266
|
+
- **`ICETileMap`** paints a whole board (10×20 or 20×20 cells) inside **one** node —
|
|
267
|
+
it extends the widget base, draws the grid in `doRender()` with the engine context and
|
|
268
|
+
keeps its own dirty flag, so a 400-cell snake board is 1 node instead of 400 (the QA
|
|
269
|
+
asserts `childNodes.length === 0`). Ghost landing spots go through its highlight layer,
|
|
270
|
+
line clears and meals go through `pulse()`, which fades the overlay with a `tween`.
|
|
271
|
+
- **`registerTheme('arcade', ICE_ARCADE_THEME)`** moves the game palette into tokens:
|
|
272
|
+
pieces and snake colours ship as `ICE_ARCADE_PALETTE`, so the board re-skins with the
|
|
273
|
+
rest of the UI instead of hard-coded hex values in the page.
|
|
274
|
+
- **`ICEHighScoreModel`** keeps a per-cartridge top-5 (sorting, capping, corrupt-storage
|
|
275
|
+
tolerance, injected storage) and the **Leaderboard (L)** button opens an `ICEModal`
|
|
276
|
+
containing an `ICETable` inside an `ICEScrollPane`.
|
|
277
|
+
- `fadeIn` on cartridge switch, `scaleIn` on game over, `pulse` on line clears — all
|
|
278
|
+
from `ICEAnimation`, so the “juice” is library code rather than hand-rolled decay.
|
|
279
|
+
- Toasts are **replaced, not stacked**: a console only needs one status line, and the QA
|
|
280
|
+
caught a stack of three toasts covering the cartridge row (the click never reached the
|
|
281
|
+
button). `ICEMessage` still supports stacking for pages that want it.
|
|
282
|
+
|
|
283
|
+
| Leaderboard (`ICEModal` + `ICETable` + `ICEScrollPane`) |
|
|
284
|
+
|---|
|
|
285
|
+
|  |
|
|
286
|
+
|
|
287
|
+
> This page deliberately does **not** start `ICEFocusManager` — it activates the
|
|
288
|
+
> focused button with Enter/Space, which collides head-on with “Space = hard drop”.
|
|
289
|
+
> A game page keeps the keyboard for itself; mouse hover still goes through
|
|
290
|
+
> `ICEHoverManager`.
|
|
291
|
+
|
|
292
|
+
## Components
|
|
293
|
+
|
|
294
|
+
| Group | Components |
|
|
295
|
+
|---|---|
|
|
296
|
+
| Basic | `ICEPanel` `ICEButton` `ICELabel` `ICETypography` `ICEIcon` `ICESvgIcon` `ICESeparator` |
|
|
297
|
+
| Layout | `ICESpace` `ICEGrid` `ICEGridCol` `ICESplitter` `ICEScrollPane` |
|
|
298
|
+
| Data entry | `ICETextField` `ICETextArea` `ICEPasswordField` `ICEInputNumber` `ICESelect` `ICEAutoComplete` `ICECascader` `ICETreeSelect` `ICEDatePicker` `ICETimePicker` `ICECheckBox` `ICECheckboxGroup` `ICERadioButton` `ICERadioGroup` `ICESwitch` `ICESlider` `ICESegmented` `ICERate` `ICEColorPicker` `ICETransfer` `ICEUpload` `ICEForm` `ICEFormItem` |
|
|
299
|
+
| Data display | `ICEVirtualList` `ICEKanban` `ICETable` `ICEList` `ICETree` `ICEStatCard` `ICEStatistic` `ICECard` `ICEComment` `ICEDescriptions` `ICETimeline` `ICEProgressBar` `ICEAvatar` `ICEAvatarGroup` `ICETag` `ICEBadge` `ICEImageView` `ICEImagePreview` `ICECalendar` `ICECarousel` `ICECollapse` `ICEWatermark` |
|
|
300
|
+
| Feedback & status | `ICEAlert` `ICEModal` `ICEDrawer` `ICEMessage` `ICENotification` `ICETooltip` `ICEPopover` `ICEPopconfirm` `ICETour` `ICEFloatButton` `ICEEmpty` `ICESkeleton` `ICESpin` `ICEResult` `ICESteps` `ICEOverlayManager` |
|
|
301
|
+
| Navigation | `ICEMenu` `ICEBreadcrumb` `ICEAnchor` `ICEBackTop` `ICEDropdown` `ICEPagination` `ICETabs` |
|
|
302
|
+
| Layout & core | `ICEWidget` `ICEContainer` `ICEHoverManager` `ICEFocusManager` `ICEMessageManager` `ICEManager` (`ICEPainter` / `ICELayoutManager` are types) |
|
|
303
|
+
| Models | `ICEButtonModel` `ICEToggleModel` `ICEBoundedRangeModel` `ICESelectionModel` `ICEFormModel` |
|
|
304
|
+
|
|
305
|
+
Helper functions: `attachTooltip` `attachPopover` `attachPopconfirm` `attachDropdown`
|
|
306
|
+
`openModal` `openDrawer` `getICEOverlayManager` `getICEFocusManager` `getICEMessageManager`
|
|
307
|
+
`formatStatisticValue` `formatCountdown` `truncateTextLines` `buildMonthGrid` `formatCalendarDate`
|
|
308
|
+
`openImagePreview` `tween` `fadeIn` `fadeOut` `slideIn` `scaleIn` and friends.
|
|
309
|
+
|
|
310
|
+
## Theme
|
|
311
|
+
|
|
312
|
+
A compact **Bootstrap 5-style** token set (see `ICE_LIGHT_THEME` / `ICE_DARK_THEME`):
|
|
313
|
+
|
|
314
|
+
- semantic colours — `primary` `#0d6efd`, `success` `#198754`, `warning` `#ffc107`,
|
|
315
|
+
`error` `#dc3545`, `info` `#0dcaf0`;
|
|
316
|
+
- subtle pairs for soft surfaces — `primaryBg` / `primaryBorder`, `successBg` /
|
|
317
|
+
`successBorder`, … plus `*TextEmphasis` (Bootstrap’s `*-text-emphasis`) for text
|
|
318
|
+
sitting on those subtle backgrounds;
|
|
319
|
+
- neutrals — `surface`, `elevated`, `background`, `border`, `borderSecondary`;
|
|
320
|
+
- text hierarchy — `text`, `textSecondary`, `textTertiary`, `textDisabled`;
|
|
321
|
+
- spacing / radius / control sizes, Bootstrap’s three shadows (`sm` / `md` / `lg`,
|
|
322
|
+
expressed as explicit `shadowColor` `shadowBlur` `shadowOffset*` numbers), and a
|
|
323
|
+
`focusRing` colour for focused controls.
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
import { iceUIManager } from 'ice-web-components';
|
|
327
|
+
|
|
328
|
+
iceUIManager.setTheme('dark'); // components read tokens when they are created
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Status chips default to Bootstrap’s solid `.text-bg-*` look (white text, black text
|
|
332
|
+
on the light `warning` / `info` colours). Pass `variant: 'soft'` for the subtle
|
|
333
|
+
background + emphasis text variant:
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
new ICETag({ text: 'Paid', status: 'success' }); // solid green
|
|
337
|
+
new ICETag({ text: 'Paid', status: 'success', variant: 'soft' }); // #d1e7dd / #0a3622
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Naming & exports
|
|
341
|
+
|
|
342
|
+
Everything exported by this package uses the **`ICE`** prefix (same convention as
|
|
343
|
+
`ice-render`), and the package’s runtime exports do **not overlap** with the
|
|
344
|
+
engine’s — you can import both namespaces, or name-import from both, without
|
|
345
|
+
ambiguity:
|
|
346
|
+
|
|
347
|
+
| concept | this package | `ice-render` |
|
|
348
|
+
|---|---|---|
|
|
349
|
+
| base class | `ICEWidget` (UI widget base, extends `ICEGroup`) | `ICEComponent` (graphic component base) |
|
|
350
|
+
| image | `ICEImageView` (widget, wraps the primitive) | `ICEImage` (image primitive) |
|
|
351
|
+
|
|
352
|
+
Layout classes are **not** re-exported — they belong to the engine:
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
import { ICEFlowLayout } from 'ice-render';
|
|
356
|
+
import { ICEPanel } from 'ice-web-components';
|
|
357
|
+
|
|
358
|
+
panel.setLayout(new ICEFlowLayout({ gap: 8 }));
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Interaction notes
|
|
362
|
+
|
|
363
|
+
- **Hover** — ICE deliberately skips full hit-testing on `mousemove`, so canvas
|
|
364
|
+
components have no native `mouseenter` / `mouseleave`. Attach
|
|
365
|
+
`new ICEHoverManager(ice).start()` once and components get lightweight hover
|
|
366
|
+
states (table rows, menu/tree items, buttons, chips…).
|
|
367
|
+
- **Popups** — always open through `getICEOverlayManager(ice)`; overlay content is
|
|
368
|
+
mounted on the engine’s tool layer, so it renders above the scene, follows the
|
|
369
|
+
anchor, and is excluded from scene hit-testing.
|
|
370
|
+
- **Focus** — `getICEFocusManager(ice).start()` gives Tab / Shift+Tab rotation,
|
|
371
|
+
Enter/Space activation, and a ring drawn on the tool layer. The ring follows
|
|
372
|
+
`:focus-visible` semantics: it only appears for **keyboard** focus, so mouse
|
|
373
|
+
clicks and thumb drags stay clean. Text-entry controls (text field, number,
|
|
374
|
+
select, date/time/cascader, colour picker…) opt into `focusRing: 'always'`;
|
|
375
|
+
any component can declare `focusRing: 'keyboard' | 'always' | 'never'` (or
|
|
376
|
+
`setFocusRingMode()`). Overlays that own the keyboard declare `keyboardCaptured`
|
|
377
|
+
so the scene yields Enter/Space.
|
|
378
|
+
- **Rendering pitfalls worth knowing** — the engine sorts by **global zIndex**
|
|
379
|
+
(creation order), so build containers before their children; components that wrap
|
|
380
|
+
caller-provided nodes (carousel slides, card `extra`, modal content) raise those
|
|
381
|
+
subtrees above themselves.
|
|
382
|
+
- **Container hit-testing** — a container that is created *after* its children and
|
|
383
|
+
stays interactive will swallow every click inside it (`ICESplitter` and plain
|
|
384
|
+
layout wrappers therefore ship with `interactive: false`; if you build your own
|
|
385
|
+
wrapper, do the same).
|
|
386
|
+
- **Focus ring vs. hit-testing** — the two rules above bite together: a form item
|
|
387
|
+
wrapper left interactive hides its own input from `ice.hitTest()`, so the focus
|
|
388
|
+
manager can't focus it (no ring, and `getFocused()` returns null while the field
|
|
389
|
+
still accepts typing through its own point-in-box check). `ICEFormItem`,
|
|
390
|
+
`ICEForm`, `ICESpace`, `ICEGrid` and `ICESplitter` are all `interactive: false`.
|
|
391
|
+
- **Accessibility** — the engine hands you an accessibility snapshot
|
|
392
|
+
(`getAccessibilityTree()`: role suggestion, `state.ariaLabel`, screen box, tab order);
|
|
393
|
+
this library wires the other half: controls carry meaningful labels (buttons use their text,
|
|
394
|
+
text fields fall back to the placeholder, checkboxes to their label), and
|
|
395
|
+
`mountICEAccessibilityMirror(ice)` renders that snapshot into invisible-but-real DOM
|
|
396
|
+
(`role` / `aria-label` / `tabindex`, positioned over the canvas). Clicking or focusing a
|
|
397
|
+
mirror element focuses and activates the canvas component, so screen readers and keyboard
|
|
398
|
+
users can drive a canvas UI.
|
|
399
|
+
- **i18n** — built-in strings (table empty state, modal/popconfirm buttons, upload hint,
|
|
400
|
+
transfer panes, tour buttons, form "validating...") come from a locale pack:
|
|
401
|
+
`setICELocale('en-US')` (built-in `zh-CN` + `en-US`), `registerICELocale()` for your own,
|
|
402
|
+
`t('pagination.total', { total: 42 })` for app strings. Components read the text when they
|
|
403
|
+
are built, so switching the locale then rebuilding/relayouting is what shows the change.
|
|
404
|
+
- **Text input & IME** — focusing a text field mounts a **fully transparent native
|
|
405
|
+
`<input>` / `<textarea>`** over it (`ICENativeInput`): the browser and the IME do the
|
|
406
|
+
typing, `input` / `compositionend` write the value back, and `change` / form binding
|
|
407
|
+
keep working. So Chinese / Japanese / Korean input works, paste works, and the caret
|
|
408
|
+
is a real DOM caret — the canvas still draws every pixel (the element is invisible).
|
|
409
|
+
In a runtime without `document` (Node / mini-program) it degrades to the old
|
|
410
|
+
per-key `keydown` path. Pressing Enter emits `submit` on single-line fields.
|
|
411
|
+
|
|
412
|
+
## Development
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
npm install
|
|
416
|
+
npm run types:check # tsc --noEmit
|
|
417
|
+
npm test # jest (unit tests, node env)
|
|
418
|
+
npm run build # cjs + esm + umd + d.ts
|
|
419
|
+
|
|
420
|
+
# browser QA for examples/admin.html: layout consistency + popup open/close
|
|
421
|
+
# (needs playwright; point PLAYWRIGHT_PATH at an existing install if needed)
|
|
422
|
+
npm run qa:admin
|
|
423
|
+
|
|
424
|
+
# browser QA for examples/gallery.html: zero overlap + real interactions of the
|
|
425
|
+
# newest components (breadcrumb collapse, countdown, radio/checkbox groups,
|
|
426
|
+
# splitter drag, watermark tiling)
|
|
427
|
+
npm run qa:gallery
|
|
428
|
+
|
|
429
|
+
# browser QA for examples/workbench.html: queue → profile, reply composer,
|
|
430
|
+
# quick replies, tags/rating, tour, back-to-top, splitter drag
|
|
431
|
+
npm run qa:workbench
|
|
432
|
+
|
|
433
|
+
# browser QA for examples/windows-xp.html: icons, windows (drag/minimise/restore),
|
|
434
|
+
# boot → login (any password) → desktop, logoff/shutdown/power-on, tray mute,
|
|
435
|
+
# start menu, minesweeper, paint strokes, wallpaper switch, clock
|
|
436
|
+
npm run qa:xp
|
|
437
|
+
|
|
438
|
+
# browser QA for examples/arcade.html: both cartridges played with real key presses,
|
|
439
|
+
# cartridge switching, the self-drawn tile map, tweens, the leaderboard modal, and
|
|
440
|
+
# real clicks on the HUD
|
|
441
|
+
npm run qa:arcade
|
|
442
|
+
|
|
443
|
+
# docs: regenerate the API reference and check relative links
|
|
444
|
+
npm run docs
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
`npm run qa:admin` drives a real browser: it asserts every page has zero overlapping
|
|
448
|
+
top-level nodes and identical first-element offsets, opens every popup layer and
|
|
449
|
+
asserts it can be closed again, checks that clicking an in-row action button does
|
|
450
|
+
not select the row, then walks the business scenario — breadcrumb follows the page,
|
|
451
|
+
the floating button opens the tour, the back-to-top button returns to the top, the
|
|
452
|
+
inventory table pages, the order queue drives the detail pane, anchors scroll the
|
|
453
|
+
detail, the image preview opens/zooms, and the settings password form revalidates
|
|
454
|
+
across fields. Every popup is screenshotted to `/tmp/qa-*.png`, and any console
|
|
455
|
+
error fails the run.
|
|
456
|
+
|
|
457
|
+
`npm run qa:gallery` does the same for the full gallery page with real mouse
|
|
458
|
+
events (hit-test path): it asserts no two top-level clusters overlap, drives the
|
|
459
|
+
breadcrumb collapse, the radio / checkbox groups, drags the splitter divider and
|
|
460
|
+
checks the watermark tiling, then screenshots to `/tmp/qa-gallery*.png`.
|
|
461
|
+
|
|
462
|
+
`npm run qa:workbench` covers the support workbench (queue → profile, reply composer,
|
|
463
|
+
quick replies, tags, rating, tour, back-to-top, splitter drag) and `npm run qa:xp`
|
|
464
|
+
covers the Windows XP desktop: the boot splash → welcome screen → typing a password and
|
|
465
|
+
pressing Enter (the run asserts the startup cue actually fired), log off back to the
|
|
466
|
+
welcome screen, shut down, power back on, logging in again with the **Log in** button, the
|
|
467
|
+
tray mute toggle, icons, window drag/minimise/restore, Start menu,
|
|
468
|
+
Minesweeper (first-click-safe, flag cycle, difficulty, timer, win), the Paint canvas,
|
|
469
|
+
the wallpaper switch, the clock — and the IE window really fetching pages (plus its
|
|
470
|
+
404 page, back button, `about:xp` table and bookmarks). `qa:xp` starts a small static
|
|
471
|
+
server itself, because `fetch` does not work from `file://`.
|
|
472
|
+
|
|
473
|
+
`npm run qa:arcade` plays the arcade page with **real key presses**. On the Tetris
|
|
474
|
+
cartridge it moves, rotates, soft-drops, hard-drops, asserts that `P` really stops
|
|
475
|
+
gravity, builds a deterministic board to force a line clear, keeps dropping until game
|
|
476
|
+
over (the best score lands in `localStorage`) and restarts with `R`. Then it clicks the
|
|
477
|
+
Snake cartridge and checks the swap (new board, re-captioned HUD, a fresh model), steers
|
|
478
|
+
with arrow keys, feeds the snake to grow it, drives it into a wall, and switches back —
|
|
479
|
+
plus real clicks on the pause / restart buttons and the sound switch. Layout assertions
|
|
480
|
+
keep both boards inside the screen bezel and the panels from overlapping.
|
|
481
|
+
|
|
482
|
+
See [ROADMAP.md](./ROADMAP.md) for the component backlog and what is still missing
|
|
483
|
+
per component.
|
|
484
|
+
|
|
485
|
+
## License
|
|
486
|
+
|
|
487
|
+
[MIT](./LICENSE)
|