ranui 0.1.10-alpha.9 → 0.2.0-alpha.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/README.md +397 -0
- package/README.zh-CN.md +216 -37
- package/dist/assets/image/failImage.d.ts +2 -0
- package/dist/bin/generate-component-api.d.ts +1 -0
- package/dist/bin/generate-style-docs.d.ts +1 -0
- package/dist/build/config.bundle.d.ts +2 -0
- package/dist/build/config.d.ts +3 -0
- package/dist/build/config.es.d.ts +2 -0
- package/dist/build/config.umd.d.ts +1 -0
- package/dist/builder.d.ts +11 -0
- package/dist/builder.js +39 -0
- package/dist/button-DukaD6uL.js +117 -0
- package/dist/button.js +3 -5
- package/dist/card-DQUJoDUQ.js +60 -0
- package/dist/card.js +5 -0
- package/dist/checkbox-iHZQb-yQ.js +94 -0
- package/dist/checkbox.js +3 -5
- package/dist/colorpicker-Ch67dWa_.js +302 -0
- package/dist/colorpicker.js +3 -9
- package/dist/components/button/index.d.ts +44 -0
- package/dist/components/card/index.d.ts +21 -0
- package/dist/components/checkbox/index.d.ts +35 -0
- package/dist/components/colorpicker/index.d.ts +88 -0
- package/dist/components/dropdown/index.d.ts +32 -0
- package/dist/components/form/index.d.ts +15 -0
- package/dist/components/icon/index.d.ts +58 -0
- package/dist/components/image/index.d.ts +25 -0
- package/dist/components/input/index.d.ts +229 -0
- package/dist/components/link/index.d.ts +21 -0
- package/dist/components/loading/index.d.ts +76 -0
- package/dist/components/math/index.d.ts +16 -0
- package/dist/components/message/container.d.ts +8 -0
- package/dist/components/message/index.d.ts +32 -0
- package/dist/components/modal/index.d.ts +100 -0
- package/dist/components/player/core/constants.d.ts +5 -0
- package/dist/components/player/core/controller.d.ts +33 -0
- package/dist/components/player/core/events.d.ts +10 -0
- package/dist/components/player/core/fullscreen.d.ts +5 -0
- package/dist/components/player/core/levels.d.ts +20 -0
- package/dist/components/player/core/media.d.ts +50 -0
- package/dist/components/player/core/playback.d.ts +14 -0
- package/dist/components/player/core/progress.d.ts +2 -0
- package/dist/components/player/core/state.d.ts +27 -0
- package/dist/components/player/core/view.d.ts +39 -0
- package/dist/components/player/index.d.ts +277 -0
- package/dist/components/popover/content/index.d.ts +12 -0
- package/dist/components/popover/index.d.ts +95 -0
- package/dist/components/progress/index.d.ts +40 -0
- package/dist/components/radar/index.d.ts +52 -0
- package/dist/components/route/index.d.ts +26 -0
- package/dist/components/router/index.d.ts +24 -0
- package/dist/components/scratch/index.d.ts +19 -0
- package/dist/components/section/index.d.ts +23 -0
- package/dist/components/select/dropdown-item/index.d.ts +21 -0
- package/dist/components/select/index.d.ts +135 -0
- package/dist/components/select/option/index.d.ts +19 -0
- package/dist/components/skeleton/index.d.ts +13 -0
- package/dist/components/tab/index.d.ts +58 -0
- package/dist/components/tabpane/index.d.ts +33 -0
- package/dist/demo/i18n.d.ts +4 -0
- package/dist/demo/index.d.ts +3 -0
- package/dist/demo/register-icons.d.ts +1 -0
- package/dist/dropdown-C5362qTy.js +82 -0
- package/dist/dropdown.js +6 -0
- package/dist/events-C59wGdni.js +30 -0
- package/dist/factory-BdsO0dEW.js +165 -0
- package/dist/form-05Lxxl3D.js +44 -0
- package/dist/form.js +5 -0
- package/dist/icon-DmoEnao5.js +165 -0
- package/dist/icon.js +4 -4
- package/dist/{index-KS2nvVcO.js → image-6GayFDqX.js} +58 -71
- package/dist/image.js +3 -2
- package/dist/index.cjs +35 -0
- package/dist/index.d.ts +277 -114
- package/dist/index.iife.js +306 -0
- package/dist/index.js +78 -38
- package/dist/input-DoraIb3x.js +198 -0
- package/dist/input.js +2 -3
- package/dist/katex-es-Bpesphgp.js +23682 -0
- package/dist/katex-es-Cnq5Pk8K.cjs +261 -0
- package/dist/link-BW-gvrpN.js +77 -0
- package/dist/link.js +6 -0
- package/dist/loading-Mko22ncl.js +206 -0
- package/dist/loading.js +4 -6
- package/dist/math-B5PpSnFR.js +52 -0
- package/dist/math.js +2 -4
- package/dist/message-CQENvkoi.js +137 -0
- package/dist/message.js +3 -4
- package/dist/mocks-CMGoftIc.js +179 -0
- package/dist/modal-DruDj3AB.js +350 -0
- package/dist/modal.js +3 -2
- package/dist/player-CvfmwWUa.js +13536 -0
- package/dist/player.js +3 -6
- package/dist/playwright.config.d.ts +2 -0
- package/dist/plugins/load-svg.d.ts +9 -0
- package/dist/popover-BCvHwLZb.js +213 -0
- package/dist/popover.js +3 -5
- package/dist/progress-BWkNf2J5.js +123 -0
- package/dist/progress.js +3 -5
- package/dist/radar-CWsawt2g.js +181 -0
- package/dist/radar.js +3 -4
- package/dist/ranui.css +1 -0
- package/dist/route-BY_E6-2C.js +88 -0
- package/dist/route.js +6 -0
- package/dist/router-BDcv4F-2.js +79 -0
- package/dist/router.js +6 -0
- package/dist/scratch-Dbd9G6Q6.js +60 -0
- package/dist/scratch.js +4 -0
- package/dist/section-CNTvgdQ-.js +63 -0
- package/dist/section.js +5 -0
- package/dist/select-BmhCUTLb.js +433 -0
- package/dist/select.js +3 -8
- package/dist/signal-XHfNsQNW.js +57 -0
- package/dist/skeleton-DPv9nFhL.js +32 -0
- package/dist/skeleton.js +3 -2
- package/dist/ssg.js +21 -0
- package/dist/ssr-DSGwPtEd.js +34 -0
- package/dist/ssr-stream.d.ts +1 -0
- package/dist/ssr-stream.js +87 -0
- package/dist/ssr.d.ts +1 -0
- package/dist/ssr.js +7 -0
- package/dist/style.d.ts +1 -0
- package/dist/style.js +0 -0
- package/dist/tab-C9swK-hm.js +185 -0
- package/dist/tab.js +3 -4
- package/dist/tabpane-B_M-Jokw.js +87 -0
- package/dist/tabpane.js +3 -2
- package/dist/test/e2e/button.spec.d.ts +1 -0
- package/dist/test/e2e/checkbox.spec.d.ts +1 -0
- package/dist/test/e2e/helpers.d.ts +13 -0
- package/dist/test/e2e/icon.spec.d.ts +1 -0
- package/dist/test/e2e/image.spec.d.ts +1 -0
- package/dist/test/e2e/input.spec.d.ts +1 -0
- package/dist/test/e2e/loading.spec.d.ts +1 -0
- package/dist/test/e2e/message.spec.d.ts +1 -0
- package/dist/test/e2e/modal.spec.d.ts +1 -0
- package/dist/test/e2e/popover.spec.d.ts +1 -0
- package/dist/test/e2e/progress.spec.d.ts +1 -0
- package/dist/test/e2e/radar.spec.d.ts +1 -0
- package/dist/test/e2e/select.spec.d.ts +1 -0
- package/dist/test/e2e/skeleton.spec.d.ts +1 -0
- package/dist/test/e2e/tab.spec.d.ts +1 -0
- package/dist/test/e2e/visual/colorpicker.spec.d.ts +1 -0
- package/dist/test/e2e/visual/components.spec.d.ts +1 -0
- package/dist/test/e2e/visual/player.spec.d.ts +1 -0
- package/dist/test/integration/composition.test.d.ts +23 -0
- package/dist/test/setup.d.ts +0 -0
- package/dist/test/ssr/builder.core.ssr.test.d.ts +1 -0
- package/dist/test/ssr/builder.factory.ssr.test.d.ts +1 -0
- package/dist/test/ssr/builder.mocks.test.d.ts +1 -0
- package/dist/test/ssr/components/button.test.d.ts +1 -0
- package/dist/test/ssr/css-vars.test.d.ts +1 -0
- package/dist/test/ssr/registry.test.d.ts +1 -0
- package/dist/test/ssr/router.ssg.test.d.ts +2 -0
- package/dist/test/ssr/snapshots/button.test.d.ts +1 -0
- package/dist/test/ssr/stream.test.d.ts +2 -0
- package/dist/test/ssr/theme.tokens.ssr.test.d.ts +1 -0
- package/dist/test/unit/builder.core.browser.test.d.ts +1 -0
- package/dist/test/unit/builder.escape.test.d.ts +1 -0
- package/dist/test/unit/builder.selectors.test.d.ts +1 -0
- package/dist/test/unit/button.contract.test.d.ts +1 -0
- package/dist/test/unit/card.contract.test.d.ts +1 -0
- package/dist/test/unit/checkbox.contract.test.d.ts +1 -0
- package/dist/test/unit/colorpicker.color.utils.test.d.ts +1 -0
- package/dist/test/unit/colorpicker.contract.test.d.ts +1 -0
- package/dist/test/unit/colorpicker.methods.test.d.ts +1 -0
- package/dist/test/unit/component.helpers.test.d.ts +1 -0
- package/dist/test/unit/dropdown-item.contract.test.d.ts +1 -0
- package/dist/test/unit/dropdown.contract.test.d.ts +1 -0
- package/dist/test/unit/form.contract.test.d.ts +1 -0
- package/dist/test/unit/helpers/component.d.ts +3 -0
- package/dist/test/unit/helpers/dom.d.ts +2 -0
- package/dist/test/unit/icon.contract.test.d.ts +1 -0
- package/dist/test/unit/image.contract.test.d.ts +1 -0
- package/dist/test/unit/input.contract.test.d.ts +1 -0
- package/dist/test/unit/link.contract.test.d.ts +1 -0
- package/dist/test/unit/loading.contract.test.d.ts +1 -0
- package/dist/test/unit/math.contract.test.d.ts +1 -0
- package/dist/test/unit/message.component.test.d.ts +1 -0
- package/dist/test/unit/message.container.test.d.ts +1 -0
- package/dist/test/unit/modal.contract.test.d.ts +1 -0
- package/dist/test/unit/option.contract.test.d.ts +1 -0
- package/dist/test/unit/package-exports.source.test.d.ts +1 -0
- package/dist/test/unit/player-controls.test.d.ts +1 -0
- package/dist/test/unit/player-core.test.d.ts +1 -0
- package/dist/test/unit/player-event-handlers.test.d.ts +1 -0
- package/dist/test/unit/player-events.test.d.ts +1 -0
- package/dist/test/unit/player-fullscreen.test.d.ts +1 -0
- package/dist/test/unit/player-interactions.test.d.ts +1 -0
- package/dist/test/unit/player-levels.test.d.ts +1 -0
- package/dist/test/unit/player-media.test.d.ts +1 -0
- package/dist/test/unit/player-playback.test.d.ts +1 -0
- package/dist/test/unit/player.contract.test.d.ts +1 -0
- package/dist/test/unit/popover-accessibility.test.d.ts +1 -0
- package/dist/test/unit/popover.contract.test.d.ts +2 -0
- package/dist/test/unit/progress.contract.test.d.ts +1 -0
- package/dist/test/unit/radar.contract.test.d.ts +1 -0
- package/dist/test/unit/route.contract.test.d.ts +1 -0
- package/dist/test/unit/router.contract.test.d.ts +2 -0
- package/dist/test/unit/scratch.contract.test.d.ts +1 -0
- package/dist/test/unit/select-accessibility.test.d.ts +1 -0
- package/dist/test/unit/select-option.contract.test.d.ts +1 -0
- package/dist/test/unit/select.contract.test.d.ts +1 -0
- package/dist/test/unit/skeleton.contract.test.d.ts +1 -0
- package/dist/test/unit/ssr.test.d.ts +1 -0
- package/dist/test/unit/tab.contract.test.d.ts +2 -0
- package/dist/test/unit/theme.semantic-tokens.test.d.ts +1 -0
- package/dist/test/unit/theme.skin-tokens.test.d.ts +1 -0
- package/dist/test/unit/utils.dom.test.d.ts +1 -0
- package/dist/test/unit/utils.error.test.d.ts +1 -0
- package/dist/test/unit/utils.i18n.test.d.ts +1 -0
- package/dist/test/unit/utils.image.test.d.ts +1 -0
- package/dist/test/unit/utils.math.test.d.ts +1 -0
- package/dist/test/unit/utils.router.test.d.ts +1 -0
- package/dist/test/unit/utils.signal.test.d.ts +1 -0
- package/dist/test/unit/utils.ssr-stream.test.d.ts +1 -0
- package/dist/test/unit/utils.style.test.d.ts +1 -0
- package/dist/test/unit/utils.theme.test.d.ts +1 -0
- package/dist/test/unit/utils.tween.test.d.ts +1 -0
- package/dist/tsconfig.json +8 -8
- package/dist/typings.d.ts +163 -114
- package/dist/utils/builder/core.d.ts +70 -0
- package/dist/utils/builder/env.d.ts +1 -0
- package/dist/utils/builder/escape.d.ts +2 -0
- package/dist/utils/builder/events.d.ts +55 -0
- package/dist/utils/builder/factory.d.ts +18 -0
- package/dist/utils/builder/index.d.ts +9 -0
- package/dist/utils/builder/mocks.d.ts +62 -0
- package/dist/utils/builder/selectors.d.ts +4 -0
- package/dist/utils/builder/signal.d.ts +87 -0
- package/dist/utils/color.d.ts +52 -0
- package/dist/utils/component.d.ts +16 -0
- package/dist/utils/dom.d.ts +39 -0
- package/dist/utils/error.d.ts +6 -0
- package/dist/utils/i18n/index.d.ts +53 -0
- package/dist/utils/i18n.js +88 -0
- package/dist/utils/image.d.ts +39 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/math.d.ts +26 -0
- package/dist/utils/router/index.d.ts +128 -0
- package/dist/utils/router.js +197 -0
- package/dist/utils/ssg.d.ts +49 -0
- package/dist/utils/ssr-registry.d.ts +8 -0
- package/dist/utils/ssr-stream.d.ts +23 -0
- package/dist/utils/ssr.d.ts +17 -0
- package/dist/utils/style.d.ts +12 -0
- package/dist/utils/theme.d.ts +9 -0
- package/dist/utils/tween.d.ts +21 -0
- package/dist/utils/video.d.ts +40 -0
- package/dist/utils-D4pPF77E.js +689 -0
- package/dist/vite.config.d.ts +6 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.ssr.d.ts +2 -0
- package/docs/COMPONENTS.md +326 -0
- package/docs/DESIGN.md +182 -0
- package/docs/style-tokens-public.md +880 -0
- package/package.json +136 -77
- package/typings.d.ts +163 -114
- package/dist/_commonjs-dynamic-modules-DLbDWi6a.js +0 -12
- package/dist/add-user-BN1JlY7e.js +0 -4
- package/dist/arrow-down-B_hCY3Wt.js +0 -662
- package/dist/book-nTEFXU2x.js +0 -4
- package/dist/check-circle-fill-B_pd8ZSs.js +0 -4
- package/dist/check-circle-szyAJiap.js +0 -4
- package/dist/close-CFnkhudp.js +0 -4
- package/dist/close-circle-CwmuN2C6.js +0 -4
- package/dist/close-circle-fill-jSqPPw9i.js +0 -4
- package/dist/colz-DJZvo_8B.js +0 -232
- package/dist/docx-CHkPTdGU.js +0 -3665
- package/dist/drop-4j4abxje.js +0 -36
- package/dist/eye-D_mEt17f.js +0 -4
- package/dist/eye-close-BVr3NJtg.js +0 -4
- package/dist/home-BUQ4USMk.js +0 -4
- package/dist/iconShadowless.js +0 -6
- package/dist/index-15M0olqa.js +0 -204
- package/dist/index-1v568_nq.js +0 -140
- package/dist/index-B897keW5.js +0 -279
- package/dist/index-BZR0HaP2.js +0 -40
- package/dist/index-B_GuEcTd.js +0 -9575
- package/dist/index-Bc2Bcl8D.js +0 -63701
- package/dist/index-C4Sv0T51.js +0 -495
- package/dist/index-C6RcMHoH.js +0 -187
- package/dist/index-C9z10IgG.js +0 -103
- package/dist/index-CFjEuvBW.js +0 -308
- package/dist/index-CGEx9VOA.js +0 -17106
- package/dist/index-CGYfjKUV.js +0 -72
- package/dist/index-CQTJoWhb.js +0 -19
- package/dist/index-CiWwvEMM.js +0 -511
- package/dist/index-CkM9ENTf.js +0 -496
- package/dist/index-CtVOgBDk.js +0 -659
- package/dist/index-D4qUro5V.js +0 -279
- package/dist/index-D4y9c1Dj.js +0 -310
- package/dist/index-DAxs7Dd2.js +0 -119
- package/dist/index-DC5EA657.js +0 -91
- package/dist/index-DJZmDHz-.js +0 -146
- package/dist/index-D_CA5VDZ.js +0 -158
- package/dist/index-WfFw4LJT.js +0 -490
- package/dist/index-mhFL9MJp.js +0 -320
- package/dist/info-circle-COnL5bTJ.js +0 -4
- package/dist/info-circle-fill-CFeVMdci.js +0 -4
- package/dist/inputShadowless.js +0 -6
- package/dist/jszip.min-Dhs3nUkD.js +0 -2346
- package/dist/katex-es-B5r5vt3m.js +0 -14628
- package/dist/loading-Dcc5RApI.js +0 -4
- package/dist/loading-scene-BMc2wqKm.js +0 -4
- package/dist/lock-Cr7BnmWN.js +0 -4
- package/dist/message-D36_Zo2l.js +0 -4
- package/dist/pdf-Cx0VWKFo.js +0 -118
- package/dist/power-off-lQRbiBak.js +0 -4
- package/dist/preview-CJbz9GjO.js +0 -4
- package/dist/preview.js +0 -7
- package/dist/selectShadowless.js +0 -564
- package/dist/setting-DemlgzVC.js +0 -4
- package/dist/shadowless.js +0 -8
- package/dist/sprite-CH2zLtZy.js +0 -141
- package/dist/style.css +0 -1
- package/dist/team-tl4NJXPC.js +0 -4
- package/dist/umd/index.umd.cjs +0 -60
- package/dist/umd/shadowless/shadowless.umd.cjs +0 -1
- package/dist/umd/shadowless/style.css +0 -1
- package/dist/umd/style.css +0 -1
- package/dist/unlock-CeU74z9n.js +0 -4
- package/dist/user-B-eVXwuk.js +0 -4
- package/dist/warning-circle-DDUgEDIv.js +0 -4
- package/dist/warning-circle-fill-lODUKz0i.js +0 -4
- package/readme.md +0 -195
package/README.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# ranui
|
|
2
|
+
|
|
3
|
+
An experimental UI component library based on Web Components. Components use Shadow DOM encapsulation, CSS Token theming, and SSR / Declarative Shadow DOM support.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<a href="https://github.com/chaxus/ran"><img src="https://img.shields.io/github/actions/workflow/status/chaxus/ran/ci.yml" alt="Build Status"></a>
|
|
8
|
+
<a href="https://github.com/chaxus/ran"><img src="https://img.shields.io/npm/v/ranui.svg" alt="npm-v"></a>
|
|
9
|
+
<a href="https://github.com/chaxus/ran"><img src="https://img.shields.io/npm/dt/ranui.svg" alt="npm-d"></a>
|
|
10
|
+
<a href="https://github.com/chaxus/ran"><img src="https://img.badgesize.io/https:/unpkg.com/ranui/dist/umd/shadowless/shadowless.umd.cjs?label=brotli&compression=brotli" alt="brotli"></a>
|
|
11
|
+
<a href="https://github.com/chaxus/ran"><img src="https://img.shields.io/badge/module%20formats-umd%2C%20esm-green.svg" alt="module formats: umd, esm"></a>
|
|
12
|
+
|
|
13
|
+
[中文](./README.zh-CN.md) | **English**
|
|
14
|
+
|
|
15
|
+
## Important Notice
|
|
16
|
+
|
|
17
|
+
This is an **experimental UI library** in early development. It is usable, but primarily intended for learning and experimentation.
|
|
18
|
+
|
|
19
|
+
Key points:
|
|
20
|
+
|
|
21
|
+
- **Early development**: features are still being developed and refined.
|
|
22
|
+
- **Experimental**: APIs may change frequently.
|
|
23
|
+
- **Learning-oriented**: mainly intended for learning Web Components and UI development.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
1. **Cross-framework compatibility:** works with React, Vue, Preact, SolidJS, Svelte, and any JavaScript project that follows W3C standards.
|
|
28
|
+
2. **Native experience:** use custom elements such as `<r-button>` and `<r-modal>` like native HTML elements.
|
|
29
|
+
3. **Modular design:** supports both full imports and per-component imports for better maintainability and bundle control.
|
|
30
|
+
4. **Shadow DOM encapsulation:** component internals are isolated by default, while CSS Tokens, `::part()`, and the `sheet` attribute provide controlled styling hooks.
|
|
31
|
+
5. **TypeScript support:** built with TypeScript and type definitions.
|
|
32
|
+
6. **SSR friendly:** supports server rendering through `defineSSR`, `renderToString`, and Declarative Shadow DOM.
|
|
33
|
+
7. **Accessible:** ARIA roles/states, full keyboard navigation, form-associated inputs (`<r-checkbox>`/`<r-input>`/`<r-select>` participate in native `FormData`), live-region toasts, and `prefers-reduced-motion` support.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Using npm:
|
|
38
|
+
|
|
39
|
+
```console
|
|
40
|
+
npm install ranui --save
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Documentation and Examples
|
|
44
|
+
|
|
45
|
+
[See components and use examples](https://chaxus.github.io/ran/src/ranui/)
|
|
46
|
+
|
|
47
|
+
### Components & API reference
|
|
48
|
+
|
|
49
|
+
Every element's attributes, properties, **events (with `detail` shapes)**, slots, and `::part()` names are generated from source — no need to grep the exports:
|
|
50
|
+
|
|
51
|
+
- Per-element API: [docs/COMPONENTS.md](./docs/COMPONENTS.md)
|
|
52
|
+
- Design standard (color/spacing/typography/motion/a11y): [docs/DESIGN.md](./docs/DESIGN.md)
|
|
53
|
+
|
|
54
|
+
Regenerate after changing a component's API with:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pnpm doc:api
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### AI / Claude Code skill
|
|
61
|
+
|
|
62
|
+
A ready-to-use skill lets AI assistants (Claude Code) read and use ranui without
|
|
63
|
+
spelunking the source. It's published from the `ran` plugin marketplace:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
/plugin marketplace add chaxus/ran
|
|
67
|
+
/plugin install ranui@ran
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Once installed, Claude uses it automatically when you work with ranui (or invoke it
|
|
71
|
+
explicitly as `/ranui:ranui`). The skill covers the import map, element inventory, the
|
|
72
|
+
builder/reactivity API, accessibility, and usage examples, and points to the API
|
|
73
|
+
reference shipped in the package ([docs/COMPONENTS.md](./docs/COMPONENTS.md)).
|
|
74
|
+
|
|
75
|
+
### Styling Documentation
|
|
76
|
+
|
|
77
|
+
The styling system is unified around CSS Tokens and `::part()`.
|
|
78
|
+
|
|
79
|
+
- Style override guide: [docs/style-override.md](./docs/style-override.md)
|
|
80
|
+
- Complete Token/Part list, generated automatically: [docs/style-tokens-parts.md](./docs/style-tokens-parts.md)
|
|
81
|
+
- Public styling API for consumers, generated automatically: [docs/style-tokens-public.md](./docs/style-tokens-public.md)
|
|
82
|
+
- Public token filter config: [docs/style-token-filter.json](./docs/style-token-filter.json)
|
|
83
|
+
|
|
84
|
+
Refresh the styling docs with:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pnpm doc:style
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Theming
|
|
91
|
+
|
|
92
|
+
ranui ships a single token system based on the [Geist design system](https://vercel.com/design), with `light`, `dark`, and `system` modes — there are no theme packs. Switch the mode or override any token at runtime (SSR-safe):
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { initTheme, setTheme, setThemeToken, setThemeTokens } from 'ranui';
|
|
96
|
+
import 'ranui/style';
|
|
97
|
+
|
|
98
|
+
initTheme(); // restore the persisted choice on load
|
|
99
|
+
setTheme('system'); // 'light' | 'dark' | 'system'
|
|
100
|
+
setThemeToken('--ran-color-primary', '#6c47ff');
|
|
101
|
+
setThemeTokens({ '--ran-radius-md': '10px' });
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Dark mode redefines only the base color scale; semantic tokens (`--ran-color-*`) reference it and flip automatically. See [docs/THEME_STYLE_SYSTEM_DESIGN.md](./docs/THEME_STYLE_SYSTEM_DESIGN.md) and [docs/DESIGN.md](./docs/DESIGN.md).
|
|
105
|
+
|
|
106
|
+
## Imports
|
|
107
|
+
|
|
108
|
+
Use per-component imports to reduce bundle size:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
import 'ranui/button';
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
If styles are missing, import the stylesheet manually:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
import 'ranui/style';
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
If type resolution fails, import one of the type entry points manually:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import 'ranui/typings';
|
|
124
|
+
// or
|
|
125
|
+
import 'ranui/dist/index.d.ts';
|
|
126
|
+
// or
|
|
127
|
+
import 'ranui/type';
|
|
128
|
+
// or
|
|
129
|
+
import 'ranui/dist/typings';
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Only one working type entry is needed.
|
|
133
|
+
|
|
134
|
+
Full import is also supported:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import 'ranui';
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
ES module:
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
import 'ranui';
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
or:
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
import 'ranui/button';
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
UMD, IIFE, CJS:
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
<script src="./ranui/dist/umd/index.umd.cjs"></script>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Usage
|
|
159
|
+
|
|
160
|
+
RanUI components are Web Components, so they can be used without framework-specific wrappers.
|
|
161
|
+
|
|
162
|
+
In most cases, use them like native HTML elements.
|
|
163
|
+
|
|
164
|
+
Examples:
|
|
165
|
+
|
|
166
|
+
- html
|
|
167
|
+
- js
|
|
168
|
+
- jsx
|
|
169
|
+
- vue
|
|
170
|
+
- tsx
|
|
171
|
+
|
|
172
|
+
### html
|
|
173
|
+
|
|
174
|
+
```html
|
|
175
|
+
<script src="./ranui/dist/umd/index.umd.cjs"></script>
|
|
176
|
+
|
|
177
|
+
<body>
|
|
178
|
+
<r-button>Button</r-button>
|
|
179
|
+
</body>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### js
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
import 'ranui';
|
|
186
|
+
|
|
187
|
+
const Button = document.createElement('r-button');
|
|
188
|
+
Button.textContent = 'this is button text';
|
|
189
|
+
document.body.appendChild(Button);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### jsx
|
|
193
|
+
|
|
194
|
+
```jsx
|
|
195
|
+
import 'ranui';
|
|
196
|
+
|
|
197
|
+
const App = () => {
|
|
198
|
+
return (
|
|
199
|
+
<>
|
|
200
|
+
<r-button>Button</r-button>
|
|
201
|
+
</>
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### vue
|
|
207
|
+
|
|
208
|
+
```vue
|
|
209
|
+
<template>
|
|
210
|
+
<r-button></r-button>
|
|
211
|
+
</template>
|
|
212
|
+
<script>
|
|
213
|
+
import 'ranui';
|
|
214
|
+
</script>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### tsx
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import 'ranui/button';
|
|
221
|
+
|
|
222
|
+
const Button = () => {
|
|
223
|
+
return (
|
|
224
|
+
<div>
|
|
225
|
+
<r-button type="primary">button</r-button>
|
|
226
|
+
</div>
|
|
227
|
+
);
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Message Position and Container
|
|
232
|
+
|
|
233
|
+
`window.message` supports custom top offset, z-index, and mount container:
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
import 'ranui/message';
|
|
237
|
+
|
|
238
|
+
const customRoot = document.getElementById('custom-message-root');
|
|
239
|
+
|
|
240
|
+
window.message?.success({
|
|
241
|
+
content: 'Saved',
|
|
242
|
+
duration: 2000,
|
|
243
|
+
top: 24,
|
|
244
|
+
zIndex: 3000,
|
|
245
|
+
getContainer: () => customRoot,
|
|
246
|
+
});
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
`top` supports `number | string`; `24` becomes `24px`, while `'2rem'` keeps its unit.
|
|
250
|
+
|
|
251
|
+
`zIndex` supports `number | string`.
|
|
252
|
+
|
|
253
|
+
`getContainer` must return an `HTMLElement`; when omitted, messages mount to `document.body`.
|
|
254
|
+
|
|
255
|
+
### Reactive Primitives
|
|
256
|
+
|
|
257
|
+
`signal`, `createEffect`, `computed`, and `batch` ship alongside the DOM builder for building reactive page sections without a framework. The design mirrors SwiftUI's `@Observable` with two correctness improvements from Solid.js: effects automatically clean up stale subscriptions before each re-run, and `batch()` coalesces multiple signal writes into a single effect flush — the same guarantee SwiftUI gives for free.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { signal, createEffect, computed, batch, EventManager, Div, ButtonBuilder } from 'ranui/builder';
|
|
261
|
+
|
|
262
|
+
function initCounter(container: HTMLElement) {
|
|
263
|
+
const [count, setCount] = signal(0);
|
|
264
|
+
const [step, setStep] = signal(1);
|
|
265
|
+
const doubled = computed(() => count() * 2);
|
|
266
|
+
const scope = new EventManager();
|
|
267
|
+
|
|
268
|
+
const label = Div().build();
|
|
269
|
+
const view = Div()
|
|
270
|
+
.children(
|
|
271
|
+
label,
|
|
272
|
+
ButtonBuilder()
|
|
273
|
+
.text('+')
|
|
274
|
+
.listen(scope, 'click', () => setCount((n) => n + step())),
|
|
275
|
+
ButtonBuilder()
|
|
276
|
+
.text('reset')
|
|
277
|
+
.listen(
|
|
278
|
+
scope,
|
|
279
|
+
'click',
|
|
280
|
+
() =>
|
|
281
|
+
batch(() => {
|
|
282
|
+
setCount(0);
|
|
283
|
+
setStep(1);
|
|
284
|
+
}), // two writes → one effect flush
|
|
285
|
+
),
|
|
286
|
+
)
|
|
287
|
+
.build();
|
|
288
|
+
|
|
289
|
+
const dispose = createEffect(() => {
|
|
290
|
+
label.textContent = `${count()} (×2 = ${doubled()})`;
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
container.appendChild(view);
|
|
294
|
+
return () => {
|
|
295
|
+
dispose();
|
|
296
|
+
scope.abort();
|
|
297
|
+
}; // teardown
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
See [Utility Documentation](./utils/README.md) for the full API.
|
|
302
|
+
|
|
303
|
+
### Routing
|
|
304
|
+
|
|
305
|
+
RanUI includes client-side routing via declarative components and a JavaScript API.
|
|
306
|
+
|
|
307
|
+
**Declarative components:**
|
|
308
|
+
|
|
309
|
+
```html
|
|
310
|
+
<r-router>
|
|
311
|
+
<nav>
|
|
312
|
+
<r-link href="/">Home</r-link>
|
|
313
|
+
<r-link href="/about">About</r-link>
|
|
314
|
+
</nav>
|
|
315
|
+
|
|
316
|
+
<r-route path="/" exact><h2>Home</h2></r-route>
|
|
317
|
+
<r-route path="/about"><h2>About</h2></r-route>
|
|
318
|
+
<r-route path="/users/:id"><h2>User detail</h2></r-route>
|
|
319
|
+
</r-router>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**JavaScript API with navigation guard:**
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
import { createRouter } from 'ranui';
|
|
326
|
+
|
|
327
|
+
const router = createRouter({
|
|
328
|
+
mode: 'history',
|
|
329
|
+
routes: [
|
|
330
|
+
{ path: '/', exact: true, meta: { title: 'Home' } },
|
|
331
|
+
{ path: '/users/:id', meta: { requiresAuth: true } },
|
|
332
|
+
],
|
|
333
|
+
viewTransition: 'spa', // 'spa' | 'mpa' | 'both'
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
router.beforeEach((to, from, next) => {
|
|
337
|
+
if (to.meta?.requiresAuth && !isLoggedIn()) next('/login');
|
|
338
|
+
else next();
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
router.push('/users/42');
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
For pure MPA sites (no JS router needed), use `enableMpaViewTransitions()` to inject `@view-transition { navigation: auto }`. Shared-element morph animations are supported via the standard `view-transition-name` CSS property.
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
import { enableMpaViewTransitions } from 'ranui';
|
|
348
|
+
enableMpaViewTransitions();
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
See the [Router documentation](https://chaxus.github.io/ran/src/ranui/router/) for the full API including guards, `onPageSwap`/`onPageReveal`, and per-element transition names.
|
|
352
|
+
|
|
353
|
+
### SSR & Builder
|
|
354
|
+
|
|
355
|
+
For SSR or declarative UI construction, RanUI internally uses `builder`, the SSR registry, and Declarative Shadow DOM. Components reuse existing Shadow Roots through `ensureShadowRoot` and keep initialization idempotent through `ensureShadowElement`.
|
|
356
|
+
|
|
357
|
+
Source-level SSR rendering example:
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import { Button } from '@/components/button';
|
|
361
|
+
import { renderToString } from '@/utils/ssr';
|
|
362
|
+
|
|
363
|
+
const button = new Button();
|
|
364
|
+
button.setAttribute('effect', 'true');
|
|
365
|
+
|
|
366
|
+
// Outputs an HTML string containing Declarative Shadow DOM.
|
|
367
|
+
const html = renderToString(button);
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
See [Utility Documentation](./utils/README.md) for details.
|
|
371
|
+
|
|
372
|
+
## Component Development Conventions
|
|
373
|
+
|
|
374
|
+
When adding or maintaining components, follow the package conventions:
|
|
375
|
+
|
|
376
|
+
- Extend `RanElement`; do not directly extend the browser `HTMLElement`.
|
|
377
|
+
- Use `ensureShadowRoot` to create or reuse Shadow Roots; do not call `attachShadow` directly.
|
|
378
|
+
- Use `ensureShadowElement` to build Shadow DOM subtrees idempotently.
|
|
379
|
+
- Include `sheet` in `observedAttributes` and sync component-level style overrides through `syncSheetAttribute`.
|
|
380
|
+
- Guard `attributeChangedCallback` with `if (old === next) return;`.
|
|
381
|
+
- Register components with `defineSSR('r-name', Component)`, not direct `customElements.define`.
|
|
382
|
+
- Add both type exports and side-effect imports in `index.ts`; also add standalone entries in `vite.config.ts` and `package.json`.
|
|
383
|
+
- Use `EventManager` from `@/utils/builder` for lifecycle-bound listeners in `connectedCallback`; call `manager.abort()` in `disconnectedCallback` instead of tracking individual `removeEventListener` calls.
|
|
384
|
+
|
|
385
|
+
## Contributing
|
|
386
|
+
|
|
387
|
+
Contributions from learners and developers are welcome. This is an experimental project, so please expect active iteration.
|
|
388
|
+
|
|
389
|
+
## Contributors
|
|
390
|
+
|
|
391
|
+
<a href="https://github.com/chaxus/ran/graphs/contributors">
|
|
392
|
+
<img src="https://contrib.rocks/image?repo=chaxus/ran" />
|
|
393
|
+
</a>
|
|
394
|
+
|
|
395
|
+
## Meta
|
|
396
|
+
|
|
397
|
+
[LICENSE (MIT)](/LICENSE)
|
package/README.zh-CN.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ranui
|
|
2
2
|
|
|
3
|
-
基于 `Web Components`
|
|
3
|
+
基于 `Web Components` 的实验性组件库。组件使用 Shadow DOM 封装、CSS Token 主题体系,并提供 SSR / Declarative Shadow DOM 支持。
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -12,14 +12,25 @@
|
|
|
12
12
|
|
|
13
13
|
**中文** | [English](./README.md)
|
|
14
14
|
|
|
15
|
+
## ⚠️ 重要说明
|
|
16
|
+
|
|
17
|
+
这是一个**实验性 UI 库**,处于早期开发阶段。虽然功能可用,但主要用于学习和实验。
|
|
18
|
+
|
|
19
|
+
**关键要点:**
|
|
20
|
+
|
|
21
|
+
- 🚧 **早期开发**: 功能仍在开发和完善中
|
|
22
|
+
- 🧪 **实验性**: API 可能会频繁变化
|
|
23
|
+
- 📚 **学习导向**: 主要用于学习 Web Components 和 UI 开发
|
|
24
|
+
|
|
15
25
|
## 特点
|
|
16
26
|
|
|
17
27
|
1. **跨框架兼容:** 与 React, Vue, Preact, SolidJS, Svelte 等兼容。可以和遵循 W3C 标准的任何 JavaScript 项目集成。
|
|
18
|
-
2. **原生体验:**
|
|
28
|
+
2. **原生体验:** 易于入门,像使用原生 HTML 标签一样使用 `<r-button>`、`<r-modal>` 等自定义元素。
|
|
19
29
|
3. **模块化设计:** 可选导入和全量导入,以增强可维护性和可伸缩性。
|
|
20
|
-
4.
|
|
30
|
+
4. **Shadow DOM 封装:** 组件内部样式默认隔离,并通过 CSS Token、`::part()` 和 `sheet` 属性开放可控的样式覆盖能力。
|
|
21
31
|
5. **支持类型校验:** 基于 TypeScript 构建,具有类型支持,确保代码的健壮性和可维护性。
|
|
22
|
-
6.
|
|
32
|
+
6. **SSR 友好:** 通过 `defineSSR`、`renderToString` 和 Declarative Shadow DOM 支持服务端渲染场景。
|
|
33
|
+
7. **无障碍:** ARIA 角色/状态、完整键盘导航、表单参与控件(`<r-checkbox>`/`<r-input>`/`<r-select>` 可被原生 `FormData` 收集)、live-region 提示,并尊重 `prefers-reduced-motion`。
|
|
23
34
|
|
|
24
35
|
## 安装
|
|
25
36
|
|
|
@@ -33,6 +44,37 @@ npm install ranui --save
|
|
|
33
44
|
|
|
34
45
|
[See components and use examples](https://chaxus.github.io/ran/cn/src/ranui/)
|
|
35
46
|
|
|
47
|
+
### 样式定制文档
|
|
48
|
+
|
|
49
|
+
当前样式系统已统一为 CSS Token 与 `::part()` 规范。
|
|
50
|
+
|
|
51
|
+
- 样式覆盖规范:[docs/style-override.md](./docs/style-override.md)
|
|
52
|
+
- 完整 Token/Part 清单(自动生成): [docs/style-tokens-parts.md](./docs/style-tokens-parts.md)
|
|
53
|
+
- 面向使用方的公开样式 API(自动生成): [docs/style-tokens-public.md](./docs/style-tokens-public.md)
|
|
54
|
+
- 公开 Token 过滤配置:[docs/style-token-filter.json](./docs/style-token-filter.json)
|
|
55
|
+
|
|
56
|
+
可通过以下命令刷新样式文档:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pnpm doc:style
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 主题与主题包
|
|
63
|
+
|
|
64
|
+
RanUI 支持亮色、暗色与系统主题,并提供多个 CSS-only 主题包。
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { initTheme, setTheme, setThemePack, setThemeToken } from 'ranui';
|
|
68
|
+
import 'ranui/theme-packs/pixel-retro';
|
|
69
|
+
|
|
70
|
+
initTheme();
|
|
71
|
+
setTheme('system');
|
|
72
|
+
setThemePack('pixel-retro');
|
|
73
|
+
setThemeToken('--ran-color-primary', '#2563eb');
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
可用主题包包括 `windows-98`、`windows-xp`、`system-6`、`wired`、`paper`、`pixel-retro`、`neo-brutalism`。使用 `setThemePack('default')` 可恢复默认主题包。
|
|
77
|
+
|
|
36
78
|
## 引入方式
|
|
37
79
|
|
|
38
80
|
支持按需导入,以显著减少包体积大小
|
|
@@ -50,15 +92,17 @@ import 'ranui/style';
|
|
|
50
92
|
如果遇到类型问题,可以选择手动导入类型文件
|
|
51
93
|
|
|
52
94
|
```ts
|
|
53
|
-
import 'ranui/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
或者
|
|
57
|
-
|
|
58
|
-
|
|
95
|
+
import 'ranui/typings';
|
|
96
|
+
// 或者
|
|
97
|
+
import 'ranui/dist/index.d.ts';
|
|
98
|
+
// 或者
|
|
99
|
+
import 'ranui/type';
|
|
100
|
+
// 或者
|
|
59
101
|
import 'ranui/dist/typings';
|
|
60
102
|
```
|
|
61
103
|
|
|
104
|
+
并不是都要,选一个能生效的就行
|
|
105
|
+
|
|
62
106
|
也支持全量导入
|
|
63
107
|
|
|
64
108
|
```ts
|
|
@@ -113,7 +157,7 @@ import 'ranui/button';
|
|
|
113
157
|
import 'ranui';
|
|
114
158
|
|
|
115
159
|
const Button = document.createElement('r-button');
|
|
116
|
-
Button.
|
|
160
|
+
Button.textContent = 'this is button text';
|
|
117
161
|
document.body.appendChild(Button);
|
|
118
162
|
```
|
|
119
163
|
|
|
@@ -145,40 +189,175 @@ import 'ranui';
|
|
|
145
189
|
### tsx
|
|
146
190
|
|
|
147
191
|
```tsx
|
|
148
|
-
|
|
149
|
-
import type { SyntheticEvent } from 'react';
|
|
150
|
-
import React, { useRef } from 'react';
|
|
151
|
-
import 'ranui';
|
|
192
|
+
import 'ranui/button';
|
|
152
193
|
|
|
153
|
-
const
|
|
154
|
-
const ref = useRef<HTMLDivElement | null>(null);
|
|
155
|
-
const uploadFile = (e: SyntheticEvent<HTMLDivElement>) => {
|
|
156
|
-
if (ref.current) {
|
|
157
|
-
const uploadFile = document.createElement('input');
|
|
158
|
-
uploadFile.setAttribute('type', 'file');
|
|
159
|
-
uploadFile.click();
|
|
160
|
-
uploadFile.onchange = (e) => {
|
|
161
|
-
const { files = [] } = uploadFile;
|
|
162
|
-
if (files && files?.length > 0 && ref.current) {
|
|
163
|
-
ref.current.setAttribute('src', '');
|
|
164
|
-
const file = files[0];
|
|
165
|
-
const url = URL.createObjectURL(file);
|
|
166
|
-
ref.current.setAttribute('src', url);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
};
|
|
194
|
+
const Button = () => {
|
|
171
195
|
return (
|
|
172
196
|
<div>
|
|
173
|
-
<r-
|
|
174
|
-
<r-button type="primary" onClick={uploadFile}>
|
|
175
|
-
choose file to preview
|
|
176
|
-
</r-button>
|
|
197
|
+
<r-button type="primary">button</r-button>
|
|
177
198
|
</div>
|
|
178
199
|
);
|
|
179
200
|
};
|
|
180
201
|
```
|
|
181
202
|
|
|
203
|
+
### Message 位置与容器配置
|
|
204
|
+
|
|
205
|
+
`window.message` 现已支持自定义顶部偏移、层级和挂载容器:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import 'ranui/message';
|
|
209
|
+
|
|
210
|
+
const customRoot = document.getElementById('custom-message-root');
|
|
211
|
+
|
|
212
|
+
window.message?.success({
|
|
213
|
+
content: '保存成功',
|
|
214
|
+
duration: 2000,
|
|
215
|
+
top: 24,
|
|
216
|
+
zIndex: 3000,
|
|
217
|
+
getContainer: () => customRoot,
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`top` 支持 `number | string`(`24` 会转成 `24px`,`'2rem'` 会保留原单位)。
|
|
222
|
+
|
|
223
|
+
`zIndex` 支持 `number | string`。
|
|
224
|
+
|
|
225
|
+
`getContainer` 需要返回 `HTMLElement`;未传时默认挂载到 `document.body`。
|
|
226
|
+
|
|
227
|
+
### 响应式原语
|
|
228
|
+
|
|
229
|
+
`signal`、`createEffect`、`computed`、`batch` 与 DOM builder 一起提供,用于在无框架依赖的情况下构建响应式页面区块。设计参考 SwiftUI 的 `@Observable`,并借鉴 Solid.js 补充了两个正确性改进:effect 重新执行前自动清理过期订阅;`batch()` 将多次 signal 写入合并为一次 effect flush,与 SwiftUI 自动合并变更的行为对齐。
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
import { signal, createEffect, computed, batch, EventManager, Div, ButtonBuilder } from 'ranui/builder';
|
|
233
|
+
|
|
234
|
+
function initCounter(container: HTMLElement) {
|
|
235
|
+
const [count, setCount] = signal(0);
|
|
236
|
+
const [step, setStep] = signal(1);
|
|
237
|
+
const doubled = computed(() => count() * 2);
|
|
238
|
+
const scope = new EventManager();
|
|
239
|
+
|
|
240
|
+
const label = Div().build();
|
|
241
|
+
const view = Div()
|
|
242
|
+
.children(
|
|
243
|
+
label,
|
|
244
|
+
ButtonBuilder()
|
|
245
|
+
.text('+')
|
|
246
|
+
.listen(scope, 'click', () => setCount((n) => n + step())),
|
|
247
|
+
ButtonBuilder()
|
|
248
|
+
.text('重置')
|
|
249
|
+
.listen(
|
|
250
|
+
scope,
|
|
251
|
+
'click',
|
|
252
|
+
() =>
|
|
253
|
+
batch(() => {
|
|
254
|
+
setCount(0);
|
|
255
|
+
setStep(1);
|
|
256
|
+
}), // 两次写入 → 一次 flush
|
|
257
|
+
),
|
|
258
|
+
)
|
|
259
|
+
.build();
|
|
260
|
+
|
|
261
|
+
const dispose = createEffect(() => {
|
|
262
|
+
label.textContent = `${count()} (×2 = ${doubled()})`;
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
container.appendChild(view);
|
|
266
|
+
return () => {
|
|
267
|
+
dispose();
|
|
268
|
+
scope.abort();
|
|
269
|
+
}; // 区块销毁时清理
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
详细 API 请参考 [工具文档](./utils/README.zh-CN.md)。
|
|
274
|
+
|
|
275
|
+
### 路由
|
|
276
|
+
|
|
277
|
+
RanUI 内置客户端路由,提供声明式组件和 JavaScript API 两种方式。
|
|
278
|
+
|
|
279
|
+
**声明式组件:**
|
|
280
|
+
|
|
281
|
+
```html
|
|
282
|
+
<r-router>
|
|
283
|
+
<nav>
|
|
284
|
+
<r-link href="/">首页</r-link>
|
|
285
|
+
<r-link href="/about">关于</r-link>
|
|
286
|
+
</nav>
|
|
287
|
+
|
|
288
|
+
<r-route path="/" exact><h2>首页</h2></r-route>
|
|
289
|
+
<r-route path="/about"><h2>关于</h2></r-route>
|
|
290
|
+
<r-route path="/users/:id"><h2>用户详情</h2></r-route>
|
|
291
|
+
</r-router>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**JavaScript API,含导航守卫:**
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { createRouter } from 'ranui';
|
|
298
|
+
|
|
299
|
+
const router = createRouter({
|
|
300
|
+
mode: 'history',
|
|
301
|
+
routes: [
|
|
302
|
+
{ path: '/', exact: true, meta: { title: '首页' } },
|
|
303
|
+
{ path: '/users/:id', meta: { requiresAuth: true } },
|
|
304
|
+
],
|
|
305
|
+
viewTransition: 'spa', // 'spa' | 'mpa' | 'both'
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
router.beforeEach((to, from, next) => {
|
|
309
|
+
if (to.meta?.requiresAuth && !isLoggedIn()) next('/login');
|
|
310
|
+
else next();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
router.push('/users/42');
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
纯 MPA 站点(无需 JS router)可使用 `enableMpaViewTransitions()` 注入 `@view-transition { navigation: auto }`。共享元素过渡动画通过标准 `view-transition-name` CSS 属性实现。
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
import { enableMpaViewTransitions } from 'ranui';
|
|
320
|
+
enableMpaViewTransitions();
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
完整 API(守卫、`onPageSwap`/`onPageReveal`、元素级动画命名)请参考 [路由文档](https://chaxus.github.io/ran/cn/src/ranui/router/)。
|
|
324
|
+
|
|
325
|
+
### SSR & Builder (推荐)
|
|
326
|
+
|
|
327
|
+
对于需要服务端渲染 (SSR) 或更声明式构建 UI 的场景,RanUI 内部使用 `builder`、SSR registry 与 Declarative Shadow DOM。组件会通过 `ensureShadowRoot` 复用已有 Shadow Root,并通过 `ensureShadowElement` 保持初始化幂等。
|
|
328
|
+
|
|
329
|
+
源码内的 SSR 渲染示例:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
import { Button } from '@/components/button';
|
|
333
|
+
import { renderToString } from '@/utils/ssr';
|
|
334
|
+
|
|
335
|
+
const button = new Button();
|
|
336
|
+
button.setAttribute('effect', 'true');
|
|
337
|
+
|
|
338
|
+
// 输出包含 Declarative Shadow DOM 的 HTML 字符串
|
|
339
|
+
const html = renderToString(button);
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
更多细节请查看 [Utility Documentation](./utils/README.md)。
|
|
343
|
+
|
|
344
|
+
## 组件开发约定
|
|
345
|
+
|
|
346
|
+
新增或维护组件时请遵循当前包内约定:
|
|
347
|
+
|
|
348
|
+
- 组件继承 `RanElement`,不要直接继承浏览器环境下的 `HTMLElement`。
|
|
349
|
+
- 使用 `ensureShadowRoot` 创建或复用 Shadow Root,不要直接调用 `attachShadow`。
|
|
350
|
+
- 使用 `ensureShadowElement` 构建 Shadow DOM 子树,保证重复构造时幂等。
|
|
351
|
+
- `observedAttributes` 包含 `sheet`,并通过 `syncSheetAttribute` 同步组件级样式覆盖。
|
|
352
|
+
- `attributeChangedCallback` 首行使用 `if (old === next) return;` 避免重复同步。
|
|
353
|
+
- 使用 `defineSSR('r-name', Component)` 注册组件,而不是直接调用 `customElements.define`。
|
|
354
|
+
- 在 `index.ts` 中同时添加类型导出和副作用导入;在 `vite.config.ts`、`package.json` 中补齐独立入口与导出。
|
|
355
|
+
- 在 `connectedCallback` 中使用 `@/utils/builder` 导出的 `EventManager` 管理生命周期事件;在 `disconnectedCallback` 中调用 `manager.abort()` 一次清理所有监听器,不要逐个调用 `removeEventListener`。
|
|
356
|
+
|
|
357
|
+
## 贡献
|
|
358
|
+
|
|
359
|
+
我们欢迎学习者和开发者的贡献!这是一个实验性项目,请对开发过程保持耐心。
|
|
360
|
+
|
|
182
361
|
## 贡献者
|
|
183
362
|
|
|
184
363
|
<a href="https://github.com/chaxus/ran/graphs/contributors">
|