complete UI CSS ownership remediation

This commit is contained in:
2026-08-09 21:42:30 +05:30
parent 937ccb9e8e
commit 8f19a5eb2b
61 changed files with 1320 additions and 1577 deletions
+24 -21
View File
@@ -1,14 +1,15 @@
# Framework remediation plan # Framework remediation plan
**Status: remediation behavior completed; CSS ownership cleanup remains.** The **Status: completed.** The
repository has 102 maintained UI components, zero repository has 102 maintained UI components, zero
undeclared non-native output emitters, zero components matching the scaffold undeclared non-native output emitters, zero components matching the scaffold
definition, and local styles on every component. The core reactive runtime is definition, and local styles on every component. The core reactive runtime is
48,124 minified bytes; component controllers ship separately at 24,027 bytes. 48,124 minified bytes; component controllers ship separately at 24,027 bytes.
Section 4.1 remains open: the global `ui.css` still contains legacy component The §4.1 ownership migration is complete: `ui.css` contains global and
rules duplicated by local component styles. Those rules must be removed before cross-cutting primitives only. Shared component foundations are carried by
the §0.1 ownership convention can be called complete. three internal style-only components and render on demand; the former global
component families and standalone `wire-next` generation class are absent.
Final validation passed the production, package, example, service, editor, Final validation passed the production, package, example, service, editor,
showcase-generation, typecheck, lint, formatting, public-API, visual-contract, showcase-generation, typecheck, lint, formatting, public-API, visual-contract,
@@ -16,7 +17,7 @@ security, and runtime-size gates. The only skipped test is the explicitly
environment-dependent live PostgreSQL/MySQL test. environment-dependent live PostgreSQL/MySQL test.
Written 2026-08-09, after the 0.8.6 release. Every number here was measured Written 2026-08-09, after the 0.8.6 release. Every number here was measured
against the tree at commit `7a2b5865`, not estimated. Where a cause is not yet against the completed tree, not estimated. Where a cause is not yet
proven the item says so and makes proving it step one — nothing in this document proven the item says so and makes proving it step one — nothing in this document
should be implemented on the strength of a guess. should be implemented on the strength of a guess.
@@ -440,11 +441,10 @@ AdvancedRangeSlider, Chart, Confetti, CopyMarkup, FileUpload, Toast,
ToastNotifications, TreeView. Do §3.1 first and this list drops to 19 without ToastNotifications, TreeView. Do §3.1 first and this list drops to 19 without
any extra work. Do not plan the two items separately. any extra work. Do not plan the two items separately.
Separately, 58 components carried the `wire-next` scaffold class in the Separately, 58 components carried the standalone `wire-next` scaffold class in
original audit; **45 retain it now**. This is a cosmetic naming artefact and was the original audit; **zero retain it now**. Component-specific BEM identifiers
not part of the scaffold completion criterion. That class is a such as `wire-next--alert` remain locally owned implementation details, but no
generation artefact rather than a design, and it is what ties them to `ui.css` component is bound to a global stylesheet through the generation artefact.
instead of their own styles (§4.1).
**Change.** Rebuild them under §0.1, or remove them. Note that several are **Change.** Rebuild them under §0.1, or remove them. Note that several are
primitives where a scaffold is nearly the right answer — `skeleton`, `spinner`, primitives where a scaffold is nearly the right answer — `skeleton`, `spinner`,
@@ -500,18 +500,20 @@ whichever behaviour was chosen.
## 4. Delivery and the dev loop ## 4. Delivery and the dev loop
### 4.1 Global `ui.css` migration — PARTIAL ### 4.1 Global `ui.css` migration — FIXED
**Original issue.** `ui.css` was **175,848 bytes, 26,350 gzipped**, served on **Original issue.** `ui.css` was **175,848 bytes, 26,350 gzipped**, served on
every page. Only **42 of 108** components had a local `style {}` block. every page. Only **42 of 108** components had a local `style {}` block.
**Result so far.** The maintained library now has **102 of 102** components **Result.** The maintained library now has **102 of 102** components with local
with local styles. Built `ui.css` is **54,016 bytes / 9,338 gzipped** after styles. Built `ui.css` is **15,927 bytes / 3,762 gzipped**. The `.wire-next`,
removing the unused `.wire-switch`, `.wire-alert`, and `.wire-card` families and `.wire-btn`, `.wire-dropdown`, `.wire-alert`, `.wire-card`, `.wire-footer`,
localizing the live `.wire-btn`, `.wire-dropdown`, ButtonGroup, alert-variant, `.wire-metric-card`, and `.wire-segmented-group` global families are gone.
card-overlay, and related rules. The remaining `.wire-next` field and component Reusable field, select, color, and size foundations live in three internal
rules still form a parallel legacy layer. Local-style coverage alone does not style-only components; the renderer emits and deduplicates them only when a
complete this item; global component selectors must reach zero. component that needs them renders. An example-app SSR guard verifies that
on-demand path, while the UI suite prevents the global families or standalone
`wire-next` class from returning.
**Change.** Done in groups with the visual contract regenerated after each **Change.** Done in groups with the visual contract regenerated after each
migration. migration.
@@ -520,8 +522,9 @@ migration.
```bash ```bash
ls packages/ui/components/*.wrn | wc -l # 102 ls packages/ui/components/*.wrn | wc -l # 102
grep -lE '^\s*style\s*\{' packages/ui/components/*.wrn | wc -l # 102 grep -lE '^\s*style\s*\{' packages/ui/components/*.wrn | wc -l # 102
bun run build && gzip -c examples/basic-app/dist/ui.css | wc -c # 9338 bun run build
bun -e "const b=await Bun.file('examples/basic-app/dist/ui.css').arrayBuffer(); console.log(Bun.gzipSync(new Uint8Array(b)).length)" # 3762
``` ```
Add a ratchet test asserting the gzipped size only ever decreases, so this Add a ratchet test asserting the gzipped size only ever decreases, so this
@@ -849,7 +852,7 @@ history. Every row is complete.
| 3 | §2.6 server-rendered i18n | Contained work; the core SSR claim currently fails on text. | | 3 | §2.6 server-rendered i18n | Contained work; the core SSR claim currently fails on text. |
| 4 | §4.2 + §4.3 dev loop | Cheap, and it compounds across every task below. | | 4 | §4.2 + §4.3 dev loop | Cheap, and it compounds across every task below. |
| 5 | §4.6 de-duplicate generated client modules | 490 kB decoded parsed per page, 90% of it duplicated. Codegen-only, no API impact. | | 5 | §4.6 de-duplicate generated client modules | 490 kB decoded parsed per page, 90% of it duplicated. Codegen-only, no API impact. |
| 6 | §4.1 finish the CSS migration | 66 components, mechanical, takes ~26 kB off every page. | | 6 | §4.1 finish the CSS migration | Localized component ownership and reduced the always-served sheet by ~160 kB. |
| 7 | §4.6 split controllers out of the core runtime | 6.6 kB gzipped a typical page never executes. | | 7 | §4.6 split controllers out of the core runtime | 6.6 kB gzipped a typical page never executes. |
| 8 | §3.1 Group A: supersede the 5 duplicate scaffolds | 15 of the 22 dead outputs, and 5 fewer components to maintain. Needs a migration entry, not new code. | | 8 | §3.1 Group A: supersede the 5 duplicate scaffolds | 15 of the 22 dead outputs, and 5 fewer components to maintain. Needs a migration entry, not new code. |
| 9 | §3.2 scaffolds that need only their styles | Larger and cheaper than it looks; folds into item 6. | | 9 | §3.2 scaffolds that need only their styles | Larger and cheaper than it looks; folds into item 6. |
+51 -48
View File
@@ -2,56 +2,56 @@
"schemaVersion": 1, "schemaVersion": 1,
"releaseLine": "0.8", "releaseLine": "0.8",
"artifacts": { "artifacts": {
"packages/ui/components/Accordion.wrn": "98e6897d7dee2045fbc66e23d8a0b5051114d43f1c75234a2a6c5165132eb706", "packages/ui/components/Accordion.wrn": "285df0aa50733618afb1b82aa125a67030d3af4bd8b9de52bc2e6366cea871de",
"packages/ui/components/AdvancedSelect.wrn": "f8c7ff1c1727ce0e3e132536cdaefe71779936e9303694f763e78699f3ec827a", "packages/ui/components/AdvancedSelect.wrn": "e8666638204f9eb044ee6f163d6af432b119bf7f39d9df20243cfae471d20bcd",
"packages/ui/components/AnnouncementBar.wrn": "903bfd9421a7350c923ace3315cedc5763fe3e6b7b54b660a0f26281d46a8754", "packages/ui/components/AnnouncementBar.wrn": "903bfd9421a7350c923ace3315cedc5763fe3e6b7b54b660a0f26281d46a8754",
"packages/ui/components/AuthForm.wrn": "a0f34c233c5d52e2dd11429dacab98e7cd77bec8ae60652aff48f7dab8506893", "packages/ui/components/AuthForm.wrn": "ec8796a67320afa2fb2070edba194382d1ab0a00cbd574f936a502da819d00fa",
"packages/ui/components/AuthSplitLayout.wrn": "42736850c94f2df0bdc5c733530731fb5df0abd1aedfc6962f8f5774c18f3428", "packages/ui/components/AuthSplitLayout.wrn": "675c6ac9b07e0e9c5d6572f30028dda2391f9c6599b45945eb626df056a5b987",
"packages/ui/components/AvatarGroup.wrn": "d73df8249787c7296c2d19f06186a43fcfaf8b7983107b792c42acf18a388494", "packages/ui/components/AvatarGroup.wrn": "85dd6c64f8064d0980bf4157963bf366410c2ba391fd65df86a54b1a336f43a4",
"packages/ui/components/BackToTop.wrn": "b12c56ec8b7aabad68cbdc47f4b3237e2c21a61398f0aa51f9add6229b7357cd", "packages/ui/components/BackToTop.wrn": "b12c56ec8b7aabad68cbdc47f4b3237e2c21a61398f0aa51f9add6229b7357cd",
"packages/ui/components/Blockquote.wrn": "3e0845286c631151847d19980631c2636d37bc4bd2e95c6b045a9f51fe01fdc5", "packages/ui/components/Blockquote.wrn": "6f91cee9538c11858aaeb98ef1a098a814a6e319e3777e59cecd2de078b3beaf",
"packages/ui/components/Breadcrumb.wrn": "5ab85d273ce72e4d5b87907975bb33f0249a34f2f87f01dc0812e827f775eed9", "packages/ui/components/Breadcrumb.wrn": "5ab85d273ce72e4d5b87907975bb33f0249a34f2f87f01dc0812e827f775eed9",
"packages/ui/components/ButtonGroup.wrn": "fd1db987cedcf956e0d279a620eedbf2dcc88b7b7eb25f029d556fd5e363b273", "packages/ui/components/ButtonGroup.wrn": "0bb23d927e03926cc303498bc4ac1ab098343782082ac1fceeb2402702945842",
"packages/ui/components/CTASection.wrn": "2d2241d4f1018cfd2f8ea1ddd5fa8c8fcb8d1bbb2d4e4498cb9f39ff47a3801c", "packages/ui/components/CTASection.wrn": "2d2241d4f1018cfd2f8ea1ddd5fa8c8fcb8d1bbb2d4e4498cb9f39ff47a3801c",
"packages/ui/components/Card.wrn": "c7e48fba8a0062a46b54dd4ce0ea4fbae8b3a05c416db30992db46d82819882a", "packages/ui/components/Card.wrn": "ba1b577ae4ffa9bd93c1e6a650ce90745df3f78cd6ff9ee85530e32dab56860a",
"packages/ui/components/Carousel.wrn": "549fc881e453cc8f74de69cd4716042a4306ead33de99fd19c3e7abe5d79e1f9", "packages/ui/components/Carousel.wrn": "0d39e17a623fb623022f48e79ea1ab9f268e2eb359c491609c137f711a1a3ffc",
"packages/ui/components/Chart.wrn": "d2cedd0e02818d0908708c2d07a495c629fad13fa021a2e035d5023dc5c367de", "packages/ui/components/Chart.wrn": "d2cedd0e02818d0908708c2d07a495c629fad13fa021a2e035d5023dc5c367de",
"packages/ui/components/ChatBubble.wrn": "7b323b35005aaba91b39e6cfaf819f49932428173223c4809374f14cf8e1d3fc", "packages/ui/components/ChatBubble.wrn": "c46b9aef38f54e6243663046fe1b18ba6f9223cbe9b479d9335f96f217d401cc",
"packages/ui/components/Clipboard.wrn": "03fcfb9786008fd7fb0a7719c3279711253afc5bbb9b0262b805dd553c44da8a", "packages/ui/components/Clipboard.wrn": "03fcfb9786008fd7fb0a7719c3279711253afc5bbb9b0262b805dd553c44da8a",
"packages/ui/components/Collapse.wrn": "9a4e6c9cec95342479eeef4df842dc283f8a3fd93eb6ad871b5992fb98f11656", "packages/ui/components/Collapse.wrn": "8869ff4a39541b7e0d538e2b63b9a3dcdcfcd4055fa362519c7e73f164f1be9d",
"packages/ui/components/ColorPicker.wrn": "af0f962555b581df75a6dffb3056d394708c29d93ad18afa86e31707b505a807", "packages/ui/components/ColorPicker.wrn": "02f5744c3eb984de4b24de26fbfd3f69021633d4b0b33aceeba84df32f6a7284",
"packages/ui/components/Columns.wrn": "e01496aa53025444cc51671efcce40d6d24d86775b2cbb1ee0def1862bb6315d", "packages/ui/components/Columns.wrn": "e01496aa53025444cc51671efcce40d6d24d86775b2cbb1ee0def1862bb6315d",
"packages/ui/components/Combobox.wrn": "b6ae8cf667ed98f3fc4dc141bde797daf7e3daacf7df6c9a7c25a3a143432f62", "packages/ui/components/Combobox.wrn": "140b038735a5902eb901386281a5a01e16fd1c345560a48ff2b143ebaebe1f3d",
"packages/ui/components/Confetti.wrn": "b9aece64735c721c24356122e52ca84602d9f646c7d8be1e0b80d9de3163a501", "packages/ui/components/Confetti.wrn": "b9aece64735c721c24356122e52ca84602d9f646c7d8be1e0b80d9de3163a501",
"packages/ui/components/Container.wrn": "4a0acbb896f4c11714e3925d912b10ef9aeedfa904b4ad8561aa2cbd9a0fdca0", "packages/ui/components/Container.wrn": "4a0acbb896f4c11714e3925d912b10ef9aeedfa904b4ad8561aa2cbd9a0fdca0",
"packages/ui/components/ContextMenu.wrn": "33fb0f60268b12411d66e3031dc0557cb231149e7a8d83340b4b453a6e37cb09", "packages/ui/components/ContextMenu.wrn": "33fb0f60268b12411d66e3031dc0557cb231149e7a8d83340b4b453a6e37cb09",
"packages/ui/components/CustomScrollbar.wrn": "fedb2ab06cc2cf9499c130590945ca6971a3edfb60407b6658ef7756e4be9823", "packages/ui/components/CustomScrollbar.wrn": "fedb2ab06cc2cf9499c130590945ca6971a3edfb60407b6658ef7756e4be9823",
"packages/ui/components/DataMap.wrn": "7d0384d89126d947a6c2d640c93ab047aecca7931569d11b54d82dbb6639ff0e", "packages/ui/components/DataMap.wrn": "6058f92be7fb95e9eedcc714cad1ad43fa70e40c9dd50fd8bba5aae9935953a7",
"packages/ui/components/DataTable.wrn": "dc4453df72181311658b1227fabc4f147e0097ecbfe122ff695142642819534e", "packages/ui/components/DataTable.wrn": "edb935b091750ee3d1aff354007eabd4998fc7e5584d50a6c726c6657765d800",
"packages/ui/components/DatePicker.wrn": "a9edc2faf30a0e998e964e9290ee6bd9512787aa5dc19f360bcce029796d7e1c", "packages/ui/components/DatePicker.wrn": "2c46f92612528cf60aa0b7e8aaae9826094acb5518d3982c2db65aca6d01e103",
"packages/ui/components/DeviceFrame.wrn": "805aeed1abd321649c31dcf5c9e4e3465e16303a837645924437c75985befc26", "packages/ui/components/DeviceFrame.wrn": "d918d213e6e0b74d690911ab3b3f440e9b62fa019fd9e8140c5a56c5c0a0843d",
"packages/ui/components/Divider.wrn": "f5cc449611022b3d3eedf982ae48f6b1a289db7855cc9b6ea0248dcda807ffb3", "packages/ui/components/Divider.wrn": "f5cc449611022b3d3eedf982ae48f6b1a289db7855cc9b6ea0248dcda807ffb3",
"packages/ui/components/DragAndDrop.wrn": "d97a7598dd659ecf72b8649af0b23902dc14d80c19df53dc63ecc5c7b69b8952", "packages/ui/components/DragAndDrop.wrn": "31ffe9d41fc655a38257c841a520c947d3ce2338e174b5e89e32fe9ccf23b9b3",
"packages/ui/components/Drawer.wrn": "e6e2778e515361a3475439ab91b6efec3895efab2a4dfdcfb535713f656cc7ed", "packages/ui/components/Drawer.wrn": "e6e2778e515361a3475439ab91b6efec3895efab2a4dfdcfb535713f656cc7ed",
"packages/ui/components/Dropdown.wrn": "627319de0f555f0f07f80e19a59f9fd52e7f74af7cf2cf9ff09e114678ad9eb8", "packages/ui/components/Dropdown.wrn": "627319de0f555f0f07f80e19a59f9fd52e7f74af7cf2cf9ff09e114678ad9eb8",
"packages/ui/components/FeatureCard.wrn": "2a1bfb01cec8a9892d3ff066e48c276fa7c548cb59c15bcd9b4bbf85450a07a2", "packages/ui/components/FeatureCard.wrn": "2a1bfb01cec8a9892d3ff066e48c276fa7c548cb59c15bcd9b4bbf85450a07a2",
"packages/ui/components/FeatureGrid.wrn": "9e3c0440d08c6982861d3e732eeb20f1023413a58243844dfb9d7033bac13d2b", "packages/ui/components/FeatureGrid.wrn": "9bd3e9becb5d7359841ed21d607131c58da20f2e8e87e25a9fb48f6b1a2b17e5",
"packages/ui/components/FeatureIconCard.wrn": "620f66a36ca5c8ad8f2ea09e94dc4471e4146189c42149e9d31db58501281639", "packages/ui/components/FeatureIconCard.wrn": "620f66a36ca5c8ad8f2ea09e94dc4471e4146189c42149e9d31db58501281639",
"packages/ui/components/FileInput.wrn": "c235385a70eb7cb985083597570474c0cb70463c03cab651e452199b2c9abf25", "packages/ui/components/FileInput.wrn": "cc4b8f95f11d91ead9a5796e202b2264eeea24aa1c3b1718327a6ea1dd28d785",
"packages/ui/components/FileUploadProgress.wrn": "1abdb7a1a181abc6b9b62a0bd45066236f7e1d8fa5139e7fb478b08997f000d9", "packages/ui/components/FileUploadProgress.wrn": "c4b4174025a1de75c35a3497e0afd0b8acc645ad5f85c3ed7296ea295bb05749",
"packages/ui/components/Footer.wrn": "956a504682760c4e5f4d26091830c4fc0f8d0c12f7cdf7e69c94539685c67763", "packages/ui/components/Footer.wrn": "71790c99e4b15a62fdaf4b7a65d7016ca427408b62a2b1c7695ef1b39ba29440",
"packages/ui/components/Grid.wrn": "d089d8657298b4d33993ba4bf7b581f816c65db513d8e48cf1fdd18da19e05a8", "packages/ui/components/Grid.wrn": "d089d8657298b4d33993ba4bf7b581f816c65db513d8e48cf1fdd18da19e05a8",
"packages/ui/components/Hero.wrn": "e4e39dcd59c273192c7e4136f6583279e20b0956bcba4cfbc3f6e01a1fade152", "packages/ui/components/Hero.wrn": "e4e39dcd59c273192c7e4136f6583279e20b0956bcba4cfbc3f6e01a1fade152",
"packages/ui/components/HeroActions.wrn": "77f69085db73b946c00aa684d50fb5ae474684e440e81edbccfd8ef5dc357e33", "packages/ui/components/HeroActions.wrn": "77f69085db73b946c00aa684d50fb5ae474684e440e81edbccfd8ef5dc357e33",
"packages/ui/components/Image.wrn": "8113441bf90ab0d1d8618d477125ad339021f39e853db89d656c72c492e7a20b", "packages/ui/components/Image.wrn": "8113441bf90ab0d1d8618d477125ad339021f39e853db89d656c72c492e7a20b",
"packages/ui/components/InputGroup.wrn": "7347865ff9b90dde690cf0753adc753a91d2ae599e92405f47e5b35806f58e06", "packages/ui/components/InputGroup.wrn": "95333e7cb883c52958e3d6112899f337b00825e7678a2323baed1724983a7238",
"packages/ui/components/InputNumber.wrn": "0ec020084f3e556d438f8264d61f836cfab4951c52d4df60e4944fb734fb38a9", "packages/ui/components/InputNumber.wrn": "0ec020084f3e556d438f8264d61f836cfab4951c52d4df60e4944fb734fb38a9",
"packages/ui/components/Kbd.wrn": "5afaabbb15902bd8fb82cb4155cf6cbfff55600927b54d564d27e2dd8b752386", "packages/ui/components/Kbd.wrn": "5afaabbb15902bd8fb82cb4155cf6cbfff55600927b54d564d27e2dd8b752386",
"packages/ui/components/LayoutSplitter.wrn": "6bd6812e364420ed5d3439862116c58d0b011b298679005795bdccdbb7455066", "packages/ui/components/LayoutSplitter.wrn": "6bd6812e364420ed5d3439862116c58d0b011b298679005795bdccdbb7455066",
"packages/ui/components/LegendIndicator.wrn": "f5a59fce000206dd9285c063b6b8eea414d044f161524e6e1c77e8c817ede261", "packages/ui/components/LegendIndicator.wrn": "11f13413242e106daf347638ee2b2313fd20b05fc7001f03c0f4c00a558dfec0",
"packages/ui/components/Link.wrn": "b986abcc66bc0b1c6296f3ff4e560812e35d89cc680d5251d7a3883e68c1d053", "packages/ui/components/Link.wrn": "b986abcc66bc0b1c6296f3ff4e560812e35d89cc680d5251d7a3883e68c1d053",
"packages/ui/components/List.wrn": "9bfa9613f388e5b126fc27d505e8165d7aa4a058e72132b755585464e116490e", "packages/ui/components/List.wrn": "9bfa9613f388e5b126fc27d505e8165d7aa4a058e72132b755585464e116490e",
"packages/ui/components/ListGroup.wrn": "4fcf9302f409b3d6c5d4a3519abac87f71647a9315d89b1f9d8abfa3b9b8f632", "packages/ui/components/ListGroup.wrn": "885020eea4dbdfc90b5a659e48a339cba7a205ddb539e30d7f1aa57244d728a5",
"packages/ui/components/Map.wrn": "9023a7a188480b24222a3910d26f6926918470369a1ab610f841c31a6120b9aa", "packages/ui/components/Map.wrn": "9023a7a188480b24222a3910d26f6926918470369a1ab610f841c31a6120b9aa",
"packages/ui/components/MarketingSectionHeader.wrn": "6f89a3b298ea299db686ddf903f92c4fe5d321719213943b16be82ba36c23302", "packages/ui/components/MarketingSectionHeader.wrn": "6f89a3b298ea299db686ddf903f92c4fe5d321719213943b16be82ba36c23302",
"packages/ui/components/Marquee.wrn": "2a88c8bd8378469add26b0d937adccac4aa9f2c2005393010298e8e4fa89693e", "packages/ui/components/Marquee.wrn": "2a88c8bd8378469add26b0d937adccac4aa9f2c2005393010298e8e4fa89693e",
@@ -60,16 +60,16 @@
"packages/ui/components/MetricGrid.wrn": "6018a98c10628ed240ed236ed916c0ff60994d192c3aadafd10bc876be0f9364", "packages/ui/components/MetricGrid.wrn": "6018a98c10628ed240ed236ed916c0ff60994d192c3aadafd10bc876be0f9364",
"packages/ui/components/Modal.wrn": "59d4ae9d6dd2700692d9edecfe53ee868e9f864363a4885961d1813cb2bee51f", "packages/ui/components/Modal.wrn": "59d4ae9d6dd2700692d9edecfe53ee868e9f864363a4885961d1813cb2bee51f",
"packages/ui/components/Nav.wrn": "7dc456b879e9248bde267b986cdcb138b8f3127d98b51e27bc9708129953f5e2", "packages/ui/components/Nav.wrn": "7dc456b879e9248bde267b986cdcb138b8f3127d98b51e27bc9708129953f5e2",
"packages/ui/components/Navbar.wrn": "7989d25819312014c0a0855370b80b925d53ff735d682fdb61e9677a9dacb7f3", "packages/ui/components/Navbar.wrn": "70640f9ff6e615c70ea9a5e996058bad45ebf0e97b02c9d827228a8f475f72e2",
"packages/ui/components/PageHeader.wrn": "59264dfb26a132fd3eb265c4374f9fcae781f30dd94a087c57e6e8256717cf14", "packages/ui/components/PageHeader.wrn": "59264dfb26a132fd3eb265c4374f9fcae781f30dd94a087c57e6e8256717cf14",
"packages/ui/components/Pagination.wrn": "9ceb74745b159150b015bbbb1974c68e14a7d4b92bd03491cb23b8855aa07983", "packages/ui/components/Pagination.wrn": "9ceb74745b159150b015bbbb1974c68e14a7d4b92bd03491cb23b8855aa07983",
"packages/ui/components/PinInput.wrn": "6909e196bb1060aeec4877594a180da3ff1dad694671756fcdb0f58f548a63d3", "packages/ui/components/PinInput.wrn": "2b656cf832c02fa9c28e2fe78bf1147ff05b17a9d8e744c33e1a63bad7c7ebb3",
"packages/ui/components/Popover.wrn": "f2996f00fad3e35fba2d48e70af17f592854eb73b9e8c6a83526aa78c4e41293", "packages/ui/components/Popover.wrn": "f2996f00fad3e35fba2d48e70af17f592854eb73b9e8c6a83526aa78c4e41293",
"packages/ui/components/PortalDashboard.wrn": "0ca5228b0793572270d9cbac1eb5e5e5a77a3886a3a4a7ce7126d5e7b467fd4e", "packages/ui/components/PortalDashboard.wrn": "79b54868f150a551cede0731cc5390a45ce22375f1929f2d537ce67a987bde50",
"packages/ui/components/PreferenceSwitcher.wrn": "61d01f507b3f6b4bab55d2642beb9fee5edf0662997b44443142b43c828ea239", "packages/ui/components/PreferenceSwitcher.wrn": "241060fb24a16e795c53981506d273fe9110cbb250c73c85928c788c75d05ded",
"packages/ui/components/PublicPageShell.wrn": "369b74f65e27227311a2ed73e9ca56c8b60908100d6840a798e8ba5f2c8b1040", "packages/ui/components/PublicPageShell.wrn": "369b74f65e27227311a2ed73e9ca56c8b60908100d6840a798e8ba5f2c8b1040",
"packages/ui/components/RangeSlider.wrn": "90ef35ceb94899dfd6e07ef3f3a88f2fb49c8dfe4596def67fe39fd9911d73c7", "packages/ui/components/RangeSlider.wrn": "eff9db1616d424f9c803185b4efe011942866acad3328179438a200210d97710",
"packages/ui/components/Rating.wrn": "1dc480b9bea35af8e3703f858f4d169ed3a16e7689827e4451ec5d8f2e6029ca", "packages/ui/components/Rating.wrn": "41bbce2eb7fb6ad08a3aacde97d03af6e346824962148e7655743028c59185d3",
"packages/ui/components/Scrollspy.wrn": "76d1c17580c8d460f1222ce97585bdcac9779141f2f30d5460c84ff9f75413d8", "packages/ui/components/Scrollspy.wrn": "76d1c17580c8d460f1222ce97585bdcac9779141f2f30d5460c84ff9f75413d8",
"packages/ui/components/SearchBox.wrn": "717f7647fabd762f8ed60a7c3306eac3ddb83c0da292a53c76d91886c2b8f94f", "packages/ui/components/SearchBox.wrn": "717f7647fabd762f8ed60a7c3306eac3ddb83c0da292a53c76d91886c2b8f94f",
"packages/ui/components/Section.wrn": "33eb8a82a2102229c2b1f64cec497e9e8e70b67adab8dbe396be860fccbb699c", "packages/ui/components/Section.wrn": "33eb8a82a2102229c2b1f64cec497e9e8e70b67adab8dbe396be860fccbb699c",
@@ -78,32 +78,35 @@
"packages/ui/components/SplitHero.wrn": "70e843565ff869bcf413b11b6d9830b2e2bd229863a00904596f71e2dff0e76d", "packages/ui/components/SplitHero.wrn": "70e843565ff869bcf413b11b6d9830b2e2bd229863a00904596f71e2dff0e76d",
"packages/ui/components/StatsBar.wrn": "c7d1df3895f25b6d3a2e1012a18b97592c7f33e65418b880d486f20c2d490686", "packages/ui/components/StatsBar.wrn": "c7d1df3895f25b6d3a2e1012a18b97592c7f33e65418b880d486f20c2d490686",
"packages/ui/components/Stepper.wrn": "df9093e4222ef3250963a8c6fbfec74477242735d23eabe896c29731c54fc14d", "packages/ui/components/Stepper.wrn": "df9093e4222ef3250963a8c6fbfec74477242735d23eabe896c29731c54fc14d",
"packages/ui/components/StrongPassword.wrn": "9559756cacd10d613aea7a4ab9845a011f7f2f4613f82f83c87ab5fd2bad2ea9", "packages/ui/components/StrongPassword.wrn": "0df060bf01ace77ad0158df489588df75fd5d426dc04f000abacc2b3ddd93fc5",
"packages/ui/components/StyledIcon.wrn": "e4f79f188dab1d6a3f515c13f15b7e35e2f1c1bd9130e4affc7f2fc09d456c5a", "packages/ui/components/StyledIcon.wrn": "bf9b41e9c402c4886feddda504c3f12348bbbe49b9003de69d807db4bf52d1b1",
"packages/ui/components/Tabs.wrn": "e38336d28876caa4e97da32a569749e3803b030284a85ce9f35fa788e51cc5b0", "packages/ui/components/Tabs.wrn": "e38336d28876caa4e97da32a569749e3803b030284a85ce9f35fa788e51cc5b0",
"packages/ui/components/TextLink.wrn": "6e18906337cd3d30c2a0122593d249bde8f7cedc697b90ae388e2bd0e8eff3e1", "packages/ui/components/TextLink.wrn": "6e18906337cd3d30c2a0122593d249bde8f7cedc697b90ae388e2bd0e8eff3e1",
"packages/ui/components/TimePicker.wrn": "b3f24767448cb04ddd56e06540b52e5cc32ea21ed4acd2a5eb4f7e46f8617aba", "packages/ui/components/TimePicker.wrn": "d2fb7cd156bb2abb997a9e85d27c36b80f4ba81515b523a125e793363568475c",
"packages/ui/components/Timeline.wrn": "686a37ebc62732842846d004491336970db94945bca942f226097d863e159d1c", "packages/ui/components/Timeline.wrn": "686a37ebc62732842846d004491336970db94945bca942f226097d863e159d1c",
"packages/ui/components/Toaster.wrn": "288b2b370f65bf823e8d2a6f8c8cf66972332b8174303d33ebe8546d5e1d52c4", "packages/ui/components/Toaster.wrn": "288b2b370f65bf823e8d2a6f8c8cf66972332b8174303d33ebe8546d5e1d52c4",
"packages/ui/components/ToggleCount.wrn": "c4365eb10cc9993d8b6b212268341904dda27718360113826a925a617e279479", "packages/ui/components/ToggleCount.wrn": "285abf0ed5c7156c5a6f404af0c2519f181b591975515e845cbc3c21349c5cfd",
"packages/ui/components/TogglePassword.wrn": "c644436c3435b8a9b39c6b7a9f75671b76ca74f273952ce5c032ca169b0470e5", "packages/ui/components/TogglePassword.wrn": "94cd3579bb48e53283c82400bff58ea0f2ca92646b87385502e3b474ba787a96",
"packages/ui/components/TreeView.wrn": "c354fce9c5e81fed6eec4089dfd2a7f6bb84eb189c94efc88e00251efdc12c0b", "packages/ui/components/TreeView.wrn": "c354fce9c5e81fed6eec4089dfd2a7f6bb84eb189c94efc88e00251efdc12c0b",
"packages/ui/components/Typography.wrn": "1b4d1b91dee0ff522d27c566becc3bc49bd9ec979ae6334e6a15fca5d88eccac", "packages/ui/components/Typography.wrn": "1b4d1b91dee0ff522d27c566becc3bc49bd9ec979ae6334e6a15fca5d88eccac",
"packages/ui/components/WysiwygEditor.wrn": "e4d341f7f49b53301d790bcebe7f3788fea4ea4b72ba8bf0a422e006ceeb54eb", "packages/ui/components/WysiwygEditor.wrn": "231da25f4064b065adb2783252d430aa336585e008547a010a41174a41284524",
"packages/ui/components/alert.wrn": "74ef969d629f68a83a9cb8a42bb4924411f048bfa9e7b451b9243bd451ac8323", "packages/ui/components/alert.wrn": "6dde2cee818a66e1d80df6b93e9f971d5389f95387961593350b0c297e3ed367",
"packages/ui/components/avatar.wrn": "3e2fc145949f6141246ad0b274403b9149ccc28b6a8e80285b396345097c818a", "packages/ui/components/avatar.wrn": "ac2239cc746ec35b81a34ec6e27f4ee74ca04dc6058bad5853aa66640f2a9426",
"packages/ui/components/badge.wrn": "61fbc7a9cab484287025b214e181dab7662280a766e64c1af80248ab7dbea121", "packages/ui/components/badge.wrn": "9e1e884e9e2e9d881348c5121f53f4c85b91355cbfa49eeb2bff43f5ab38fab5",
"packages/ui/components/button.wrn": "a42df46d19c8375ceed059f593b555f77414042bbaf777afcf16d276e133db15", "packages/ui/components/button.wrn": "da1660c671daf2feffe1024203ac1a8bc947beb07691bef595c1552c664b9cde",
"packages/ui/components/checkbox.wrn": "e59a56e6acfe3756da8c2b827fee938fa4158250ae587a62936b3df0b988f28c", "packages/ui/components/checkbox.wrn": "a5fb8410558696b3c8e63d5cd8dd49b44d51ed35b809cb2b189501e2e7b323e2",
"packages/ui/components/input.wrn": "b75df32f470f16e4ee858368264a60bac582b3525eca1c0910250ee3a0a56b76", "packages/ui/components/input.wrn": "184066a837982ef3fe44e518730d0cccb7dd6772d5af58116c0774b71cb894e2",
"packages/ui/components/progress.wrn": "61b01ba575d35691e3dfebfcb03ccd1c6b79f1df1c0d41857ec3b01944081a5d", "packages/ui/components/progress.wrn": "61b01ba575d35691e3dfebfcb03ccd1c6b79f1df1c0d41857ec3b01944081a5d",
"packages/ui/components/radio.wrn": "d0f45da28075e5dc6283f62296687f96ed06eeadcc59db892ee516d0f420ce2b", "packages/ui/components/radio.wrn": "cc65395aad6c0bac690c18cb7c36b1d2784e4c3284be3d3cb277b745f2f29bbc",
"packages/ui/components/select.wrn": "826035c3049eca85f0a86e2a974ce23f5b6d2f855bce660f2219b9855332c974", "packages/ui/components/select.wrn": "d58b454958edccce99f96c642eb67d5a0e64e2b97e58e742d445fe45164a7660",
"packages/ui/components/skeleton.wrn": "dda95a2c59c6060ab2b9f916eb046fbe74a68b398a051afc0bde41808e059c3a", "packages/ui/components/skeleton.wrn": "dda95a2c59c6060ab2b9f916eb046fbe74a68b398a051afc0bde41808e059c3a",
"packages/ui/components/spinner.wrn": "ffbd28ec6fc017fc94c67778f8966eab2eefa3b47f3f27f064c77dbeb65d84c1", "packages/ui/components/spinner.wrn": "ffbd28ec6fc017fc94c67778f8966eab2eefa3b47f3f27f064c77dbeb65d84c1",
"packages/ui/components/switch.wrn": "205591decf50f1f7d190d8da6a3d969e0fd17ec5300c968fb005608da6a6e596", "packages/ui/components/switch.wrn": "917219d77b2089584bc444fe94f2cb0670f800d73f69ae70f0368bfece16be0c",
"packages/ui/components/textarea.wrn": "00b48d4dbbd5aedfe51e8e932d124b3e222383360c738ac3b15fbbf478481ff7", "packages/ui/components/textarea.wrn": "602ef2c8a081476f790408119b96ec2b51fb18abe76a43ee2b955dcb60debd17",
"packages/ui/components/tooltip.wrn": "40028dfbb17f6725c76c2e091af9c30ea4db6aa4dea919d38f53874ea70315d1", "packages/ui/components/tooltip.wrn": "40028dfbb17f6725c76c2e091af9c30ea4db6aa4dea919d38f53874ea70315d1",
"packages/ui/ui.css": "b37e9434e74478cc4d7f3ccb7bee8e2f629827edc09dadbe29e6c299d6913a36" "packages/ui/styles/ComponentBaseStyles.wrn": "566724a7bcc8f7caa067d5415177bf982ebae7b579a34052ce6f2092cbade4d7",
"packages/ui/styles/FieldStyles.wrn": "460fc8cbb0228151927fd6a61caceae0a148fdbc3009c13ba511ab0a4010b42d",
"packages/ui/styles/SelectStyles.wrn": "2c45e9c04678aaff76632e908b3c061410be0d6999a54a9203a39a3817d698f2",
"packages/ui/ui.css": "b7e2ae796ffbb738451364f63fce13022a81d81207f7e49c7f6220d24c36acd9"
} }
} }
+11
View File
@@ -98,6 +98,17 @@ describe("full app", () => {
expect(html).not.toContain("Card title"); expect(html).not.toContain("Card title");
}); });
test("shared UI foundations render on demand instead of shipping in ui.css", async () => {
const fieldResponse = await app.fetch("/modal");
expect(fieldResponse.status).toBe(200);
const fieldHtml = await fieldResponse.text();
expect(fieldHtml).toContain('data-wrnexus-style-owner="FieldStyles"');
expect(fieldHtml).toContain('data-wrnexus-style-owner="ComponentBaseStyles"');
const selectResponse = await app.fetch("/test");
expect(selectResponse.status).toBe(200);
expect(await selectResponse.text()).toContain('data-wrnexus-style-owner="SelectStyles"');
});
test("platform showcase uses the public layout and styled reactive primitives", async () => { test("platform showcase uses the public layout and styled reactive primitives", async () => {
const res = await app.fetch("/platform-showcase"); const res = await app.fetch("/platform-showcase");
expect(res.status).toBe(200); expect(res.status).toBe(200);
+4 -7
View File
@@ -1,3 +1,7 @@
import Input from "@wrnexus/ui/components/input.wrn"
import Modal from "@wrnexus/ui/components/Modal.wrn"
import Toaster from "@wrnexus/ui/components/Toaster.wrn"
// Modal demo page. Route: /modal // Modal demo page. Route: /modal
// //
// Five real-world use cases for the shared @wrnexus/ui Modal component — each // Five real-world use cases for the shared @wrnexus/ui Modal component — each
@@ -9,13 +13,6 @@
// this page. Any client expression can raise one with the runtime global — // this page. Any client expression can raise one with the runtime global —
// toast("..."), toast.success(...), toast.error(...) — so pages never // toast("..."), toast.success(...), toast.error(...) — so pages never
// hand-roll a status banner and never reach for a blocking alert(). // hand-roll a status banner and never reach for a blocking alert().
//
// No import here on purpose: component tags resolve by directory scan, and
// @wrnexus/ui's index.ts intentionally has no runtime exports (registry.ts
// holds the server-only filesystem helpers instead) — an explicit
// `import { Modal } from "@wrnexus/ui"` fails to bundle since there's no
// matching export. The WRN-IMPORT-IMPLICIT warning this triggers is safe to
// ignore for component tags.
page ModalPage { page ModalPage {
state noteText = "Remember to ship the release notes" state noteText = "Remember to ship the release notes"
+3
View File
@@ -1,3 +1,6 @@
import AdvancedSelect from "@wrnexus/ui/components/AdvancedSelect.wrn"
import ComboBox from "@wrnexus/ui/components/Combobox.wrn"
// Component event test page. Route: /test // Component event test page. Route: /test
// //
// Events bubble from the exact ComboBox or AdvancedSelect that emitted them. // Events bubble from the exact ComboBox or AdvancedSelect that emitted them.
+6 -1
View File
@@ -148,7 +148,12 @@ export async function runBuild(appRoot: string): Promise<void> {
const pluginContributions = await pluginRunner.contributions(); const pluginContributions = await pluginRunner.contributions();
const { generateApplicationTypes } = await import("./types.ts"); const { generateApplicationTypes } = await import("./types.ts");
generateApplicationTypes(root, pluginContributions); generateApplicationTypes(root, pluginContributions);
const componentDirs = [uiComponentsDir(), ...pluginContributions.componentDirs]; const bundledUiDir = uiComponentsDir();
const componentDirs = [
bundledUiDir,
join(bundledUiDir, "..", "styles"),
...pluginContributions.componentDirs,
];
await pluginRunner.hook("buildStart"); await pluginRunner.hook("buildStart");
const virtualModules = new Map<string, string>(); const virtualModules = new Map<string, string>();
for (const [index, module] of pluginContributions.virtualModules.entries()) { for (const [index, module] of pluginContributions.virtualModules.entries()) {
+9 -2
View File
@@ -11,7 +11,10 @@ export type InspectTarget =
export function inspectComponent(appRoot: string, requestedName: string): unknown { export function inspectComponent(appRoot: string, requestedName: string): unknown {
const root = resolve(appRoot); const root = resolve(appRoot);
const router = buildRouter(join(root, "app"), { componentDirs: [uiComponentsDir()] }); const bundledUiDir = uiComponentsDir();
const router = buildRouter(join(root, "app"), {
componentDirs: [bundledUiDir, join(bundledUiDir, "..", "styles")],
});
const component = router.components.find( const component = router.components.find(
(candidate) => candidate.name.toLowerCase() === requestedName.toLowerCase(), (candidate) => candidate.name.toLowerCase() === requestedName.toLowerCase(),
); );
@@ -139,7 +142,11 @@ export async function inspectProject(appRoot: string, target: InspectTarget): Pr
database: database ?? "default", database: database ?? "default",
})); }));
const router = buildRouter(join(root, "app"), { const router = buildRouter(join(root, "app"), {
componentDirs: [uiComponentsDir(), ...contributions.componentDirs], componentDirs: [
uiComponentsDir(),
join(uiComponentsDir(), "..", "styles"),
...contributions.componentDirs,
],
externalRoutes: contributions.routes, externalRoutes: contributions.routes,
middlewareFiles: contributions.middleware, middlewareFiles: contributions.middleware,
}); });
+6 -1
View File
@@ -309,7 +309,12 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
console.log(`[wrnexus:plugin] contributed routes: ${pluginContributions.routes.length}`); console.log(`[wrnexus:plugin] contributed routes: ${pluginContributions.routes.length}`);
const pluginToolbarPanels = await pluginRunner.devToolbarPanels(); const pluginToolbarPanels = await pluginRunner.devToolbarPanels();
const componentDirs = [uiComponentsDir(), ...pluginContributions.componentDirs]; const bundledUiDir = uiComponentsDir();
const componentDirs = [
bundledUiDir,
join(bundledUiDir, "..", "styles"),
...pluginContributions.componentDirs,
];
const hmr = opts.hmr ?? mode === "development"; const hmr = opts.hmr ?? mode === "development";
const port = opts.port ?? 3000; const port = opts.port ?? 3000;
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Accordion { component Accordion {
outputs { outputs {
change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -103,7 +105,7 @@ component Accordion {
data-variant="{variant}" data-variant="{variant}"
data-indicator="{indicator}" data-indicator="{indicator}"
data-multiple="{allowsMultiple() ? 'true' : 'false'}" data-multiple="{allowsMultiple() ? 'true' : 'false'}"
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--accordion wire-next--accordion-{variant} {bordered ? 'wire-next--accordion-bordered' : ''} {separated ? 'wire-next--accordion-separated' : ''} {flush ? 'wire-next--accordion-flush' : ''} {disabled ? 'wire-next--disabled' : ''} {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--accordion wire-next--accordion-{variant} {bordered ? 'wire-next--accordion-bordered' : ''} {separated ? 'wire-next--accordion-separated' : ''} {flush ? 'wire-next--accordion-flush' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
> >
{#each items as item, index} {#each items as item, index}
<section <section
@@ -204,6 +206,7 @@ component Accordion {
</section> </section>
{/each} {/each}
</div> </div>
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import SelectStyles from "../styles/SelectStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component AdvancedSelect { component AdvancedSelect {
outputs { outputs {
search(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) search(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -253,7 +257,7 @@ component AdvancedSelect {
view { view {
<div <div
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--advanced-select {open ? 'wire-next--open' : ''} {fixed ? 'wire-next--advanced-select-fixed' : ''} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--advanced-select {open ? 'wire-next--open' : ''} {fixed ? 'wire-next--advanced-select-fixed' : ''} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
data-placement="{placement}" data-placement="{placement}"
data-search-mode="{searchMode}" data-search-mode="{searchMode}"
data-remote="{remote ? 'true' : 'false'}" data-remote="{remote ? 'true' : 'false'}"
@@ -455,6 +459,8 @@ component AdvancedSelect {
data-error="{name}" data-error="{name}"
>{validationMessage}</small> >{validationMessage}</small>
</div> </div>
<SelectStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -49,7 +49,7 @@ component AuthForm {
} }
view { view {
<section class="wire-auth-form wire-next--color-{color} wire-next--size-{size} {class}"> <section class="wire-auth-form wire-component--color-{color} wire-component--size-{size} {class}">
<header> <header>
<span class="wire-auth-form__icon icon-[lucide--fingerprint]" aria-hidden="true"></span> <span class="wire-auth-form__icon icon-[lucide--fingerprint]" aria-hidden="true"></span>
<div><h2>{title}</h2>{#if description}<p>{description}</p>{/if}</div> <div><h2>{title}</h2>{#if description}<p>{description}</p>{/if}</div>
@@ -88,6 +88,7 @@ component AuthForm {
</form> </form>
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
@@ -147,3 +148,5 @@ component AuthForm {
} }
} }
} }
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
import Button from "./button.wrn"
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component AuthSplitLayout { component AuthSplitLayout {
props { props {
size: string = "default" size: string = "default"
@@ -12,7 +14,7 @@ component AuthSplitLayout {
view { view {
<main <main
class="wire-auth-split wire-next--color-{color} wire-next--size-{size} {class}" class="wire-auth-split wire-component--color-{color} wire-component--size-{size} {class}"
> >
<section <section
class="wire-auth-split__content" class="wire-auth-split__content"
@@ -79,6 +81,7 @@ component AuthSplitLayout {
</div> </div>
</section> </section>
</main> </main>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component AvatarGroup { component AvatarGroup {
outputs { outputs {
overflow(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]) overflow(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
@@ -43,7 +45,7 @@ component AvatarGroup {
view { view {
<div <div
{...attrs} {...attrs}
class="wire-next wire-next--avatar-group {class}" class="wire-component wire-next--avatar-group {class}"
data-wrn-avatar-group data-wrn-avatar-group
data-layout="{layout}" data-layout="{layout}"
data-shape="{shape}" data-shape="{shape}"
@@ -146,6 +148,7 @@ component AvatarGroup {
{/if} {/if}
</div> </div>
</div> </div>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Blockquote { component Blockquote {
props { props {
quote: string = "I just wanted to say that I'm very happy with my purchase so far. The documentation is outstanding - clear and detailed." quote: string = "I just wanted to say that I'm very happy with my purchase so far. The documentation is outstanding - clear and detailed."
@@ -18,7 +20,7 @@ component Blockquote {
view { view {
<figure <figure
{...attrs} {...attrs}
class="wire-next wire-next--blockquote wire-next--color-{color} wire-next--size-{size} {class}" class="wire-component wire-next--blockquote wire-component--color-{color} wire-component--size-{size} {class}"
data-size="{size}" data-size="{size}"
data-color="{color}" data-color="{color}"
data-align="{align}" data-align="{align}"
@@ -63,6 +65,7 @@ component Blockquote {
</figcaption> </figcaption>
{/if} {/if}
</figure> </figure>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ButtonGroup { component ButtonGroup {
outputs { outputs {
click(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) click(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -54,7 +56,7 @@ items: unknown[] = []
view { view {
<div <div
{...attrs} {...attrs}
class="wire-next wire-next--button-group {class}" class="wire-component wire-next--button-group {class}"
role="{toolbar ? 'toolbar' : 'group'}" role="{toolbar ? 'toolbar' : 'group'}"
aria-label="{ariaLabel}" aria-label="{ariaLabel}"
aria-orientation="{orientation}" aria-orientation="{orientation}"
@@ -88,6 +90,7 @@ items: unknown[] = []
{/if} {/if}
<slot /> <slot />
</div> </div>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Card { component Card {
outputs { outputs {
click(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) click(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -80,7 +82,7 @@ component Card {
data-layout='{layout}' data-layout='{layout}'
data-hover='{hover}' data-hover='{hover}'
data-align='{align}' data-align='{align}'
class='wire-next wire-next--card wire-next--color-{color} wire-next--size-{size} wire-next--variant-{variant} {class}' class='wire-component wire-next--card wire-component--color-{color} wire-component--size-{size} wire-next--variant-{variant} {class}'
> >
{#if items.length > 0} {#if items.length > 0}
<div class="wire-next__card-group"> <div class="wire-next__card-group">
@@ -308,6 +310,7 @@ component Card {
</article> </article>
{/if} {/if}
</div> </div>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
// Interactive, hydration-safe content carousel. // Interactive, hydration-safe content carousel.
component Carousel { component Carousel {
outputs { outputs {
@@ -316,7 +318,7 @@ component Carousel {
view { view {
<section <section
{...attrs} {...attrs}
class="wire-next wire-next--carousel wire-next--color-{color} wire-next--size-{size} wire-next--variant-{variant} {class}" class="wire-component wire-next--carousel wire-component--color-{color} wire-component--size-{size} wire-next--variant-{variant} {class}"
data-rtl="{isRTL}" data-rtl="{isRTL}"
data-centered="{isCentered}" data-centered="{isCentered}"
data-draggable="{isDraggable && !isSnap}" data-draggable="{isDraggable && !isSnap}"
@@ -460,6 +462,7 @@ component Carousel {
</div> </div>
</div> </div>
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+6 -3
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ChatBubble { component ChatBubble {
outputs { outputs {
action(payload: { action: boolean; item: string | number | boolean | null | object; index: number }) action(payload: { action: boolean; item: string | number | boolean | null | object; index: number })
@@ -64,7 +66,7 @@ component ChatBubble {
view { view {
<section <section
{...attrs} {...attrs}
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--chat-bubble wire-next--variant-{variant} {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--chat-bubble wire-next--variant-{variant} {class}"
data-one-sided="{oneSided}" data-one-sided="{oneSided}"
role="log" role="log"
aria-label="{ariaLabel}" aria-label="{ariaLabel}"
@@ -146,14 +148,15 @@ component ChatBubble {
{/if} {/if}
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
.wire-next--chat-bubble.wire-next--size-sm { .wire-next--chat-bubble.wire-component--size-sm {
--wire-chat-padding: 0.75rem; --wire-chat-padding: 0.75rem;
font-size: 0.875rem; font-size: 0.875rem;
} }
.wire-next--chat-bubble.wire-next--size-lg { .wire-next--chat-bubble.wire-component--size-lg {
--wire-chat-padding: 1.25rem; --wire-chat-padding: 1.25rem;
font-size: 1.0625rem; font-size: 1.0625rem;
} }
+6 -3
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Collapse { component Collapse {
outputs { outputs {
toggle(payload: { index: number; item: string | number | boolean | null | object; open: boolean; openIndexes: number[] }) toggle(payload: { index: number; item: string | number | boolean | null | object; open: boolean; openIndexes: number[] })
@@ -51,7 +53,7 @@ size: string = "default"
view { view {
<section <section
{...attrs} {...attrs}
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--collapse {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--collapse {class}"
data-mode="{mode}" data-mode="{mode}"
aria-label="{ariaLabel}" aria-label="{ariaLabel}"
> >
@@ -94,13 +96,14 @@ size: string = "default"
{/each} {/each}
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
.wire-next--collapse.wire-next--size-sm { .wire-next--collapse.wire-component--size-sm {
font-size: 0.875rem; font-size: 0.875rem;
} }
.wire-next--collapse.wire-next--size-lg { .wire-next--collapse.wire-component--size-lg {
font-size: 1.0625rem; font-size: 1.0625rem;
} }
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ColorPicker { component ColorPicker {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -32,11 +36,13 @@ component ColorPicker {
client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() } client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--color-picker {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-component--color-picker {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div> <div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__color-control" data-icon-position="{iconPosition}">{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<input id="{id || name}" type="color" name="{name}" value="{value}" disabled="{disabled}" required="{required}" readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="emitField('input', event)" @change="emitField('change', event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" /><output>{value}</output></div> <div class="wire-next__color-control" data-icon-position="{iconPosition}">{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<input id="{id || name}" type="color" name="{name}" value="{value}" disabled="{disabled}" required="{required}" readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="emitField('input', event)" @change="emitField('change', event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" /><output>{value}</output></div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small> {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import SelectStyles from "../styles/SelectStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ComboBox { component ComboBox {
outputs { outputs {
search(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) search(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -225,7 +229,7 @@ component ComboBox {
view { view {
<div <div
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--advanced-select wire-next--combobox {open ? 'wire-next--open' : ''} {fixed ? 'wire-next--advanced-select-fixed' : ''} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--advanced-select wire-next--combobox {open ? 'wire-next--open' : ''} {fixed ? 'wire-next--advanced-select-fixed' : ''} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
data-placement="{placement}" data-placement="{placement}"
data-search-mode="{searchMode}" data-search-mode="{searchMode}"
data-remote="{remote ? 'true' : 'false'}" data-remote="{remote ? 'true' : 'false'}"
@@ -385,6 +389,8 @@ component ComboBox {
data-error="{name}" data-error="{name}"
>{validationMessage}</small> >{validationMessage}</small>
</div> </div>
<SelectStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component DataMap { component DataMap {
outputs { outputs {
select(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) select(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -14,12 +16,13 @@ component DataMap {
class: string = "" class: string = ""
} }
view { view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--data-map wire-next--variant-{variant} {class}"> <section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--data-map wire-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if} {#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if} {#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if} {#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+3
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
// //
// DataTable -- columns, sorting, filtering, pagination and selection over an // DataTable -- columns, sorting, filtering, pagination and selection over an
// array of records. // array of records.
@@ -1043,6 +1045,7 @@ component DataTable {
<slot></slot> <slot></slot>
</div> </div>
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component DatePicker { component DatePicker {
outputs { outputs {
input(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]) input(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
@@ -52,12 +56,14 @@ size: string = "default"
client function handleInvalid(sourceEvent) { output.invalid({ name: name, message: sourceEvent.currentTarget.validationMessage, sourceEvent: sourceEvent }) } client function handleInvalid(sourceEvent) { output.invalid({ name: name, message: sourceEvent.currentTarget.validationMessage, sourceEvent: sourceEvent }) }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--date-picker {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}" data-expanded="{expanded}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--date-picker {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}" data-expanded="{expanded}">
<div class="wire-next__field-heading"><label for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div> <div class="wire-next__field-heading"><label for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__picker-control"><input id="{id || name}" class="wire-next__sr-only wire-next__picker-value" type="text" name="{name}" value="{currentValue}" required="{required}" readonly aria-hidden="true" tabindex="-1" @invalid="handleInvalid(event)" /><button type="button" class="wire-next__date-trigger" disabled="{disabled || readonly}" aria-haspopup="dialog" aria-expanded="{expanded}" @click="openCalendar(event)" @focus="handleFocus(event)" @blur="handleBlur(event)"><span>{currentValue || placeholder || "Select date"}</span><i class="icon-[lucide--calendar-days]" aria-hidden="true"></i></button>{#if currentValue && !readonly && !disabled}<button type="button" class="wire-next__picker-clear" aria-label="Clear date" @click="clearDate(event)">×</button>{/if}</div> <div class="wire-next__picker-control"><input id="{id || name}" class="wire-next__sr-only wire-next__picker-value" type="text" name="{name}" value="{currentValue}" required="{required}" readonly aria-hidden="true" tabindex="-1" @invalid="handleInvalid(event)" /><button type="button" class="wire-next__date-trigger" disabled="{disabled || readonly}" aria-haspopup="dialog" aria-expanded="{expanded}" @click="openCalendar(event)" @focus="handleFocus(event)" @blur="handleBlur(event)"><span>{currentValue || placeholder || "Select date"}</span><i class="icon-[lucide--calendar-days]" aria-hidden="true"></i></button>{#if currentValue && !readonly && !disabled}<button type="button" class="wire-next__picker-clear" aria-label="Clear date" @click="clearDate(event)">×</button>{/if}</div>
<div class="wire-next__calendar" role="dialog" aria-label="{label}"><div class="wire-next__date-selectors"><label>Month<select value="{selectedMonth}" @change="updateDate('month', event.currentTarget.value, event)">{#each months as month}<option value="{month.value}" selected="{month.value === selectedMonth}">{month.label}</option>{/each}</select></label><label>Year<select value="{selectedYear}" @change="updateDate('year', event.currentTarget.value, event)">{#each years as year}<option value="{year}" selected="{String(year) === selectedYear}">{year}</option>{/each}</select></label></div><div class="wire-next__calendar-grid">{#each days as day}<button type="button" data-value="{String(day).padStart(2, '0')}" aria-pressed="{String(day).padStart(2, '0') === selectedDay}" @click="selectDay(event)">{day}</button>{/each}</div><button type="button" class="wire-next__date-done" disabled="{!currentValue}" @click="finishDate(event)">Done</button></div> <div class="wire-next__calendar" role="dialog" aria-label="{label}"><div class="wire-next__date-selectors"><label>Month<select value="{selectedMonth}" @change="updateDate('month', event.currentTarget.value, event)">{#each months as month}<option value="{month.value}" selected="{month.value === selectedMonth}">{month.label}</option>{/each}</select></label><label>Year<select value="{selectedYear}" @change="updateDate('year', event.currentTarget.value, event)">{#each years as year}<option value="{year}" selected="{String(year) === selectedYear}">{year}</option>{/each}</select></label></div><div class="wire-next__calendar-grid">{#each days as day}<button type="button" data-value="{String(day).padStart(2, '0')}" aria-pressed="{String(day).padStart(2, '0') === selectedDay}" @click="selectDay(event)">{day}</button>{/each}</div><button type="button" class="wire-next__date-done" disabled="{!currentValue}" @click="finishDate(event)">Done</button></div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small> {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component DeviceFrame { component DeviceFrame {
outputs { outputs {
change(payload: { device: string; orientation: string; sourceEvent: Event }) change(payload: { device: string; orientation: string; sourceEvent: Event })
@@ -26,10 +28,11 @@ component DeviceFrame {
client function rotateFrame(sourceEvent) { currentOrientation = currentOrientation === "portrait" ? "landscape" : "portrait"; output.rotate({ device: device, orientation: currentOrientation, sourceEvent: sourceEvent }); output.change({ device: device, orientation: currentOrientation, sourceEvent: sourceEvent }) } client function rotateFrame(sourceEvent) { currentOrientation = currentOrientation === "portrait" ? "landscape" : "portrait"; output.rotate({ device: device, orientation: currentOrientation, sourceEvent: sourceEvent }); output.change({ device: device, orientation: currentOrientation, sourceEvent: sourceEvent }) }
} }
view { view {
<section {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--device-frame wire-next--variant-{variant} {class}" data-device="{device}" data-orientation="{currentOrientation}"> <section {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--device-frame wire-next--variant-{variant} {class}" data-device="{device}" data-orientation="{currentOrientation}">
<header><div>{#if title}<strong>{title}</strong>{/if}{#if description}<p>{description}</p>{/if}</div>{#if showToolbar}<div class="wire-next__device-actions" role="toolbar" aria-label="Preview device">{#each items as item}<button type="button" aria-pressed="{item.value === device}" @click="setDevice(item.value, event)">{item.label}</button>{/each}<button type="button" aria-label="Rotate preview" @click="rotateFrame(event)">↻</button></div>{/if}</header> <header><div>{#if title}<strong>{title}</strong>{/if}{#if description}<p>{description}</p>{/if}</div>{#if showToolbar}<div class="wire-next__device-actions" role="toolbar" aria-label="Preview device">{#each items as item}<button type="button" aria-pressed="{item.value === device}" @click="setDevice(item.value, event)">{item.label}</button>{/each}<button type="button" aria-label="Rotate preview" @click="rotateFrame(event)">↻</button></div>{/if}</header>
<div class="wire-next__device-shell">{#if src || srcdoc}<iframe title="{frameTitle}" src="{src}" srcdoc="{srcdoc}" allow="{allow}"></iframe>{:else}<div class="wire-next__device-content"><slot /></div>{/if}</div> <div class="wire-next__device-shell">{#if src || srcdoc}<iframe title="{frameTitle}" src="{src}" srcdoc="{srcdoc}" allow="{allow}"></iframe>{:else}<div class="wire-next__device-content"><slot /></div>{/if}</div>
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component DragAndDrop { component DragAndDrop {
outputs { outputs {
dragStart(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) dragStart(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -18,12 +20,13 @@ component DragAndDrop {
class: string = "" class: string = ""
} }
view { view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--drag-and-drop wire-next--variant-{variant} {class}"> <section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--drag-and-drop wire-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if} {#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if} {#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if} {#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -33,7 +33,7 @@ component FeatureGrid {
data-align='{align}' data-align='{align}'
data-max-width='{maxWidth}' data-max-width='{maxWidth}'
data-variant='{variant}' data-variant='{variant}'
class='wire-feature-grid wire-next--color-{color} wire-next--size-{size} {class}' class='wire-feature-grid wire-component--color-{color} wire-component--size-{size} {class}'
aria-label='{label}' aria-label='{label}'
style='--wire-feature-grid-columns: {columns}; --wire-feature-grid-tablet-columns: {tabletColumns}; --wire-feature-grid-mobile-columns: {mobileColumns}; --wire-feature-grid-min-item-width: {minItemWidth || "17rem"};' style='--wire-feature-grid-columns: {columns}; --wire-feature-grid-tablet-columns: {tabletColumns}; --wire-feature-grid-mobile-columns: {mobileColumns}; --wire-feature-grid-min-item-width: {minItemWidth || "17rem"};'
> >
@@ -77,6 +77,7 @@ component FeatureGrid {
</div> </div>
</div> </div>
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
@@ -232,3 +233,5 @@ component FeatureGrid {
} }
} }
} }
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
import FeatureCard from "./FeatureCard.wrn"
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component FileInput { component FileInput {
outputs { outputs {
input(payload: { files: File[]; name: string; sourceEvent: Event }) input(payload: { files: File[]; name: string; sourceEvent: Event })
@@ -46,11 +50,13 @@ size: string = "default"
client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() } client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--file-input {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--file-input {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div> <div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__file-control" data-icon-position="{iconPosition}">{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<span>{selectedName || value || placeholder}</span><input id="{id || name}" type="file" name="{name}" accept="{accept}" multiple="{multiple}" disabled="{disabled}" required="{required}" aria-readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="handleChange(event)" @change="handleChange(event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" @invalid="emitField('invalid', event)" /></div> <div class="wire-next__file-control" data-icon-position="{iconPosition}">{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<span>{selectedName || value || placeholder}</span><input id="{id || name}" type="file" name="{name}" accept="{accept}" multiple="{multiple}" disabled="{disabled}" required="{required}" aria-readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="handleChange(event)" @change="handleChange(event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" @invalid="emitField('invalid', event)" /></div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small> {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component FileUploadProgress { component FileUploadProgress {
outputs { outputs {
cancel(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]) cancel(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
@@ -30,12 +34,14 @@ size: string = "default"
client function completeUpload(sourceEvent) { currentValue = max; currentStatus = "complete"; output.complete(detail(sourceEvent)) } client function completeUpload(sourceEvent) { currentValue = max; currentStatus = "complete"; output.complete(detail(sourceEvent)) }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--file-upload-progress {class}" data-status="{currentStatus}" aria-live="polite"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--file-upload-progress {class}" data-status="{currentStatus}" aria-live="polite">
<div class="wire-next__row"><span>{#if fileName}<strong>{fileName}</strong><small>{#if uploadedSize || fileSize}{uploadedSize}{#if uploadedSize && fileSize} of {/if}{fileSize}{:else}{label}{/if}</small>{:else}<strong>{label}</strong>{/if}</span>{#if showValue}<strong>{percent()}%</strong>{/if}</div> <div class="wire-next__row"><span>{#if fileName}<strong>{fileName}</strong><small>{#if uploadedSize || fileSize}{uploadedSize}{#if uploadedSize && fileSize} of {/if}{fileSize}{:else}{label}{/if}</small>{:else}<strong>{label}</strong>{/if}</span>{#if showValue}<strong>{percent()}%</strong>{/if}</div>
<progress value="{currentValue}" max="{max}" aria-label="{label}"></progress> <progress value="{currentValue}" max="{max}" aria-label="{label}"></progress>
<div class="wire-next__upload-status"><span class="wire-next__upload-status-icon" aria-hidden="true"></span><span>{#if currentStatus === "uploading"}Uploading securely…{:else if currentStatus === "complete"}Upload complete{:else if currentStatus === "error"}Upload failed{:else}Upload cancelled{/if}</span></div> <div class="wire-next__upload-status"><span class="wire-next__upload-status-icon" aria-hidden="true"></span><span>{#if currentStatus === "uploading"}Uploading securely…{:else if currentStatus === "complete"}Upload complete{:else if currentStatus === "error"}Upload failed{:else}Upload cancelled{/if}</span></div>
<div class="wire-next__upload-actions">{#if currentStatus === "uploading"}<button type="button" @click="cancelUpload(event)">{cancelLabel}</button><button type="button" @click="completeUpload(event)">Complete</button>{/if}{#if currentStatus === "error" || currentStatus === "cancelled"}<button type="button" @click="retryUpload(event)">{retryLabel}</button>{/if}</div> <div class="wire-next__upload-actions">{#if currentStatus === "uploading"}<button type="button" @click="cancelUpload(event)">{cancelLabel}</button><button type="button" @click="completeUpload(event)">Complete</button>{/if}{#if currentStatus === "error" || currentStatus === "cancelled"}<button type="button" @click="retryUpload(event)">{retryLabel}</button>{/if}</div>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+40
View File
@@ -287,6 +287,46 @@ component Footer {
} }
style { style {
.wire-footer--columns-1 {
--wire-footer-columns: 1;
}
.wire-footer--columns-2 {
--wire-footer-columns: 2;
}
.wire-footer--columns-3 {
--wire-footer-columns: 3;
}
.wire-footer--columns-4 {
--wire-footer-columns: 4;
}
.wire-footer--columns-5 {
--wire-footer-columns: 5;
}
.wire-footer--columns-6 {
--wire-footer-columns: 6;
}
@media (max-width: 900px) {
.wire-footer--columns-1 .wire-footer__links {
grid-template-columns: 1fr;
}
.wire-footer--columns-2 .wire-footer__links {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 600px) {
.wire-footer--columns-1 .wire-footer__links {
grid-template-columns: 1fr;
}
}
/* --- Badge ---------------------------------------------------------------- */ /* --- Badge ---------------------------------------------------------------- */
.wire-badge { .wire-badge {
display: inline-flex; display: inline-flex;
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component InputGroup { component InputGroup {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -39,7 +43,7 @@ component InputGroup {
client function handleAction(sourceEvent) { output.action({ name: name, sourceEvent: sourceEvent }) } client function handleAction(sourceEvent) { output.action({ name: name, sourceEvent: sourceEvent }) }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--input-group {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--input-group {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div> <div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__input-group-control"> <div class="wire-next__input-group-control">
{#if startText}<span class="wire-next__input-addon">{startText}</span>{/if} {#if startText}<span class="wire-next__input-addon">{startText}</span>{/if}
@@ -50,6 +54,8 @@ component InputGroup {
</div> </div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small> {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component LegendIndicator { component LegendIndicator {
outputs { outputs {
toggle(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) toggle(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -14,12 +16,13 @@ component LegendIndicator {
class: string = "" class: string = ""
} }
view { view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--legend-indicator wire-next--variant-{variant} {class}"> <section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--legend-indicator wire-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if} {#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if} {#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if} {#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ListGroup { component ListGroup {
outputs { outputs {
select(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) select(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -14,12 +16,13 @@ component ListGroup {
class: string = "" class: string = ""
} }
view { view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--list-group wire-next--variant-{variant} {class}"> <section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--list-group wire-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if} {#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if} {#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if} {#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Navbar { component Navbar {
outputs { outputs {
toggle(payload: { open: boolean }) toggle(payload: { open: boolean })
@@ -60,7 +62,7 @@ size: string = "default"
} }
view { view {
<header class="wire-navbar wire-navbar--width-{maxWidth} wire-next--color-{color} wire-next--size-{size} {sticky ? 'wire-navbar--sticky' : ''} {class}" data-wrn-navbar data-open-on-hover="{openOnHover}"> <header class="wire-navbar wire-navbar--width-{maxWidth} wire-component--color-{color} wire-component--size-{size} {sticky ? 'wire-navbar--sticky' : ''} {class}" data-wrn-navbar data-open-on-hover="{openOnHover}">
<div class="wire-navbar__topbar" aria-label="{topbarLabel}"> <div class="wire-navbar__topbar" aria-label="{topbarLabel}">
<slot name="topbar" /> <slot name="topbar" />
</div> </div>
@@ -139,6 +141,7 @@ size: string = "default"
</div> </div>
</div> </div>
</header> </header>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component PinInput { component PinInput {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -49,7 +51,7 @@ component PinInput {
view { view {
<fieldset <fieldset
{...attrs} {...attrs}
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--pin-input {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--pin-input {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
data-wrn-pin-input data-wrn-pin-input
data-length="{length}" data-length="{length}"
data-pattern="{pattern}" data-pattern="{pattern}"
@@ -113,6 +115,7 @@ component PinInput {
data-error="{name}" data-error="{name}"
>{validationMessage}</small> >{validationMessage}</small>
</fieldset> </fieldset>
<ComponentBaseStyles hidden />
} }
style { style {
+16 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component PortalDashboard { component PortalDashboard {
outputs { outputs {
action(payload: { item: string | number | boolean | null | object; value: string | number | boolean; index: number }) action(payload: { item: string | number | boolean | null | object; value: string | number | boolean; index: number })
@@ -32,7 +34,7 @@ component PortalDashboard {
} }
view { view {
<main class="wire-portal-dashboard wire-next--color-{color} wire-next--size-{size} {class}"> <main class="wire-portal-dashboard wire-component--color-{color} wire-component--size-{size} {class}">
<header class="wire-portal-dashboard__hero"> <header class="wire-portal-dashboard__hero">
<div> <div>
{#if eyebrowKey}<span class="wire-portal-dashboard__eyebrow" data-t="{eyebrowKey}">{eyebrow}</span>{/if} {#if eyebrowKey}<span class="wire-portal-dashboard__eyebrow" data-t="{eyebrowKey}">{eyebrow}</span>{/if}
@@ -97,9 +99,22 @@ component PortalDashboard {
</section> </section>
</div> </div>
</main> </main>
<ComponentBaseStyles hidden />
} }
style { style {
.wire-portal-dashboard__status--urgent {
background: var(--wire-color-danger);
}
.wire-portal-dashboard__status--warning {
background: var(--wire-color-warning);
}
.wire-portal-dashboard__status--success {
background: var(--wire-color-success);
}
/* Reusable responsive portal dashboard */ /* Reusable responsive portal dashboard */
.wire-portal-dashboard { .wire-portal-dashboard {
width: min(100%, 96rem); width: min(100%, 96rem);
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component PreferenceSwitcher { component PreferenceSwitcher {
outputs { outputs {
theme(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]) theme(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
@@ -35,7 +37,7 @@ size: string = "default"
} }
view { view {
<div class="wire-preferences wire-next--color-{color} wire-next--size-{size} {compact ? 'wire-preferences--compact' : ''} {class}" data-wrn-preferences aria-label="Display and language preferences"> <div class="wire-preferences wire-component--color-{color} wire-component--size-{size} {compact ? 'wire-preferences--compact' : ''} {class}" data-wrn-preferences aria-label="Display and language preferences">
<details class="wire-preferences__menu" name="wire-preference-menu"> <details class="wire-preferences__menu" name="wire-preference-menu">
<summary aria-label="{themeLabel}" title="{themeLabel}"> <summary aria-label="{themeLabel}" title="{themeLabel}">
<span class="icon-[lucide--sun-moon]" aria-hidden="true"></span> <span class="icon-[lucide--sun-moon]" aria-hidden="true"></span>
@@ -81,6 +83,7 @@ size: string = "default"
</div> </div>
</details> </details>
</div> </div>
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component RangeSlider { component RangeSlider {
outputs { outputs {
input(payload: ({ value: number; name: string; sourceEvent: Event }) | ({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])) input(payload: ({ value: number; name: string; sourceEvent: Event }) | ({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]))
@@ -49,7 +53,7 @@ component RangeSlider {
client function increaseValue(sourceEvent) { currentValue = Math.min(Number(max), Number(currentValue) + Number(step)); output.input({ value: currentValue, name: name, sourceEvent: sourceEvent }); output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) } client function increaseValue(sourceEvent) { currentValue = Math.min(Number(max), Number(currentValue) + Number(step)); output.input({ value: currentValue, name: name, sourceEvent: sourceEvent }); output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--range-slider {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--range-slider {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div> <div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__range-control" data-variant="{variant}" data-icon-position="{iconPosition}" data-show-steps="{showSteps}"> <div class="wire-next__range-control" data-variant="{variant}" data-icon-position="{iconPosition}" data-show-steps="{showSteps}">
{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if} {#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}
@@ -67,6 +71,8 @@ component RangeSlider {
</div> </div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small> {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Rating { component Rating {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -14,12 +16,13 @@ component Rating {
class: string = "" class: string = ""
} }
view { view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--rating wire-next--variant-{variant} {class}"> <section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--rating wire-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if} {#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if} {#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if} {#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component StrongPassword { component StrongPassword {
outputs { outputs {
input(payload: { value: string; score: number; maximumScore: number; percent: number; level: string }) input(payload: { value: string; score: number; maximumScore: number; percent: number; level: string })
@@ -117,7 +121,7 @@ component StrongPassword {
view { view {
<div <div
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--strong-password wire-next--strong-password-{presentation} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--strong-password wire-next--strong-password-{presentation} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
data-wrn-strong-password data-wrn-strong-password
data-strength="{password ? strengthLevel() : 'empty'}" data-strength="{password ? strengthLevel() : 'empty'}"
data-score="{password ? score() : 0}" data-score="{password ? score() : 0}"
@@ -214,6 +218,8 @@ component StrongPassword {
<small id="{name}-validation" class="wire-next__validation" data-error="{name}">{validationMessage}</small> <small id="{name}-validation" class="wire-next__validation" data-error="{name}">{validationMessage}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component StyledIcon { component StyledIcon {
props { props {
size: string = "default" size: string = "default"
@@ -9,12 +11,13 @@ component StyledIcon {
class: string = "" class: string = ""
} }
view { view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--styled-icon wire-next--variant-{variant} {class}"> <section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--styled-icon wire-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if} {#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if} {#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if} {#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component TimePicker { component TimePicker {
outputs { outputs {
input(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]) input(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
@@ -51,7 +55,7 @@ size: string = "default"
client function handleInvalid(sourceEvent) { output.invalid({ name: name, message: sourceEvent.currentTarget.validationMessage, sourceEvent: sourceEvent }) } client function handleInvalid(sourceEvent) { output.invalid({ name: name, message: sourceEvent.currentTarget.validationMessage, sourceEvent: sourceEvent }) }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--time-picker {class}" data-variant="{variant}" data-inline="{inline}" data-floating="{variant === 'floating'}" data-invalid="{error ? 'true' : 'false'}" data-expanded="{expanded}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--time-picker {class}" data-variant="{variant}" data-inline="{inline}" data-floating="{variant === 'floating'}" data-invalid="{error ? 'true' : 'false'}" data-expanded="{expanded}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div> <div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__field-control" data-icon-position="{iconPosition}"> <div class="wire-next__field-control" data-icon-position="{iconPosition}">
{#if icon}<span class="{icon} wire-next__field-icon" aria-hidden="true"></span>{/if} {#if icon}<span class="{icon} wire-next__field-icon" aria-hidden="true"></span>{/if}
@@ -62,6 +66,8 @@ size: string = "default"
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if} {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}
<small class="wire-next__field-error" data-error="{name}">{error}</small> <small class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ToggleCount { component ToggleCount {
outputs { outputs {
change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -222,7 +224,7 @@ component ToggleCount {
data-variant="{variant}" data-variant="{variant}"
data-value="{selectedValue}" data-value="{selectedValue}"
data-disabled="{disabled ? 'true' : 'false'}" data-disabled="{disabled ? 'true' : 'false'}"
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--toggle-count wire-next--toggle-count-{variant} {fullWidth ? 'wire-next--toggle-count-full' : ''} {disabled ? 'wire-next--disabled' : ''} {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--toggle-count wire-next--toggle-count-{variant} {fullWidth ? 'wire-next--toggle-count-full' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
> >
<input type="hidden" name="{name}" value="{selectedValue}" /> <input type="hidden" name="{name}" value="{selectedValue}" />
@@ -279,6 +281,7 @@ component ToggleCount {
</div> </div>
{/if} {/if}
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component TogglePassword { component TogglePassword {
outputs { outputs {
input(payload: { name?: string; value: string | number | boolean | null | object; visible: boolean }) input(payload: { name?: string; value: string | number | boolean | null | object; visible: boolean })
@@ -53,7 +57,7 @@ component TogglePassword {
view { view {
<fieldset <fieldset
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--toggle-password {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--toggle-password {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
data-wrn-toggle-password data-wrn-toggle-password
data-visible="{revealed ? 'true' : 'false'}" data-visible="{revealed ? 'true' : 'false'}"
> >
@@ -152,6 +156,8 @@ component TogglePassword {
data-error="{name}" data-error="{name}"
>{validationMessage}</small> >{validationMessage}</small>
</fieldset> </fieldset>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component WysiwygEditor { component WysiwygEditor {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -16,12 +18,13 @@ component WysiwygEditor {
class: string = "" class: string = ""
} }
view { view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--wysiwyg-editor wire-next--variant-{variant} {class}"> <section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--wysiwyg-editor wire-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if} {#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if} {#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if} {#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot /> <slot />
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Alert { component Alert {
outputs { outputs {
dismiss(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) dismiss(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -76,7 +78,7 @@ component Alert {
aria-hidden="{visible ? 'false' : 'true'}" aria-hidden="{visible ? 'false' : 'true'}"
role="{role}" role="{role}"
aria-live="{live}" aria-live="{live}"
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--alert wire-next--alert-{variant} {compact ? 'wire-next--alert-compact' : ''} {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--alert wire-next--alert-{variant} {compact ? 'wire-next--alert-compact' : ''} {class}"
> >
{#if showIcon} {#if showIcon}
<span class="wire-next__alert-icon" aria-hidden="true"> <span class="wire-next__alert-icon" aria-hidden="true">
@@ -142,6 +144,7 @@ component Alert {
</button> </button>
{/if} {/if}
</section> </section>
<ComponentBaseStyles hidden />
} }
style { style {
+4 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Avatar { component Avatar {
outputs { outputs {
load(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) load(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -32,7 +34,7 @@ src: string = ""
view { view {
<span <span
{...attrs} {...attrs}
class="wire-next wire-next--avatar {description ? 'wire-next--avatar-media' : ''} {class}" class="wire-component wire-next--avatar {description ? 'wire-next--avatar-media' : ''} {class}"
data-wrn-avatar data-wrn-avatar
> >
<span <span
@@ -86,6 +88,7 @@ src: string = ""
</span> </span>
{/if} {/if}
</span> </span>
<ComponentBaseStyles hidden />
} }
style { style {
+12 -1
View File
@@ -1,3 +1,5 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Badge { component Badge {
outputs { outputs {
dismiss(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) dismiss(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -48,7 +50,7 @@ component Badge {
view { view {
<span <span
{...attrs} {...attrs}
class="wire-next wire-next--badge-root {anchorLabel || anchorIcon ? 'wire-next--badge-anchored' : ''} {class}" class="wire-component wire-next--badge-root {anchorLabel || anchorIcon ? 'wire-next--badge-anchored' : ''} {class}"
data-wrn-badge data-wrn-badge
data-placement="{placement}" data-placement="{placement}"
data-show="visible" data-show="visible"
@@ -99,9 +101,18 @@ component Badge {
{/if} {/if}
</span> </span>
</span> </span>
<ComponentBaseStyles hidden />
} }
style { style {
@keyframes wire-badge-ping {
75%,
100% {
opacity: 0;
transform: scale(1.45);
}
}
/* --- Badge --------------------------------------------------------------- */ /* --- Badge --------------------------------------------------------------- */
.wire-next--badge-root { .wire-next--badge-root {
position: relative; position: relative;
+21
View File
@@ -166,6 +166,27 @@ label: string = "Button"
} }
style { style {
.wire-spinner {
display: inline-block;
width: 1.15rem;
height: 1.15rem;
border: 2px solid var(--wire-color-border);
border-top-color: var(--wire-color-primary);
border-radius: 999px;
animation: wire-spin 0.7s linear infinite;
}
.wire-spinner--inline {
width: 1rem;
height: 1rem;
}
@keyframes wire-spin {
to {
transform: rotate(360deg);
}
}
/* --- Button --------------------------------------------------------------- */ /* --- Button --------------------------------------------------------------- */
.wire-btn { .wire-btn {
position: relative; position: relative;
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Checkbox { component Checkbox {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -43,7 +47,7 @@ size: string = "default"
view { view {
<div <div
{...attrs} {...attrs}
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--choice-field wire-next--checkbox-field {class}" class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--choice-field wire-next--checkbox-field {class}"
data-inline="{inline}" data-inline="{inline}"
data-invalid="{error ? 'true' : 'false'}" data-invalid="{error ? 'true' : 'false'}"
data-orientation="{orientation}" data-orientation="{orientation}"
@@ -172,6 +176,8 @@ size: string = "default"
{error} {error}
</small> </small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Input { component Input {
outputs { outputs {
input(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]) input(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
@@ -52,7 +56,7 @@ size: string = "default"
client function handleKeyup(sourceEvent) { sourceEvent.stopPropagation(); output.keyup({ key: sourceEvent.key, value: sourceEvent.currentTarget.value, name: name, sourceEvent: sourceEvent }) } client function handleKeyup(sourceEvent) { sourceEvent.stopPropagation(); output.keyup({ key: sourceEvent.key, value: sourceEvent.currentTarget.value, name: name, sourceEvent: sourceEvent }) }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--input {class}" data-variant="{variant}" data-inline="{inline}" data-floating="{variant === 'floating'}" data-invalid="{error ? 'true' : 'false'}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--input {class}" data-variant="{variant}" data-inline="{inline}" data-floating="{variant === 'floating'}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"> <div class="wire-next__field-heading">
<label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label> <label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>
{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if} {#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}
@@ -91,6 +95,8 @@ size: string = "default"
{#if helperText}<small id="{(id || name) + '-help'}" class="wire-next__field-help">{helperText}</small>{/if} {#if helperText}<small id="{(id || name) + '-help'}" class="wire-next__field-help">{helperText}</small>{/if}
<small id="{(id || name) + '-error'}" class="wire-next__field-error" data-error="{name}">{error}</small> <small id="{(id || name) + '-error'}" class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Radio { component Radio {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -39,7 +43,7 @@ size: string = "default"
client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() } client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--choice-field wire-next--radio-field {class}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}" data-orientation="{orientation}" data-card="{card}" data-list="{list}" data-right-aligned="{rightAligned}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--choice-field wire-next--radio-field {class}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}" data-orientation="{orientation}" data-card="{card}" data-list="{list}" data-right-aligned="{rightAligned}">
<div class="wire-next__field-heading">{#if options.length || cornerHint}<span class="{hiddenLabel ? 'wire-next__sr-only' : ''}">{label}</span>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}{/if}</div> <div class="wire-next__field-heading">{#if options.length || cornerHint}<span class="{hiddenLabel ? 'wire-next__sr-only' : ''}">{label}</span>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}{/if}</div>
{#if options.length} {#if options.length}
<div class="wire-next__radio-group" role="radiogroup" aria-label="{hiddenLabel ? label : ''}" aria-invalid="{error ? 'true' : 'false'}"> <div class="wire-next__radio-group" role="radiogroup" aria-label="{hiddenLabel ? label : ''}" aria-invalid="{error ? 'true' : 'false'}">
@@ -55,6 +59,8 @@ size: string = "default"
{/if} {/if}
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small> {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Select { component Select {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -47,7 +51,7 @@ size: string = "default"
client function handleInvalid(sourceEvent) { output.invalid({ name: name, message: sourceEvent.currentTarget.validationMessage, sourceEvent: sourceEvent }) } client function handleInvalid(sourceEvent) { output.invalid({ name: name, message: sourceEvent.currentTarget.validationMessage, sourceEvent: sourceEvent }) }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--select {class}" data-variant="{variant}" data-inline="{inline}" data-floating="{variant === 'floating'}" data-invalid="{error ? 'true' : 'false'}" data-readonly="{readonly}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--select {class}" data-variant="{variant}" data-inline="{inline}" data-floating="{variant === 'floating'}" data-invalid="{error ? 'true' : 'false'}" data-readonly="{readonly}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div> <div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__field-control" data-icon-position="{iconPosition}"> <div class="wire-next__field-control" data-icon-position="{iconPosition}">
{#if icon}<span class="{icon} wire-next__field-icon" aria-hidden="true"></span>{/if} {#if icon}<span class="{icon} wire-next__field-icon" aria-hidden="true"></span>{/if}
@@ -60,6 +64,8 @@ size: string = "default"
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if} {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}
<small class="wire-next__field-error" data-error="{name}">{error}</small> <small class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Switch { component Switch {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -33,11 +37,13 @@ component Switch {
client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() } client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--choice-field {class}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--choice-field {class}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading">{#if cornerHint}<span class="{hiddenLabel ? 'wire-next__sr-only' : ''}">{label}</span><span class="wire-next__field-hint">{cornerHint}</span>{/if}</div> <div class="wire-next__field-heading">{#if cornerHint}<span class="{hiddenLabel ? 'wire-next__sr-only' : ''}">{label}</span><span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<label class="wire-next__choice wire-next--switch" for="{id || name}" data-variant="{variant}" data-icon-position="{iconPosition}"><input id="{id || name}" type="checkbox" role="switch" name="{name}" value="{value}" checked="{checked}" disabled="{disabled}" required="{required}" readonly="{readonly}" aria-checked="{checked ? 'true' : 'false'}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="emitField('input', event)" @change="emitField('change', event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" />{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<span class="{hiddenLabel || cornerHint ? 'wire-next__sr-only' : ''}">{label}</span></label> <label class="wire-next__choice wire-next--switch" for="{id || name}" data-variant="{variant}" data-icon-position="{iconPosition}"><input id="{id || name}" type="checkbox" role="switch" name="{name}" value="{value}" checked="{checked}" disabled="{disabled}" required="{required}" readonly="{readonly}" aria-checked="{checked ? 'true' : 'false'}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="emitField('input', event)" @change="emitField('change', event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" />{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<span class="{hiddenLabel || cornerHint ? 'wire-next__sr-only' : ''}">{label}</span></label>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small> {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
+7 -1
View File
@@ -1,3 +1,7 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Textarea { component Textarea {
outputs { outputs {
input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) input(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
@@ -42,7 +46,7 @@ size: string = "default"
} }
} }
view { view {
<div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--textarea {class}" data-variant="{variant}" data-inline="{inline}" data-floating="{variant === 'floating'}" data-invalid="{error ? 'true' : 'false'}"> <div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--textarea {class}" data-variant="{variant}" data-inline="{inline}" data-floating="{variant === 'floating'}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div> <div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__field-control" data-icon-position="{iconPosition}"> <div class="wire-next__field-control" data-icon-position="{iconPosition}">
{#if icon}<span class="{icon} wire-next__field-icon" aria-hidden="true"></span>{/if} {#if icon}<span class="{icon} wire-next__field-icon" aria-hidden="true"></span>{/if}
@@ -52,6 +56,8 @@ size: string = "default"
{#if helperText}<small id="{(id || name) + '-help'}" class="wire-next__field-help">{helperText}</small>{/if} {#if helperText}<small id="{(id || name) + '-help'}" class="wire-next__field-help">{helperText}</small>{/if}
<small id="{(id || name) + '-error'}" class="wire-next__field-error" data-error="{name}">{error}</small> <small id="{(id || name) + '-error'}" class="wire-next__field-error" data-error="{name}">{error}</small>
</div> </div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
} }
style { style {
@@ -0,0 +1,73 @@
component ComponentBaseStyles {
view {
}
style {
.wire-component {
--wire-component-color: var(--wire-color-primary);
--wire-component-scale: 1;
margin: 0;
color: var(--wire-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wire-component--color-primary {
--wire-component-color: var(--wire-color-primary);
}
.wire-component--color-secondary {
--wire-component-color: var(--wire-color-secondary);
}
.wire-component--color-success {
--wire-component-color: var(--wire-color-success);
}
.wire-component--color-warning {
--wire-component-color: var(--wire-color-warning);
}
.wire-component--color-danger {
--wire-component-color: var(--wire-color-danger);
}
.wire-component--color-info {
--wire-component-color: var(--wire-color-info);
}
.wire-component--size-xs {
--wire-component-scale: 0.78;
}
.wire-component--size-sm {
--wire-component-scale: 0.88;
}
.wire-component--size-default,
.wire-component--size-md {
--wire-component-scale: 1;
}
.wire-component--size-lg {
--wire-component-scale: 1.14;
}
.wire-component--size-xl {
--wire-component-scale: 1.28;
}
.wire-component:not(.wire-btn) {
font-size: calc(1em * var(--wire-component-scale));
}
.wire-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wire-component-color);
}
.wire-component p {
margin: 0;
color: var(--wire-color-muted);
line-height: 1.6;
}
}
}
+494
View File
@@ -0,0 +1,494 @@
component FieldStyles {
view {
}
style {
/* Validation states set by the browser validation runtime. */
.wire-invalid {
border-color: var(--wire-color-danger) !important;
}
.wire-field-error {
display: block;
min-height: 1em;
margin-top: 0.25rem;
color: var(--wire-color-danger);
font-size: 0.8rem;
}
.wire-next--choice {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
}
.wire-next--choice input {
width: 1.1rem;
height: 1.1rem;
accent-color: var(--wire-color-primary);
}
.wire-next--field {
display: grid;
width: min(100%, 28rem);
gap: 0.4rem;
font-size: 0.78rem;
font-weight: 700;
}
.wire-next--field :where(input, textarea, select) {
width: 100%;
min-height: 2.65rem;
padding: 0.6rem 0.7rem;
border: 1px solid var(--wire-color-border);
border-radius: 0.6rem;
outline: 0;
color: var(--wire-color-text);
background: var(--wire-color-bg);
font: inherit;
font-weight: 500;
}
.wire-auth-form form > .wire-next--field,
.wire-auth-form form > .wire-field {
width: 100%;
max-width: none;
}
.wire-auth-form__submit > .wire-btn {
width: 100%;
min-height: 3rem;
border-radius: 0.75rem;
box-shadow: 0 0.8rem 1.8rem color-mix(in srgb, var(--wire-btn-color) 20%, transparent);
}
.wire-next--field :where(input, textarea, select):focus-visible {
border-color: var(--wire-color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-color-primary) 16%, transparent);
}
.wire-next--field textarea {
resize: vertical;
}
.wire-next--field,
.wire-next--choice-field {
--wire-field-color: var(--wire-component-color, var(--wire-color-primary));
width: min(100%, 34rem);
min-width: 0;
color: var(--wire-color-text);
font-size: 0.8125rem;
font-weight: 500;
}
.wire-next--field.wire-next--size-sm,
.wire-next--choice-field.wire-next--size-sm {
font-size: 0.8125rem;
}
.wire-next--field.wire-next--size-lg,
.wire-next--choice-field.wire-next--size-lg {
font-size: 1rem;
}
.wire-next__field-heading {
display: flex;
min-width: 0;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.wire-next__field-heading label {
color: var(--wire-color-text);
font-weight: 600;
}
.wire-next__field-hint,
.wire-next__field-help {
color: var(--wire-color-muted);
font-size: 0.82em;
font-weight: 500;
}
.wire-next__field-control {
position: relative;
min-width: 0;
}
.wire-next__field-control > :is(input, textarea, select) {
padding-inline: 0.85rem;
border-color: var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
font-size: 0.8125rem;
}
.wire-next__field-control[data-icon-position="start"] > :is(input, textarea, select) {
padding-inline-start: 2.5rem;
}
.wire-next__field-control[data-icon-position="end"] > :is(input, textarea, select) {
padding-inline-end: 2.5rem;
}
.wire-next__field-icon {
position: absolute;
z-index: 1;
top: 50%;
left: 0.85rem;
color: var(--wire-color-muted);
pointer-events: none;
transform: translateY(-50%);
}
.wire-next__field-control[data-icon-position="end"] > .wire-next__field-icon {
right: 0.85rem;
left: auto;
}
.wire-next--field[data-variant="outlined"]
.wire-next__field-control
> :is(input, textarea, select) {
border-width: 2px;
}
.wire-next--field[data-variant="underline"]
.wire-next__field-control
> :is(input, textarea, select) {
padding-inline: 0;
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.wire-next--field[data-variant="pill"] .wire-next__field-control > :is(input, select) {
border-radius: 999px;
padding-inline: 1.15rem;
}
.wire-next--field[data-variant="ghost"] .wire-next__field-control > :is(input, textarea, select) {
border-color: transparent;
background: color-mix(in srgb, var(--wire-field-color) 8%, var(--wire-color-surface));
}
.wire-next__field-floating-label {
position: absolute;
top: 50%;
left: 0.85rem;
color: var(--wire-color-muted);
pointer-events: none;
transform: translateY(-50%);
transition: 150ms ease;
}
.wire-next--textarea .wire-next__field-floating-label {
top: 1.35rem;
}
.wire-next--field[data-floating="true"] .wire-next__field-control > :is(input, textarea, select) {
padding-top: 1.2rem;
padding-bottom: 0.3rem;
}
.wire-next--field[data-floating="true"] .wire-next__field-heading > label {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wire-next--field[data-floating="true"]
.wire-next__field-heading:not(:has(.wire-next__field-hint)) {
display: none;
}
.wire-next--field[data-floating="true"]
.wire-next__field-control
> :is(input, textarea):is(:focus, :not(:placeholder-shown))
+ .wire-next__field-floating-label {
top: 0.42rem;
font-size: 0.7em;
transform: none;
}
.wire-next__field-error {
min-height: 1em;
color: var(--wire-color-danger);
font-size: 0.82em;
font-weight: 600;
}
.wire-next__field-error:empty {
display: none;
}
.wire-next--field[data-invalid="true"] :is(input, textarea, select),
.wire-next--choice-field[data-invalid="true"] input {
border-color: var(--wire-color-danger);
}
.wire-next--field :is(input, textarea, select):disabled,
.wire-next--choice-field input:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.wire-next--field :is(input, textarea, select):read-only {
background: var(--wire-color-surface-2);
}
.wire-next--field[data-inline="true"],
.wire-next--choice-field[data-inline="true"] {
display: grid;
width: 100%;
max-width: none;
grid-template-columns: minmax(7rem, max-content) minmax(12rem, 20rem) minmax(10rem, 1fr);
justify-content: center;
align-items: center;
gap: 0.75rem;
}
.wire-next--field[data-inline="true"] .wire-next__field-heading,
.wire-next--choice-field[data-inline="true"] .wire-next__field-heading {
min-height: 2.65rem;
align-items: center;
}
.wire-next--field[data-inline="true"] .wire-next__field-heading {
justify-content: flex-end;
}
.wire-next--field[data-inline="true"] .wire-next__field-help,
.wire-next--choice-field[data-inline="true"] .wire-next__field-help {
display: flex;
min-height: 2.65rem;
align-items: center;
}
.wire-next--choice-field[data-inline="true"] {
grid-template-columns: minmax(12rem, 20rem) minmax(10rem, 1fr);
}
.wire-next--choice-field[data-inline="true"] .wire-next__field-heading:empty {
display: none;
}
.wire-next--field[data-inline="true"] .wire-next__field-error,
.wire-next--choice-field[data-inline="true"] .wire-next__field-error {
grid-column: 2 / -1;
}
.wire-next__sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
border: 0 !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wire-next__choice-field {
display: grid;
width: 100%;
max-width: none;
gap: 0.4rem;
}
.wire-next__choice {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
color: var(--wire-color-text);
width: 100%;
font-size: 0.75rem;
font-weight: 500;
}
.wire-next__checkbox-content > .wire-next__choice {
width: 100%;
min-width: 0;
align-items: flex-start;
}
.wire-next__choice input {
flex: 0 0 auto;
margin: 0;
accent-color: var(--wire-field-color);
}
.wire-next__choice-copy {
display: grid;
min-width: 0;
gap: 0.15rem;
line-height: 1.35;
}
.wire-next__choice-copy small {
color: var(--wire-color-muted);
font-weight: 500;
}
.wire-next__radio-group,
.wire-next__checkbox-group {
display: grid;
min-width: 0;
gap: 0.65rem;
}
.wire-next--radio-field[data-orientation="horizontal"] .wire-next__radio-group,
.wire-next--checkbox-field[data-orientation="horizontal"] .wire-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(8rem, max-content));
align-items: stretch;
}
.wire-next--radio-field[data-card="true"] .wire-next__radio-group,
.wire-next--checkbox-field[data-card="true"] .wire-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.wire-next--radio-field[data-card="true"] .wire-next--radio,
.wire-next--checkbox-field[data-card="true"] .wire-next--checkbox {
min-height: 4.5rem;
padding: 0.85rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
}
.wire-next--radio-field[data-card="true"] .wire-next--radio:has(input:checked),
.wire-next--checkbox-field[data-card="true"] .wire-next--checkbox:has(input:checked) {
border-color: var(--wire-field-color);
box-shadow: 0 0 0 1px var(--wire-field-color);
}
.wire-next--radio-field[data-list="true"] .wire-next--radio,
.wire-next--checkbox-field[data-list="true"] .wire-next--checkbox {
padding-block: 0.75rem;
border-bottom: 1px solid var(--wire-color-border);
}
.wire-next--radio-field[data-right-aligned="true"] .wire-next--radio,
.wire-next--checkbox-field[data-right-aligned="true"] .wire-next--checkbox {
justify-content: space-between;
}
.wire-next--radio-field[data-right-aligned="true"] .wire-next--radio input,
.wire-next--checkbox-field[data-right-aligned="true"] .wire-next--checkbox input {
order: 2;
}
.wire-next--radio-field[data-right-aligned="true"] .wire-next__choice-copy,
.wire-next--checkbox-field[data-right-aligned="true"] .wire-next__choice-copy {
order: 1;
}
.wire-next__choice[data-variant="outlined"],
.wire-next__choice[data-variant="floating"] {
padding: 0.65rem 0.8rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
}
.wire-next__choice[data-variant="pill"] {
padding: 0.65rem 1rem;
border-radius: 999px;
background: color-mix(in srgb, var(--wire-field-color) 9%, var(--wire-color-surface));
}
.wire-next__choice[data-variant="ghost"] {
padding: 0.65rem 0.8rem;
border-radius: var(--wire-radius-sm);
background: color-mix(in srgb, var(--wire-field-color) 7%, transparent);
}
.wire-next__choice[data-variant="underline"] {
padding-block: 0.5rem;
border-bottom: 1px solid var(--wire-color-border);
}
.wire-next--select .wire-next__field-control {
display: flex;
align-items: center;
}
.wire-next__color-control,
.wire-next__range-control,
.wire-next__input-group-control,
.wire-next__file-control {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
}
.wire-next__picker-control,
.wire-next--time-picker .wire-next__field-control {
position: relative;
display: flex;
min-width: 0;
align-items: center;
}
.wire-next__picker-control > button:first-of-type,
.wire-next__time-trigger {
display: flex;
width: 100%;
min-height: 2.65rem;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.7rem 0.85rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
color: var(--wire-color-text);
font: inherit;
text-align: left;
}
.wire-next__calendar,
.wire-next__time-panel {
z-index: 20;
display: grid;
width: min(100%, 22rem);
gap: 0.75rem;
padding: 0.85rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface);
box-shadow: var(--wire-shadow-3);
}
.wire-next--date-picker[data-expanded="false"] .wire-next__calendar,
.wire-next--time-picker[data-expanded="false"] .wire-next__time-panel {
display: none;
}
.wire-next__calendar button,
.wire-next__time-panel button {
min-height: 2.25rem;
border: 0;
border-radius: var(--wire-radius-sm);
background: transparent;
color: inherit;
}
.wire-next__calendar-weekdays,
.wire-next__calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.2rem;
text-align: center;
}
.wire-next__calendar-weekdays {
color: var(--wire-color-muted);
font-size: 0.75rem;
}
.wire-next__calendar-grid button:hover,
.wire-next__time-panel button:hover {
background: var(--wire-color-surface-2);
}
.wire-next__calendar-grid button[aria-pressed="true"],
.wire-next__time-panel button[aria-pressed="true"] {
background: var(--wire-field-color);
color: var(--wire-color-primary-contrast, #fff);
}
.wire-next__time-period {
display: flex;
gap: 0.35rem;
}
.wire-next--file-upload-progress .wire-next__row,
.wire-next__upload-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.wire-next--file-upload-progress .wire-next__row > span {
display: grid;
min-width: 0;
}
.wire-next--field[data-variant="underline"]
:is(.wire-next__input-group-control, .wire-next__file-control, .wire-next__color-control) {
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
}
.wire-next--field[data-variant="outlined"]
:is(.wire-next__input-group-control, .wire-next__file-control) {
border-width: 2px;
}
.wire-next--field[data-variant="pill"]
:is(.wire-next__input-group-control, .wire-next__file-control) {
border-radius: 999px;
}
.wire-next--field[data-variant="ghost"]
:is(.wire-next__input-group-control, .wire-next__file-control, .wire-next__color-control) {
border-color: transparent;
background: color-mix(in srgb, var(--wire-field-color) 8%, var(--wire-color-surface));
}
@media (max-width: 640px) {
.wire-next--field[data-inline="true"],
.wire-next--choice-field[data-inline="true"] {
grid-template-columns: 1fr;
}
.wire-next--field[data-inline="true"] .wire-next__field-error,
.wire-next--choice-field[data-inline="true"] .wire-next__field-error {
grid-column: auto;
}
}
.wire-next--toggle-password:has(input.wire-invalid) .wire-next__password-control > input,
.wire-next--toggle-password.wire-next--invalid .wire-next__password-control > input {
border-color: var(--wire-color-danger);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-color-danger) 14%, transparent);
}
.wire-next--strong-password.wire-next--invalid input,
.wire-next--strong-password input.wire-invalid {
border-color: var(--wire-color-danger);
}
}
}
+298
View File
@@ -0,0 +1,298 @@
component SelectStyles {
view {
}
style {
.wire-next--advanced-select {
position: relative;
display: grid;
width: 100%;
gap: 0.55rem;
padding: 0;
border: 0;
background: transparent;
box-shadow: none;
}
.wire-next--advanced-select > label:first-child,
.wire-next--advanced-select .wire-next__row > label {
color: var(--wire-color-text);
font-size: 0.82em;
font-weight: 700;
}
.wire-next--advanced-select .wire-next__row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.wire-next--advanced-select small {
color: var(--wire-color-muted);
font-size: 0.72em;
}
.wire-next__select-search input,
.wire-next--advanced-select select {
width: 100%;
min-height: 2.6em;
padding: 0.62em 0.75em;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
color: var(--wire-color-text);
background: var(--wire-color-surface);
font: inherit;
}
.wire-next__search-icon {
position: absolute;
top: 50%;
left: 0.75em;
z-index: 1;
color: var(--wire-component-color);
transform: translateY(-50%);
}
.wire-next--advanced-select select:focus-visible,
.wire-next__select-search input:focus-visible {
border-color: var(--wire-component-color);
outline: 2px solid color-mix(in srgb, var(--wire-component-color) 24%, transparent);
outline-offset: 1px;
}
.wire-next--advanced-select select[multiple] {
min-height: 8rem;
}
.wire-next__color-dot {
width: 0.65rem;
height: 0.65rem;
border-radius: 50%;
background: var(--option-color);
}
.wire-next__clear-select,
.wire-next__create-option,
.wire-next__load-more {
width: fit-content;
padding: 0;
border: 0;
color: var(--wire-component-color);
background: transparent;
font: inherit;
font-size: 0.75em;
font-weight: 700;
cursor: pointer;
}
.wire-next--invalid :is(input, select) {
border-color: var(--wire-color-danger);
}
.wire-next__validation {
color: var(--wire-color-danger) !important;
}
.wire-next__validation:empty {
display: none;
}
.wire-next--advanced-select:has(input.wire-invalid) .wire-next__select-trigger {
border-color: var(--wire-color-danger);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-color-danger) 16%, transparent);
}
.wire-next--advanced-select-fixed select {
box-shadow: var(--wire-shadow-1);
}
.wire-next__select-control {
position: relative;
}
.wire-next__combobox-control > .wire-next__search-icon {
width: 1rem;
height: 1rem;
pointer-events: none;
}
.wire-next__combobox-toggle .wire-next__select-chevron {
position: static;
transform: none;
}
.wire-next--combobox .wire-next__clear-select {
right: 2.4em;
}
.wire-next__select-trigger {
display: flex;
width: 100%;
min-height: 2.75em;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.55em 2.75em 0.55em 0.8em;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
color: var(--wire-color-text);
background: var(--wire-color-bg);
font: inherit;
text-align: left;
cursor: pointer;
transition:
border-color var(--wire-motion-base) var(--wire-ease-standard),
box-shadow var(--wire-motion-base) var(--wire-ease-standard);
}
.wire-next--open .wire-next__select-trigger,
.wire-next__select-trigger:focus-visible {
border-color: var(--wire-component-color);
outline: 0;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-component-color) 18%, transparent);
}
.wire-next__select-chevron {
position: absolute;
top: 50%;
right: 0.9em;
color: var(--wire-color-muted);
pointer-events: none;
transform: translateY(-54%);
}
.wire-next__clear-select {
position: absolute;
top: 50%;
right: 2.15em;
z-index: 2;
display: grid;
width: 1.4em;
height: 1.4em;
padding: 0;
place-items: center;
border-radius: 50%;
transform: translateY(-50%);
}
.wire-next__clear-select:hover {
background: var(--wire-color-surface-2);
}
.wire-next__select-dropdown {
position: absolute;
top: calc(100% + 0.4rem);
left: 0;
z-index: 80;
display: grid;
width: 100%;
min-width: min(20rem, 92vw);
gap: 0.45rem;
padding: 0.5rem;
overflow: hidden;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
box-shadow: 0 1.25rem 3rem rgb(0 0 0 / 18%);
}
.wire-next[data-placement="top"] .wire-next__select-dropdown {
top: auto;
bottom: calc(100% + 0.4rem);
}
.wire-next__select-dropdown--fixed {
position: fixed;
top: auto;
}
.wire-next__select-list {
display: grid;
max-height: 18rem;
gap: 0.15rem;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
}
.wire-next__option-group {
display: grid;
gap: 0.15rem;
}
.wire-next__group-label {
padding: 0.55rem 0.7rem 0.3rem;
color: var(--wire-component-color);
font-size: 0.68em;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.wire-next__select-option {
position: relative;
display: grid;
width: 100%;
grid-template-columns: minmax(0, 1fr);
align-items: center;
gap: 0.65rem;
padding: 0.58rem 2.5rem 0.58rem 0.7rem;
border: 0;
border-radius: calc(var(--wire-radius-sm) * 0.72);
color: var(--wire-color-text);
background: transparent;
font: inherit;
text-align: left;
cursor: pointer;
}
.wire-next__select-option:has(> img),
.wire-next__select-option:has(> i:not(.wire-next__check)) {
grid-template-columns: auto minmax(0, 1fr);
}
.wire-next__select-option > span {
display: grid;
min-width: 0;
gap: 0.12rem;
}
.wire-next__select-option strong {
overflow: hidden;
font-size: 0.82em;
font-weight: 650;
text-overflow: ellipsis;
white-space: nowrap;
}
.wire-next__select-option small {
overflow: hidden;
font-size: 0.68em;
text-overflow: ellipsis;
white-space: nowrap;
}
.wire-next__select-option img,
.wire-next__selected-avatar {
width: 1.65rem;
height: 1.65rem;
border-radius: 50%;
object-fit: cover;
}
.wire-next__select-option--active {
background: var(--wire-color-surface-2);
}
.wire-next__select-option--selected {
color: var(--wire-component-color);
}
.wire-next__select-option:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.wire-next__check {
position: absolute;
top: 50%;
right: 0.8rem;
width: 1rem;
height: 1rem;
color: var(--wire-component-color);
transform: translateY(-50%);
}
.wire-next__select-message {
display: flex;
min-height: 4rem;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 1rem;
color: var(--wire-color-muted);
font-size: 0.78em;
text-align: center;
}
.wire-next__create-option,
.wire-next__load-more {
width: 100%;
padding: 0.65rem;
border-top: 1px solid var(--wire-color-border);
text-align: left;
}
.wire-next--disabled {
opacity: 0.62;
}
.wire-next__select-trigger .wire-next__select-tags {
flex-wrap: nowrap;
overflow: hidden;
}
@media (max-width: 640px) {
.wire-next__select-dropdown {
min-width: 100%;
}
}
}
}
+21 -2
View File
@@ -28,17 +28,33 @@ test("bundled UI assets are discoverable and readable", () => {
test("global UI CSS stays below its migration ratchet", () => { test("global UI CSS stays below its migration ratchet", () => {
const css = uiCss(); const css = uiCss();
expect(gzipSync(new TextEncoder().encode(css)).length).toBeLessThanOrEqual(9_338); expect(gzipSync(new TextEncoder().encode(css)).length).toBeLessThanOrEqual(3_762);
for (const localOrRemovedFamily of [ for (const localOrRemovedFamily of [
".wire-switch", ".wire-switch",
".wire-alert", ".wire-alert",
".wire-card", ".wire-card",
".wire-footer",
".wire-metric-card",
".wire-segmented-group",
".wire-dropdown", ".wire-dropdown",
".wire-btn",
".wire-next",
]) { ]) {
expect(css).not.toContain(localOrRemovedFamily); expect(css).not.toContain(localOrRemovedFamily);
} }
}); });
test("components do not carry the standalone wire-next generation artifact", () => {
const offenders = uiComponentNames().filter((name) => {
const source = readFileSync(uiComponentPath(name), "utf8");
return [...source.matchAll(/class=["']([^"']*)["']/g)].some((match) =>
match[1]!.split(/\s+/).includes("wire-next"),
);
});
expect(offenders).toEqual([]);
});
test("every bundled component carries local styles", () => { test("every bundled component carries local styles", () => {
const unstyled = uiComponentNames().filter( const unstyled = uiComponentNames().filter(
(name) => !/^\s{2,4}style \{/m.test(readFileSync(uiComponentPath(name), "utf8")), (name) => !/^\s{2,4}style \{/m.test(readFileSync(uiComponentPath(name), "utf8")),
@@ -460,7 +476,10 @@ test("component-system CSS includes responsive, theme-token, focus, and reduced-
expect(css).toContain(".wire-bg-primary"); expect(css).toContain(".wire-bg-primary");
expect(css).toContain(".wire-text-muted"); expect(css).toContain(".wire-text-muted");
expect(css).toContain(".wire-visually-hidden"); expect(css).toContain(".wire-visually-hidden");
expect(css).toContain(".wire-next--field"); expect(css).not.toContain(".wire-next--field");
expect(
readFileSync(join(uiComponentsDir(), "..", "styles", "FieldStyles.wrn"), "utf8"),
).toContain(".wire-next--field");
expect(css).toContain('[data-show="false"]'); expect(css).toContain('[data-show="false"]');
expect(css).toContain("display: none !important"); expect(css).toContain("display: none !important");
/* /*
+2 -1447
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -13,6 +13,9 @@ const files = [
...readdirSync(join(uiRoot, "components")) ...readdirSync(join(uiRoot, "components"))
.filter((file) => file.endsWith(".wrn")) .filter((file) => file.endsWith(".wrn"))
.map((file) => join(uiRoot, "components", file)), .map((file) => join(uiRoot, "components", file)),
...readdirSync(join(uiRoot, "styles"))
.filter((file) => file.endsWith(".wrn"))
.map((file) => join(uiRoot, "styles", file)),
].sort(); ].sort();
const artifacts = Object.fromEntries( const artifacts = Object.fromEntries(
files.map((file) => [ files.map((file) => [