Compare commits
46
Commits
7a36c1905f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84e16ebc41 | ||
|
|
7993a303ac | ||
|
|
a99290a216 | ||
|
|
c648efc240 | ||
|
|
decbee2fd1 | ||
|
|
e62e7f5d27 | ||
|
|
b2c74a3a29 | ||
|
|
fe86570f45 | ||
|
|
bc1211ea8b | ||
|
|
d2e24eb089 | ||
|
|
e71885d51c | ||
|
|
138b5d0950 | ||
|
|
7e1eb899b3 | ||
|
|
3f6612cfe7 | ||
|
|
510b3ba777 | ||
|
|
1ff2c3339c | ||
|
|
027873459d | ||
|
|
d0bccfe37e | ||
|
|
120889060a | ||
|
|
26ec18c24b | ||
|
|
7658c5d499 | ||
|
|
3cb6ba1233 | ||
|
|
64db7da08d | ||
|
|
9f6552ea62 | ||
|
|
5bc391e74f | ||
|
|
3327ec0d37 | ||
|
|
ceb865f4f1 | ||
|
|
45309b3f72 | ||
|
|
8f3a13c854 | ||
|
|
eca4447dcf | ||
|
|
904bca21b2 | ||
|
|
b98d417185 | ||
|
|
7d737ec0a0 | ||
|
|
f5538ed621 | ||
|
|
d2d830e7e2 | ||
|
|
0618782355 | ||
|
|
9067e68f69 | ||
|
|
9d64ec60c4 | ||
|
|
fc1eea3939 | ||
|
|
4ec5dad60a | ||
|
|
a1a2947910 | ||
|
|
270040fb5f | ||
|
|
f9e285b182 | ||
|
|
3b402b4e36 | ||
|
|
fa535575c5 | ||
|
|
46ef4b775b |
@@ -292,7 +292,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.8.63",
|
||||
"version": "0.8.64",
|
||||
"bin": {
|
||||
"wrnexus": "src/index.ts",
|
||||
},
|
||||
@@ -320,7 +320,7 @@
|
||||
},
|
||||
"packages/compiler": {
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.8.21",
|
||||
"version": "0.8.22",
|
||||
"dependencies": {
|
||||
"@wrnexus/csr": "workspace:*",
|
||||
"@wrnexus/store": "workspace:*",
|
||||
@@ -342,7 +342,7 @@
|
||||
},
|
||||
"packages/csr": {
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.35",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
@@ -668,7 +668,7 @@
|
||||
},
|
||||
"packages/syntax": {
|
||||
"name": "@wrnexus/syntax",
|
||||
"version": "0.8.16",
|
||||
"version": "0.8.18",
|
||||
},
|
||||
"packages/test": {
|
||||
"name": "@wrnexus/test",
|
||||
@@ -680,7 +680,7 @@
|
||||
},
|
||||
"packages/typecheck": {
|
||||
"name": "@wrnexus/typecheck",
|
||||
"version": "0.8.15",
|
||||
"version": "0.8.16",
|
||||
"dependencies": {
|
||||
"@wrnexus/syntax": "workspace:*",
|
||||
"typescript": "^6.0.3",
|
||||
@@ -688,7 +688,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.8.23",
|
||||
"version": "0.8.56",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
|
||||
@@ -4,22 +4,22 @@
|
||||
"artifacts": {
|
||||
"packages/ui/components/Accordion.wrn": "09251f76a2f9385e97eb67e06c0cf868e1771ac5f420647221bec5160287034b",
|
||||
"packages/ui/components/AdvancedSelect.wrn": "b952b894a5b35050fc2f6e43c4182101eeee05c75662e2aca4abfe61512b305b",
|
||||
"packages/ui/components/AnnouncementBar.wrn": "cb66b8d0619ee798a115b43f0ad93fda93dfb46571589e7a9f0a8d65ec3c84c9",
|
||||
"packages/ui/components/AnnouncementBar.wrn": "5b3219db555e0fcdb94ec41dc18186ee1b7c0cf9c260469f7fb1fc43435c28c4",
|
||||
"packages/ui/components/AuthForm.wrn": "46a3e1db1c9e69efc52473cbf2fababdcbfe7144e7571469638d2c3addd4d7f0",
|
||||
"packages/ui/components/AuthSplitLayout.wrn": "724938921bc38cc5a0422de69c237b630f099f2df94c66977362b048e4a420a5",
|
||||
"packages/ui/components/AvatarGroup.wrn": "30b702e06a1adff8b9bae32820206020e086a69560cbdcbadb19f04f25951f97",
|
||||
"packages/ui/components/BackToTop.wrn": "5b760ec7941ffd8e58c5dedae1b322ee46e072e927533d7ec077b511a816e436",
|
||||
"packages/ui/components/BackToTop.wrn": "d4b8d1fd37ff26c89b4276ce54ce6ac9581dc2ff8bbcbe160e072778bba76eab",
|
||||
"packages/ui/components/Blockquote.wrn": "3761bf13babb6eb07ce7392258ee439ef5478064cf589eff63f945b950e00d2e",
|
||||
"packages/ui/components/Breadcrumb.wrn": "9f6c23550ebfc635c36ca8660953775170b4a471c0cd0206b9a7ba760994980a",
|
||||
"packages/ui/components/ButtonGroup.wrn": "fc23565ad56bd8483fcb777d9e02ccba2f9ea476c085754d42503bb83b718f72",
|
||||
"packages/ui/components/CTASection.wrn": "d0665f2aa33eea95e6f84120f739b3dd9c8723c882138bcb1a7f80dcd7909904",
|
||||
"packages/ui/components/Card.wrn": "3cc2ff238bda279c169026a462236144ad11ec0046deef00ab00e001a07e8a4c",
|
||||
"packages/ui/components/CTASection.wrn": "f6872b980432d5ee9d88388250be9838ccce51bd706349b283ef472ee692b237",
|
||||
"packages/ui/components/Card.wrn": "be8cd6a438d1ede4c284a302b76f0fbc235a2423b83a7ced21131763fecc8a90",
|
||||
"packages/ui/components/Carousel.wrn": "e7d921f802aa19210f3f415b59b70750a2c8d78e30684ad334809741c70a6bf9",
|
||||
"packages/ui/components/Chart.wrn": "07bc01247b5a1c2b82c2ba8386b7fcdbf480efead75d52b7da04d471072c7ee4",
|
||||
"packages/ui/components/ChatBubble.wrn": "d71f7d6567d76ac0eb3ecc8e2e567fec12bb3236ea05f4d0a72286abc0b7cbb1",
|
||||
"packages/ui/components/Clipboard.wrn": "5a93f2bae337d7ce364229b796bac673f60dde6dc135f0969f4e106e04854d30",
|
||||
"packages/ui/components/Collapse.wrn": "26df087a61c82f1aa1749785f4c9be654c4f934859c1a47acfcb8cc67eecc75d",
|
||||
"packages/ui/components/ColorPicker.wrn": "557e36adbdee66d8ba9dcdc2446c13f3385a243f2766777feef8504908006c99",
|
||||
"packages/ui/components/ColorPicker.wrn": "ccac7eb49c0f443b404004f1298fa905fde45c52b51466194920939c920348c9",
|
||||
"packages/ui/components/Columns.wrn": "8ccd9e53f6799b09f8b996740d12e4e73ee782f6fd3e57595c6c16f788b9b812",
|
||||
"packages/ui/components/Combobox.wrn": "58488dbb60e41dacbf223afdb4a05abdbec53b7f54cd79a0db659add7057b1c1",
|
||||
"packages/ui/components/Confetti.wrn": "0e81426a3ee8aff672385a5466cf96085c25ba17677a2fef432bcddf13f4d58e",
|
||||
@@ -28,7 +28,7 @@
|
||||
"packages/ui/components/CustomScrollbar.wrn": "2a3a2719580c21823d079c92ebb000c7a5a7d9d16e3fa9275b8fb7d4449cb437",
|
||||
"packages/ui/components/DataMap.wrn": "338f0d0a3307289dd0387b7fe7ba631fa0b65b6be8c7b9ee3feb2203306f76a3",
|
||||
"packages/ui/components/DataTable.wrn": "e929fdb4e59613878a03fb6a11e09c42f773c06ca7b20e43ae16a266835f2af4",
|
||||
"packages/ui/components/DatePicker.wrn": "91bcfd3832d3bccc4de5402c0c06209947bde593bad4f869b4c29ee14a5c0bef",
|
||||
"packages/ui/components/DatePicker.wrn": "cefbbb2abb57ad4c57864a0c65589592a20f9ea1695f9fc7693c3135dd82e71a",
|
||||
"packages/ui/components/DeviceFrame.wrn": "c38d25419ee44b05d7c443cc4e854aeeb7a53a9289e374d71834de2dd2e4d979",
|
||||
"packages/ui/components/Divider.wrn": "bfa36cf16ccd48c996bb0b08a438f1b281ea1811049ed69fc7680925b1eba019",
|
||||
"packages/ui/components/DragAndDrop.wrn": "84305f07d43cdb9eff1f63c19458d88b0aec013424d05a49a2cafbecedc60513",
|
||||
@@ -36,15 +36,15 @@
|
||||
"packages/ui/components/Dropdown.wrn": "978367a9dde22448cc664d686e4dd9c579fe8ba0b2cf02d610d95a3501f8dbb0",
|
||||
"packages/ui/components/FeatureCard.wrn": "1bc548c115f924a0c7ade4c557852757083755c60ca0ee747b150a7cca3ebced",
|
||||
"packages/ui/components/FeatureGrid.wrn": "15b5d6f9ddb7e3697682e7247fdf3c2dd080b770335f84ba9fab2692166fe897",
|
||||
"packages/ui/components/FeatureIconCard.wrn": "3b4f3fa62c6729e686886a5b848e26c255766684829cec2b715967b4e73d6f07",
|
||||
"packages/ui/components/FileInput.wrn": "866a292a3280527bf429893469e7c50f7cf38965d8adb37b45a35ede1606d5b8",
|
||||
"packages/ui/components/FileUploadProgress.wrn": "e5da29c562a521cdd6bb50b8f4d217aa1ab981d7b2a2432af47391472f0a8034",
|
||||
"packages/ui/components/Footer.wrn": "f7c5a77064a09e244f689d42475c4c20143f4c8859cac451c10a50c14652db33",
|
||||
"packages/ui/components/FeatureIconCard.wrn": "5f65d64235babc3e653a5695204412202015e6f3ffadca566fc18c7f9ce70428",
|
||||
"packages/ui/components/FileInput.wrn": "611047c362458a4e2513b741cc3ef43fbe54c10ddafc13775e4ff39c9de34931",
|
||||
"packages/ui/components/FileUploadProgress.wrn": "ba00557afebf60e6221385d6efcf08635789328deee6e551fc53fbb71e1f413b",
|
||||
"packages/ui/components/Footer.wrn": "e3515e05ef3ef51e6dad71d67ddfe110c80fdcf69e952aecdc7973e69d4f08c7",
|
||||
"packages/ui/components/Grid.wrn": "83d4f5f2656d539538723f5791ba3c238901432e9d8c55c74f068d3eb5a74517",
|
||||
"packages/ui/components/Hero.wrn": "345478b212701817ff57060f87982a987baf01a7179c979768e1cd4218b50900",
|
||||
"packages/ui/components/HeroActions.wrn": "67cd31400ccb6abdbbf16219267dee946b44b064b79f25276d20ceb7d6e8a790",
|
||||
"packages/ui/components/Image.wrn": "361179c478d7674c3b998891a366d73de9514fc6cc8786440c6c50b9ef065357",
|
||||
"packages/ui/components/InputGroup.wrn": "30e9f2e4364542179180b6d6e796002d0ffe1e30305146749874697cde6629ea",
|
||||
"packages/ui/components/InputGroup.wrn": "9e259d15207500d07ff626f09c2e2f5eaf4e471f805a7bdbec154b1a23ce0f8d",
|
||||
"packages/ui/components/InputNumber.wrn": "069195cc483f3f7d6f305d03fbe5a821906a030376fafb6f0d969e4c95b68fd0",
|
||||
"packages/ui/components/Kbd.wrn": "6b402cb93f0fa76bb978d1ba483215f70ec7214b1c5e84b568fa3e26e87ed9d9",
|
||||
"packages/ui/components/LayoutSplitter.wrn": "41d070e801a271552023e4392e4c0f67327c61e2e8eaee5f8b87af254b1d79c1",
|
||||
@@ -55,38 +55,38 @@
|
||||
"packages/ui/components/Map.wrn": "3e6d5de5a21dd050c5c65950d6fb2f89af4024e1856a48f055b65b88fcd7b890",
|
||||
"packages/ui/components/MarketingSectionHeader.wrn": "39b79499b77dc1c7d34e4e5ecb45a9ddc6182f78c2a2586671c4fa07f44679ad",
|
||||
"packages/ui/components/Marquee.wrn": "81cd80732be34c8d7e4bf28b00347e1a9273166afdf66589d9f5d5774b69403b",
|
||||
"packages/ui/components/MegaMenu.wrn": "6a6db166259f8f2bd7ac8fbb21dba6d915c4b936d87b19805caa78189f54730a",
|
||||
"packages/ui/components/MegaMenu.wrn": "a34a84f31f7a3016cf5a607a22f8f7b2322552c550278f6e8c5e6d6cb18848a8",
|
||||
"packages/ui/components/MetricCard.wrn": "513a6e99be3d9db46d96af9942b51331a0a048b184603e77e814506f0a52bdc5",
|
||||
"packages/ui/components/MetricGrid.wrn": "c71e53249835908833055b553e64a8ee55fdf11ac4605436a9581ebb87a0608b",
|
||||
"packages/ui/components/MetricGrid.wrn": "1fcebf51dcbe8520fe16bda904c1ef206c25c3900438d53877492c64ea21a1a5",
|
||||
"packages/ui/components/Modal.wrn": "7fa4b877772736f29f690e24d8742922b310397ef3083f0b78acb67a9f0d14b2",
|
||||
"packages/ui/components/Nav.wrn": "c45b0ecb42250f4b3ede33c8932a025f789dda9ef48dbf332459ae458163e69a",
|
||||
"packages/ui/components/Navbar.wrn": "01903230210cd2e5e6d9325a0708f85af9623d4118e3a53f1adb770fd545425d",
|
||||
"packages/ui/components/PageHeader.wrn": "321cde36ce8d521a57901033d46e8b437f42e558cca27f49ca26f7172aff1d54",
|
||||
"packages/ui/components/Navbar.wrn": "b68a824e479ce6957afc3e9710442281cdc1a8d02cd96501fe060271f54368e4",
|
||||
"packages/ui/components/PageHeader.wrn": "29a93cdca7285548b79ebe5b6421eef6dbb4d5ae8be2508fa2d681e1068ba07f",
|
||||
"packages/ui/components/Pagination.wrn": "d54226705d4556f76ee5f0d6ae82d101ca6d006397756d547a9aa5954415ba39",
|
||||
"packages/ui/components/PinInput.wrn": "4dc398456f6392d7925db941debb484c7cb0358ecef527f696fe5ec4800a7602",
|
||||
"packages/ui/components/Popover.wrn": "1246b99b4236bc7109a5d1427d013bb07bbea7a2da4f821517d2545ee0b5c019",
|
||||
"packages/ui/components/PortalDashboard.wrn": "f04171eb1f8cabef06de3c20e4694b766def3407e1e20d2c9aaebc9bb7da03ee",
|
||||
"packages/ui/components/PreferenceSwitcher.wrn": "77229e15449182563ebf49159507139928eb74e799e4438e00e9aa81fd60f982",
|
||||
"packages/ui/components/PreferenceSwitcher.wrn": "519cf911390f529fff3fd16e11fb58e79d618fe851902202dc37552ee1182395",
|
||||
"packages/ui/components/PublicPageShell.wrn": "3d3f25c03d48f3d3d7e3c567e79cb137c7fce1867e054fd6d440fa1d2e961077",
|
||||
"packages/ui/components/RangeSlider.wrn": "bbf646c5d5a89a62986290db811d77b8f635ebcaea9ca2d334b569aa5edc785b",
|
||||
"packages/ui/components/RangeSlider.wrn": "e8ab41782c9d6e58638f0bfccf6e3172af4ebf552189d2199cc6c96bd1652067",
|
||||
"packages/ui/components/Rating.wrn": "902b55c64e3798f3097f09b1558d1a2372f66d4f760662de085783108ee04587",
|
||||
"packages/ui/components/Scrollspy.wrn": "b9c6400ee92a7c228362610a2051f37bff1d3baba1dbacd66d58397dfda5d856",
|
||||
"packages/ui/components/Scrollspy.wrn": "4b452fcbd524feda9c9f2c81894ee01747a51888e3b86fafb37c5d4d192a3a51",
|
||||
"packages/ui/components/SearchBox.wrn": "3f88340e0b69c0cd552023c72bd22dc511bc00422d46060097bcf0777e683675",
|
||||
"packages/ui/components/Section.wrn": "45b7a1977c25a6020d5cc8bab2ce9c80730e7ec2d51ad53e8c9f64cd9b5b64cd",
|
||||
"packages/ui/components/Section.wrn": "78da13eb8bc1acafe8e6151cbaeed4baaac2ab860e3209f1d13c467f279d2e68",
|
||||
"packages/ui/components/SectionHeader.wrn": "aca2df8eb35806096f5b9af466b018e5c22a19422e8665f488380da577a0ceb5",
|
||||
"packages/ui/components/Sidebar.wrn": "e0250a973aea589a4d7dcc8b30dd01f22592bf22ba012e54a672b2a2288d8af6",
|
||||
"packages/ui/components/SplitHero.wrn": "843387dc7b920b3cf6a1b2b785b8653c281982e524479edc2638a89381ff3577",
|
||||
"packages/ui/components/StatsBar.wrn": "c273bd6ca417a0a20e5e6f1214414f678741b73bedd885fea1037575fa8c5b95",
|
||||
"packages/ui/components/SplitHero.wrn": "7127cb869d1228522d066e028818dd9e512de8b6dd8f99e3dec500e6da5a3cb0",
|
||||
"packages/ui/components/StatsBar.wrn": "6fabcb4374b1e074f58ebcaba968f235c8a3409762163aea51298e2515a14435",
|
||||
"packages/ui/components/Stepper.wrn": "7ec0902acd7cb74b44d490215b6421dae9a41731735ad1e9aec17e8bfc3fa142",
|
||||
"packages/ui/components/StrongPassword.wrn": "407eabcc13affc9323a346ddba8e39c81b885a59ef7043d2bd5567d4b7553282",
|
||||
"packages/ui/components/StrongPassword.wrn": "cc31cb080c4b4116c544b4340b8d740d08fec1b05563372b3280c50759416c87",
|
||||
"packages/ui/components/StyledIcon.wrn": "0874f9a234b053a7c8b9c6983be5d664453a64c4576dff1a049101e249a2866d",
|
||||
"packages/ui/components/Tabs.wrn": "f85214960e62da2fac41228deba511a50c57fd94f2830a8233b75048f6a542e9",
|
||||
"packages/ui/components/TextLink.wrn": "9d561631c5f65a2bcdc59f5c0b166a5dd794a396f42093f2cd26388386f6f954",
|
||||
"packages/ui/components/TimePicker.wrn": "ec0860a4a82a042a65acf9aa81519a5c815b61e0b8080cf6f1c986d26ce7f0e8",
|
||||
"packages/ui/components/TimePicker.wrn": "33bd7a7069b7abe858cf1617b5b950733b37a8d522469ed673cff00b4dfcad0e",
|
||||
"packages/ui/components/Timeline.wrn": "fb553f69763840532eef1c0aafa79013f57411dc5335e7cb7e7c20c5a3753008",
|
||||
"packages/ui/components/Toaster.wrn": "5e4c9b2c1bfd5675a81a32e57684b99393ac7fe45d8420c7409a4e95d47d080c",
|
||||
"packages/ui/components/ToggleCount.wrn": "e577195ee05ddb43cceec47145f6b90c0149f2f17055733d58911fcd02de2c7f",
|
||||
"packages/ui/components/TogglePassword.wrn": "6bd4d9fa82b0c8e80178430d5a42b1b3ec9725fb62be158585f68af9338c901a",
|
||||
"packages/ui/components/TogglePassword.wrn": "caa58479e5c8b1e3e504181a56a7d6bcf71d0a230010733710961c31e1cc58ab",
|
||||
"packages/ui/components/TreeView.wrn": "f54d8495730203b923629bcfb167d5ca41236af0101ea2723d33354413555b4b",
|
||||
"packages/ui/components/Typography.wrn": "9676af57f2a937b21f45512e0a290b5bee4a16267fbf404850297d29645d88b5",
|
||||
"packages/ui/components/WysiwygEditor.wrn": "a858ebd4e6e2672463ba57d4d049176ca1cc98dac32ab3df3638d6a9cdcd0f2d",
|
||||
@@ -94,15 +94,15 @@
|
||||
"packages/ui/components/avatar.wrn": "8bc705e459b13c7f063c57e1506ce540e50fa8ef9020ae85b6e8b6a3579fc5dc",
|
||||
"packages/ui/components/badge.wrn": "e44e33633fb34e897696cd9290f210108e35a3e2dc3b2a4a367411f45c69a1e1",
|
||||
"packages/ui/components/button.wrn": "65570c58cb2547f1e17cd158eec7cf7b1ffc721e6976c6486c9df564627b1219",
|
||||
"packages/ui/components/checkbox.wrn": "18046396b75d0b9c6bb2bdb09dbff1846c84fb07490f390a25ca7b5367fd2ef4",
|
||||
"packages/ui/components/input.wrn": "e4c14527f009b610b4aa96d8d7b7d5ac3ca1ffd34237bab348ee9dc4ddc55847",
|
||||
"packages/ui/components/checkbox.wrn": "da735354b57f33946591e1bd018b521ff709bae1325e2317735824673188377a",
|
||||
"packages/ui/components/input.wrn": "2f17353cbf0711ca50a935ae03ee9ee53594d7e35eb73f5df573fadb7958fac2",
|
||||
"packages/ui/components/progress.wrn": "6307a90585197d7aab19a8710b2430f5d4ed27ce77e9b90b1414ea0eed876492",
|
||||
"packages/ui/components/radio.wrn": "09425a78358de5bbd2f47482f313e80065135335b969ce5ccdd5c7cc3ea5232c",
|
||||
"packages/ui/components/select.wrn": "1d8d47a74d5ab9ed58d0ba11f46910897233b96652d17f5962f5dda60bd4cf8a",
|
||||
"packages/ui/components/radio.wrn": "df8e562324b6d99f5a157b0971d63bd715daa0cf6189e7b0d4df04d6f0411388",
|
||||
"packages/ui/components/select.wrn": "747321d6292b8052e44e8014e87ce25f4b56d8e07f86f1061e0ed0cec7651356",
|
||||
"packages/ui/components/skeleton.wrn": "4fc5e0846eeefd7830c038e1789be995c4f9d833aa913ff079eb4863baa65648",
|
||||
"packages/ui/components/spinner.wrn": "2322645da7ef53f7c06035ff071d9a2f6ffe2901ff9338daed84037369367105",
|
||||
"packages/ui/components/switch.wrn": "874504e4828e9db6a570d78984c4d3a76d0ceb39d70baa877649cb3798c82c85",
|
||||
"packages/ui/components/textarea.wrn": "9870d37664471a103d44435a3977bf40b087a743acd76ee4d77df9b56cccbb2b",
|
||||
"packages/ui/components/switch.wrn": "243f521431ffb644089df7f629bace0c07094638dfdd27e75b2017efa9cbba4f",
|
||||
"packages/ui/components/textarea.wrn": "aa3a75214e998a0b57c50e416476d6f85fa0313aaf97262f2f9222150c6c9e26",
|
||||
"packages/ui/components/tooltip.wrn": "f3dfdfa5cd9661fe5e95ef3580eef4c7437c069726421370c2d7328fbe7d840d",
|
||||
"packages/ui/styles/SelectStyles.wrn": "074fe0d67de4ef5e9f9cfe879beb72fd5c83352888687d3a1a4b7722c87af3ca",
|
||||
"packages/ui/ui.css": "9ea591404e9cf675bbf1003e15e9fad00327094ff217dc20733f0fc87c0f4a62"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "wrnexus",
|
||||
"displayName": "WRNexus Language Support",
|
||||
"description": "Complete WRNexus v0.8.3 language support for typed imports, props, state, outputs, runtime functions, stores, diagnostics, formatting, navigation, and migration assistance.",
|
||||
"version": "0.8.9",
|
||||
"version": "0.8.11",
|
||||
"publisher": "wrnexus",
|
||||
"private": true,
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
// Generated by scripts/build-editor-compiler.mjs. Do not edit directly.
|
||||
// WRN editor compiler source hash: 884c9faffb5517c907321484014e00562654ea0ba96e432bec319e0001934446
|
||||
// WRN editor compiler source hash: 3d346a264807ba7b107584dc9a7b505b1d14057b2ad1aa85c5ea890f9b09c272
|
||||
// WRN editor compiler generator hash: f8c1094b47e695151205899de28cd63d574356d26ac49219c4a43db0f2391ef5
|
||||
// Generated with TypeScript: 6.0.3
|
||||
const __nodeRequire = require;
|
||||
@@ -4067,6 +4067,7 @@ function islandNamesFrom(imports) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.NativeCompileError = void 0;
|
||||
exports.generateNative = generateNative;
|
||||
const syntax_1 = require("@wrnexus/syntax");
|
||||
class NativeCompileError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
@@ -4132,12 +4133,12 @@ function expression(value) {
|
||||
function textJsx(value) {
|
||||
const pieces = [];
|
||||
let last = 0;
|
||||
for (const match of value.matchAll(/\{([^{}]+)\}/g)) {
|
||||
if (match.index > last)
|
||||
pieces.push(value.slice(last, match.index));
|
||||
const expr = match[1].trim();
|
||||
for (const segment of (0, syntax_1.extractInterpolations)(value)) {
|
||||
if (segment.start > last)
|
||||
pieces.push(value.slice(last, segment.start));
|
||||
const expr = segment.expression;
|
||||
pieces.push(expr.startsWith("t:") ? `{${JSON.stringify(expr.slice(2).trim())}}` : `{${expr}}`);
|
||||
last = match.index + match[0].length;
|
||||
last = segment.end;
|
||||
}
|
||||
pieces.push(value.slice(last));
|
||||
return pieces.join("").replace(/([<>])/g, (char) => (char === "<" ? "<" : ">"));
|
||||
@@ -6227,6 +6228,26 @@ function expandStructuredStateDeclarations(lines, unit) {
|
||||
}
|
||||
});
|
||||
}
|
||||
function formatStructuredPropDeclaration(value, unit, depth, printWidth) {
|
||||
const match = /^([A-Za-z_$][\w$]*\??\s*(?::\s*[^=]+?)?\s*=\s*)([\[{][\s\S]*)$/.exec(value);
|
||||
if (!match)
|
||||
return null;
|
||||
try {
|
||||
const parsed = JSON.parse(match[2].trim());
|
||||
const compact = JSON.stringify(parsed);
|
||||
const indentation = unit.repeat(depth);
|
||||
if (indentation.length + match[1].length + compact.length <= printWidth)
|
||||
return null;
|
||||
const jsonLines = JSON.stringify(parsed, null, unit).split("\n");
|
||||
return [
|
||||
`${indentation}${match[1]}${jsonLines[0]}`,
|
||||
...jsonLines.slice(1).map((jsonLine) => `${indentation}${jsonLine}`),
|
||||
];
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function formatWrnPass(source, options = {}) {
|
||||
const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4);
|
||||
const printWidth = options.printWidth ?? 100;
|
||||
@@ -6282,6 +6303,12 @@ function formatWrnPass(source, options = {}) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
const structuredProp = formatStructuredPropDeclaration(value, unit, codeDepth + htmlDepth + controlDepth, printWidth);
|
||||
if (structuredProp) {
|
||||
output.push(...structuredProp);
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
const leadingClosingBraces = countLeadingClosingBraces(value);
|
||||
const closesControlBlock = isControlBlockClose(value);
|
||||
const continuesControlBlock = isControlBlockMiddle(value);
|
||||
@@ -6362,7 +6389,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.stripRuntimeFunctionModifiers = exports.parseStructuredImports = exports.parseStoreLifecycle = exports.parseStateDeclarations = exports.parseRuntimeFunctions = exports.parsePersist = exports.parseOutputs = exports.parseComputedDeclarations = exports.supportsSyntaxFeature = exports.diagnosticSummary = exports.sliceSource = exports.createSourceRange = exports.WRN_SYNTAX_FEATURES = exports.WRN_SYNTAX_VERSION = exports.positionAt = exports.isRuntimeTarget = exports.isHydrationStrategy = exports.formatDiagnostic = exports.diagnosticFromError = exports.diagnose = exports.containsReadonlyPropMutation = exports.classifyParseError = exports.assertValidAst = exports.validateTypedInitializer = exports.runtimeTypeOf = exports.inferredRuntimeType = exports.eraseFunctionTypes = exports.VOID_ELEMENTS = exports.ParseError = exports.parseHtmlView = exports.parse = exports.formatWrn = exports.skipLiteralOrComment = exports.LexError = exports.Lexer = void 0;
|
||||
exports.stripRuntimeFunctionModifiers = exports.parseStructuredImports = exports.parseStoreLifecycle = exports.parseStateDeclarations = exports.parseRuntimeFunctions = exports.parsePersist = exports.parseOutputs = exports.parseComputedDeclarations = exports.supportsSyntaxFeature = exports.diagnosticSummary = exports.sliceSource = exports.createSourceRange = exports.WRN_SYNTAX_FEATURES = exports.WRN_SYNTAX_VERSION = exports.positionAt = exports.isRuntimeTarget = exports.isHydrationStrategy = exports.formatDiagnostic = exports.diagnosticFromError = exports.diagnose = exports.containsReadonlyPropMutation = exports.classifyParseError = exports.assertValidAst = exports.validateTypedInitializer = exports.runtimeTypeOf = exports.inferredRuntimeType = exports.eraseFunctionTypes = exports.extractInterpolations = exports.VOID_ELEMENTS = exports.ParseError = exports.parseHtmlView = exports.parse = exports.formatWrn = exports.skipLiteralOrComment = exports.LexError = exports.Lexer = void 0;
|
||||
var tokenizer_ts_1 = require("./tokenizer.js");
|
||||
Object.defineProperty(exports, "Lexer", { enumerable: true, get: function () { return tokenizer_ts_1.Lexer; } });
|
||||
Object.defineProperty(exports, "LexError", { enumerable: true, get: function () { return tokenizer_ts_1.LexError; } });
|
||||
@@ -6374,6 +6401,8 @@ Object.defineProperty(exports, "parse", { enumerable: true, get: function () { r
|
||||
Object.defineProperty(exports, "parseHtmlView", { enumerable: true, get: function () { return parser_ts_1.parseHtmlView; } });
|
||||
Object.defineProperty(exports, "ParseError", { enumerable: true, get: function () { return parser_ts_1.ParseError; } });
|
||||
Object.defineProperty(exports, "VOID_ELEMENTS", { enumerable: true, get: function () { return parser_ts_1.VOID_ELEMENTS; } });
|
||||
var interpolation_ts_1 = require("./interpolation.js");
|
||||
Object.defineProperty(exports, "extractInterpolations", { enumerable: true, get: function () { return interpolation_ts_1.extractInterpolations; } });
|
||||
var types_ts_1 = require("./types.js");
|
||||
Object.defineProperty(exports, "eraseFunctionTypes", { enumerable: true, get: function () { return types_ts_1.eraseFunctionTypes; } });
|
||||
Object.defineProperty(exports, "inferredRuntimeType", { enumerable: true, get: function () { return types_ts_1.inferredRuntimeType; } });
|
||||
@@ -6407,6 +6436,50 @@ Object.defineProperty(exports, "parseStoreLifecycle", { enumerable: true, get: f
|
||||
Object.defineProperty(exports, "parseStructuredImports", { enumerable: true, get: function () { return v060_ts_1.parseStructuredImports; } });
|
||||
Object.defineProperty(exports, "stripRuntimeFunctionModifiers", { enumerable: true, get: function () { return v060_ts_1.stripRuntimeFunctionModifiers; } });
|
||||
|
||||
},
|
||||
"packages/syntax/src/interpolation.ts": function (module, exports, require, __filename, __dirname) {
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.extractInterpolations = extractInterpolations;
|
||||
/** Extract top-level `{...}` expressions while preserving nested object/array literals. */
|
||||
function extractInterpolations(value) {
|
||||
const segments = [];
|
||||
let index = 0;
|
||||
while (index < value.length) {
|
||||
if (value[index] !== "{") {
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
const start = index++;
|
||||
let depth = 1;
|
||||
let quote = null;
|
||||
while (index < value.length && depth > 0) {
|
||||
const char = value[index];
|
||||
if (quote) {
|
||||
if (char === "\\" && index + 1 < value.length)
|
||||
index += 2;
|
||||
else {
|
||||
if (char === quote)
|
||||
quote = null;
|
||||
index++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (char === '"' || char === "'" || char === "`")
|
||||
quote = char;
|
||||
else if (char === "{")
|
||||
depth++;
|
||||
else if (char === "}")
|
||||
depth--;
|
||||
index++;
|
||||
}
|
||||
if (depth === 0) {
|
||||
segments.push({ expression: value.slice(start + 1, index - 1).trim(), start, end: index });
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
},
|
||||
"packages/syntax/src/parser.ts": function (module, exports, require, __filename, __dirname) {
|
||||
"use strict";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// WRN editor extension source hash: 8e634610c27521e6ccbf4e7293eb4320ecaac29944bde5236bb286ff84422203
|
||||
// WRN editor extension source hash: d07c535bd69332e90f207cbc254aae06982b40dd863fbc8de63fb386ecf31f50
|
||||
// WRN editor extension generator hash: 456d1d614e44e5fb1f19b784176c09cf2ade9b64ef73a17934c2698150b62728
|
||||
"use strict";
|
||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// WRN editor language server source hash: 64ecd72706de345e7e8891089d2c3d1aa5c1c2beaa7cf9b816ba32a9c4ad6c54
|
||||
// WRN editor language server source hash: 5258b3e76cec47df752d4bb886fbd57a3f3c4af9ca9b6b2bf6742c8d78025ff1
|
||||
// WRN editor language server generator hash: f593a44aaf05495b789ce7a3086bee1eebb951b884d41c0e017bbcfe5f547e72
|
||||
// @bun @bun-cjs
|
||||
(function(exports, require, module, __filename, __dirname) {var __create = Object.create;
|
||||
@@ -170722,6 +170722,26 @@ function expandStructuredStateDeclarations(lines, unit) {
|
||||
}
|
||||
});
|
||||
}
|
||||
function formatStructuredPropDeclaration(value, unit, depth, printWidth) {
|
||||
const match = /^([A-Za-z_$][\w$]*\??\s*(?::\s*[^=]+?)?\s*=\s*)([\[{][\s\S]*)$/.exec(value);
|
||||
if (!match)
|
||||
return null;
|
||||
try {
|
||||
const parsed = JSON.parse(match[2].trim());
|
||||
const compact = JSON.stringify(parsed);
|
||||
const indentation = unit.repeat(depth);
|
||||
if (indentation.length + match[1].length + compact.length <= printWidth)
|
||||
return null;
|
||||
const jsonLines = JSON.stringify(parsed, null, unit).split(`
|
||||
`);
|
||||
return [
|
||||
`${indentation}${match[1]}${jsonLines[0]}`,
|
||||
...jsonLines.slice(1).map((jsonLine) => `${indentation}${jsonLine}`)
|
||||
];
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function formatWrnPass(source, options = {}) {
|
||||
const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4);
|
||||
const printWidth = options.printWidth ?? 100;
|
||||
@@ -170778,6 +170798,12 @@ function formatWrnPass(source, options = {}) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
const structuredProp = formatStructuredPropDeclaration(value, unit, codeDepth + htmlDepth + controlDepth, printWidth);
|
||||
if (structuredProp) {
|
||||
output.push(...structuredProp);
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
const leadingClosingBraces = countLeadingClosingBraces(value);
|
||||
const closesControlBlock = isControlBlockClose(value);
|
||||
const continuesControlBlock = isControlBlockMiddle(value);
|
||||
@@ -172393,6 +172419,44 @@ function parseHtmlView(src, pos) {
|
||||
const nodes = parseNodeList("root");
|
||||
return { nodes, endPos: i };
|
||||
}
|
||||
// packages/syntax/src/interpolation.ts
|
||||
function extractInterpolations(value) {
|
||||
const segments = [];
|
||||
let index = 0;
|
||||
while (index < value.length) {
|
||||
if (value[index] !== "{") {
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
const start = index++;
|
||||
let depth = 1;
|
||||
let quote = null;
|
||||
while (index < value.length && depth > 0) {
|
||||
const char = value[index];
|
||||
if (quote) {
|
||||
if (char === "\\" && index + 1 < value.length)
|
||||
index += 2;
|
||||
else {
|
||||
if (char === quote)
|
||||
quote = null;
|
||||
index++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (char === '"' || char === "'" || char === "`")
|
||||
quote = char;
|
||||
else if (char === "{")
|
||||
depth++;
|
||||
else if (char === "}")
|
||||
depth--;
|
||||
index++;
|
||||
}
|
||||
if (depth === 0) {
|
||||
segments.push({ expression: value.slice(start + 1, index - 1).trim(), start, end: index });
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
// packages/syntax/src/diagnostics.ts
|
||||
function stripAsciiControlAndSpace(value) {
|
||||
let result = "";
|
||||
@@ -173126,6 +173190,7 @@ function virtualTypeScriptModule(source, filePath = "component.wrn", appRoot = f
|
||||
append(`declare const api: { ${apiType} };`);
|
||||
append(`declare const props: Readonly<${ast.name}Props>;`);
|
||||
append("declare const refs: Record<string, Element | null>;");
|
||||
append("declare const toast: ((message: string, options?: Record<string, unknown>) => unknown) & { success(message: string, options?: Record<string, unknown>): unknown; error(message: string, options?: Record<string, unknown>): unknown; danger(message: string, options?: Record<string, unknown>): unknown; warning(message: string, options?: Record<string, unknown>): unknown; info(message: string, options?: Record<string, unknown>): unknown; dismiss(id: string | number): void; clear(): void };");
|
||||
append("declare function useFetch(path: string, method?: string | { method?: string; query?: unknown; params?: unknown; body?: unknown; data?: unknown }, input?: unknown): Promise<any>;");
|
||||
append(ast.kind === "global-store" || ast.kind === "page-store" ? storeContract(ast) : componentContract(ast));
|
||||
append(importedWrnDeclarations(ast, filePath, appRoot));
|
||||
@@ -173157,7 +173222,7 @@ ${sharedAliases}`);
|
||||
for (const node of nodes) {
|
||||
if (node.type === "element") {
|
||||
for (const attr of node.attrs) {
|
||||
const expressions = attr.event ? [attr.value] : [...attr.value.matchAll(/\{([^{}]+)\}/g)].map((match) => match[1].trim());
|
||||
const expressions = attr.event ? [attr.value] : extractInterpolations(attr.value).map((segment) => segment.expression);
|
||||
for (const expression of expressions) {
|
||||
if (!expression.trim())
|
||||
continue;
|
||||
|
||||
@@ -20,6 +20,20 @@ test("preserves nested prop defaults while formatting", () => {
|
||||
assert.equal(formatWrn(formatted, { insertSpaces: true, tabSize: 2 }), formatted);
|
||||
});
|
||||
|
||||
test("formats large typed JSON prop defaults with the bundled formatter", () => {
|
||||
const source = `component Catalog {
|
||||
props {
|
||||
categories: unknown[] = [{"label":"Platform & Core","apps":[{"label":"Home","href":"/home"},{"label":"SSO","href":"/sso"}]},{"label":"Sales","apps":[{"label":"CRM","href":"/crm"}]}]
|
||||
}
|
||||
view { <div></div> }
|
||||
}`;
|
||||
const formatted = formatWrn(source, { insertSpaces: true, tabSize: 2, printWidth: 70 });
|
||||
|
||||
assert.match(formatted, /categories: unknown\[\] = \[\n \{/);
|
||||
assert.match(formatted, /"apps": \[\n \{/);
|
||||
assert.equal(formatWrn(formatted, { insertSpaces: true, tabSize: 2, printWidth: 70 }), formatted);
|
||||
});
|
||||
|
||||
test("formats native JSON state values with readable indentation", () => {
|
||||
const source = `component Footer {
|
||||
state footerItems = [{"label":"Accessibility","href":"/accessibility","value":"accessibility"},{"label":"Privacy","href":"/privacy","value":"privacy"}]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.8.63",
|
||||
"version": "0.8.64",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -63,14 +63,16 @@ const execFileAsync = promisify(execFile);
|
||||
|
||||
/** Resolve every installed WRNexus dependency independently and in parallel. */
|
||||
export async function latestPackageVersions(names: string[]): Promise<Map<string, string>> {
|
||||
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
||||
const unique = [...new Set(names.filter((name) => name.startsWith("@wrnexus/")))];
|
||||
const entries = await Promise.all(
|
||||
unique.map(async (name) => {
|
||||
try {
|
||||
const { stdout } = await execFileAsync(npm, ["view", name, "version", "--json"], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
const command = process.platform === "win32" ? (process.env.ComSpec ?? "cmd.exe") : "npm";
|
||||
const args =
|
||||
process.platform === "win32"
|
||||
? ["/d", "/s", "/c", "npm", "view", name, "version", "--json"]
|
||||
: ["view", name, "version", "--json"];
|
||||
const { stdout } = await execFileAsync(command, args, { encoding: "utf8" });
|
||||
const parsed = JSON.parse(stdout.trim()) as string | string[];
|
||||
const version = Array.isArray(parsed) ? parsed.at(-1) : parsed;
|
||||
return typeof version === "string" && /^\d+\.\d+\.\d+/.test(version)
|
||||
@@ -1274,6 +1276,13 @@ export async function runUpdate(dir: string, args: string[]): Promise<void> {
|
||||
? new Map<string, string>()
|
||||
: await latestPackageVersions(dependencyNames);
|
||||
|
||||
if (!versionArg && dependencyNames.length > 0 && packageVersions.size === 0) {
|
||||
throw new Error(
|
||||
`Unable to resolve any of ${new Set(dependencyNames).size} installed WRNexus package ` +
|
||||
`version(s) from npm. Check the @wrnexus registry and authentication before retrying.`,
|
||||
);
|
||||
}
|
||||
|
||||
console.log(`\n ⚡ wrnexus update → migrations ${target}${dryRun ? " (dry run)" : ""}`);
|
||||
if (!versionArg) {
|
||||
console.log(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.8.21",
|
||||
"version": "0.8.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Attr, PageAst, ViewNode } from "@wrnexus/syntax";
|
||||
import { extractInterpolations, type Attr, type PageAst, type ViewNode } from "@wrnexus/syntax";
|
||||
|
||||
export class NativeCompileError extends Error {
|
||||
constructor(message: string) {
|
||||
@@ -68,11 +68,11 @@ function expression(value: string): string | null {
|
||||
function textJsx(value: string): string {
|
||||
const pieces: string[] = [];
|
||||
let last = 0;
|
||||
for (const match of value.matchAll(/\{([^{}]+)\}/g)) {
|
||||
if (match.index! > last) pieces.push(value.slice(last, match.index));
|
||||
const expr = match[1]!.trim();
|
||||
for (const segment of extractInterpolations(value)) {
|
||||
if (segment.start > last) pieces.push(value.slice(last, segment.start));
|
||||
const expr = segment.expression;
|
||||
pieces.push(expr.startsWith("t:") ? `{${JSON.stringify(expr.slice(2).trim())}}` : `{${expr}}`);
|
||||
last = match.index! + match[0].length;
|
||||
last = segment.end;
|
||||
}
|
||||
pieces.push(value.slice(last));
|
||||
return pieces.join("").replace(/([<>])/g, (char) => (char === "<" ? "<" : ">"));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.35",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -90,7 +90,7 @@ const CONTROLLER_LOADER = String.raw`
|
||||
if (!componentControllerPromise) {
|
||||
componentControllerPromise = new Promise(function (resolve, reject) {
|
||||
var script = document.createElement("script");
|
||||
script.src = componentControllerUrl;
|
||||
script.src = trustedScriptURL(componentControllerUrl);
|
||||
script.defer = true;
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
|
||||
@@ -26,6 +26,40 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
var behaviorObserver;
|
||||
var clientModuleCache = new Map();
|
||||
|
||||
/*
|
||||
* Production enables require-trusted-types-for 'script'. Every HTML write
|
||||
* below uses compiler-emitted template markup (or the explicit data-html
|
||||
* escape hatch), and controller URLs are generated by the framework. Route
|
||||
* those known sinks through the policy allowed by the default security
|
||||
* preset instead of letting the browser reject hydration midway through a
|
||||
* state update.
|
||||
*/
|
||||
var wrnexusTrustedTypesPolicy = null;
|
||||
if (window.trustedTypes && typeof window.trustedTypes.createPolicy === "function") {
|
||||
try {
|
||||
wrnexusTrustedTypesPolicy = window.trustedTypes.createPolicy("wrnexus", {
|
||||
createHTML: function (value) { return value; },
|
||||
createScriptURL: function (value) { return value; }
|
||||
});
|
||||
} catch (_) {
|
||||
wrnexusTrustedTypesPolicy = window.trustedTypes.defaultPolicy || null;
|
||||
}
|
||||
}
|
||||
|
||||
function trustedHTML(value) {
|
||||
var source = value == null ? "" : String(value);
|
||||
return wrnexusTrustedTypesPolicy
|
||||
? wrnexusTrustedTypesPolicy.createHTML(source)
|
||||
: source;
|
||||
}
|
||||
|
||||
function trustedScriptURL(value) {
|
||||
var source = value == null ? "" : String(value);
|
||||
return wrnexusTrustedTypesPolicy
|
||||
? wrnexusTrustedTypesPolicy.createScriptURL(source)
|
||||
: source;
|
||||
}
|
||||
|
||||
/*
|
||||
* Two builtin chains the runtime reaches for constantly. Aliasing them is
|
||||
* not only shorter: hasOwn keeps prototype keys from reading as data, and
|
||||
@@ -234,8 +268,21 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
Array.prototype.forEach.call(rules || [], function (rule) {
|
||||
var cssText = rule.cssText || "";
|
||||
var match;
|
||||
var pattern = /var\(\s*(--wrn-[A-Za-z0-9_-]+)/g;
|
||||
while ((match = pattern.exec(cssText))) referenced[match[1]] = true;
|
||||
// Only inspect declarations that can affect the current document. A
|
||||
// component stylesheet may contain optional variants that are not
|
||||
// rendered on this page, and variables with fallbacks are intentionally
|
||||
// optional rather than missing theme tokens.
|
||||
if (rule.selectorText) {
|
||||
try {
|
||||
if (!document.querySelector(rule.selectorText)) return;
|
||||
} catch (_) {
|
||||
// Keep inspecting selectors that the DOM implementation cannot parse.
|
||||
}
|
||||
}
|
||||
var pattern = /var\(\s*(--wrn-[A-Za-z0-9_-]+)\s*(,)?/g;
|
||||
while ((match = pattern.exec(cssText))) {
|
||||
if (!match[2]) referenced[match[1]] = true;
|
||||
}
|
||||
if (rule.style) {
|
||||
Array.prototype.forEach.call(rule.style, function (property) {
|
||||
if (String(property).indexOf("--wrn-") === 0) declared[property] = true;
|
||||
@@ -1818,7 +1865,7 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
var next = markup == null ? "" : String(markup);
|
||||
|
||||
if (htmlNode.innerHTML !== next) {
|
||||
htmlNode.innerHTML = next;
|
||||
htmlNode.innerHTML = trustedHTML(next);
|
||||
}
|
||||
});
|
||||
runHtml();
|
||||
@@ -2002,7 +2049,7 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
}
|
||||
|
||||
function clearControlContent() {
|
||||
if (!rangeEnd) { block.innerHTML = ""; return; }
|
||||
if (!rangeEnd) { block.innerHTML = trustedHTML(""); return; }
|
||||
while (block.nextSibling && block.nextSibling !== rangeEnd) {
|
||||
block.parentNode.removeChild(block.nextSibling);
|
||||
}
|
||||
@@ -2010,7 +2057,7 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
|
||||
function appendControlContent(markup, locals) {
|
||||
var template = document.createElement("template");
|
||||
template.innerHTML = markup || "";
|
||||
template.innerHTML = trustedHTML(markup || "");
|
||||
var fragment = template.content;
|
||||
var elements = toArray(fragment.childNodes).filter(function (node) {
|
||||
return node.nodeType === 1;
|
||||
@@ -2399,7 +2446,7 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
var markup = value == null ? "" : String(value);
|
||||
|
||||
if (node.innerHTML !== markup) {
|
||||
node.innerHTML = markup;
|
||||
node.innerHTML = trustedHTML(markup);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,6 +62,13 @@ test("split runtime hydrates a controller only from the controller asset", () =>
|
||||
expect(win.document.activeElement).toBe(buttons[1]);
|
||||
});
|
||||
|
||||
test("production hydration uses Trusted Types for HTML and controller URLs", () => {
|
||||
const runtime = getReactiveRuntime();
|
||||
expect(runtime).toContain('createPolicy("wrnexus"');
|
||||
expect(runtime).toContain('template.innerHTML = trustedHTML(markup || "")');
|
||||
expect(runtime).toContain("script.src = trustedScriptURL(componentControllerUrl)");
|
||||
});
|
||||
|
||||
test("a page without controller markers does not request the controller asset", () => {
|
||||
const win = mount(
|
||||
`<main data-scope="count: 1"><span>{count}</span></main>`,
|
||||
@@ -1081,6 +1088,38 @@ test("development runtime accepts component-local and inline wrn variables", ()
|
||||
expect(warnings).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("development runtime ignores fallback and inactive component variables", () => {
|
||||
const win = new Window() as unknown as Window & Record<string, unknown>;
|
||||
win.document.head.innerHTML =
|
||||
`<style>.visible { color: var(--wrn-optional, red); } ` +
|
||||
`.not-rendered { color: var(--wrn-inactive); }</style>`;
|
||||
win.document.body.innerHTML = `<div class="visible" data-scope=""></div>`;
|
||||
(globalThis as Record<string, unknown>).window = win;
|
||||
(globalThis as Record<string, unknown>).document = win.document;
|
||||
(globalThis as Record<string, unknown>).location = win.location;
|
||||
(globalThis as Record<string, unknown>).NodeFilter = (
|
||||
win as unknown as { NodeFilter: unknown }
|
||||
).NodeFilter;
|
||||
(globalThis as Record<string, unknown>).MutationObserver = (
|
||||
win as unknown as { MutationObserver: unknown }
|
||||
).MutationObserver;
|
||||
(globalThis as Record<string, unknown>).CustomEvent = (
|
||||
win as unknown as { CustomEvent: unknown }
|
||||
).CustomEvent;
|
||||
const warnings: unknown[][] = [];
|
||||
const originalWarn = console.warn;
|
||||
console.warn = (...args: unknown[]) => warnings.push(args);
|
||||
try {
|
||||
(0, eval)(getReactiveRuntime(true));
|
||||
(
|
||||
win as unknown as { __wrnexusHydrateScopes?: (root: unknown) => void }
|
||||
).__wrnexusHydrateScopes?.(win.document);
|
||||
} finally {
|
||||
console.warn = originalWarn;
|
||||
}
|
||||
expect(warnings).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("production runtime strips development diagnostics", () => {
|
||||
const production = getReactiveRuntime();
|
||||
expect(production).not.toContain("WRN-DEV-");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/syntax",
|
||||
"version": "0.8.16",
|
||||
"version": "0.8.18",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -767,6 +767,28 @@ function expandStructuredStateDeclarations(lines, unit) {
|
||||
});
|
||||
}
|
||||
|
||||
function formatStructuredPropDeclaration(value, unit, depth, printWidth) {
|
||||
const match = /^([A-Za-z_$][\w$]*\??\s*(?::\s*[^=]+?)?\s*=\s*)([\[{][\s\S]*)$/.exec(
|
||||
value,
|
||||
);
|
||||
if (!match) return null;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(match[2].trim());
|
||||
const compact = JSON.stringify(parsed);
|
||||
const indentation = unit.repeat(depth);
|
||||
if (indentation.length + match[1].length + compact.length <= printWidth) return null;
|
||||
|
||||
const jsonLines = JSON.stringify(parsed, null, unit).split("\n");
|
||||
return [
|
||||
`${indentation}${match[1]}${jsonLines[0]}`,
|
||||
...jsonLines.slice(1).map((jsonLine) => `${indentation}${jsonLine}`),
|
||||
];
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function formatWrnPass(source: string, options: FormatWrnOptions = {}): string {
|
||||
const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4);
|
||||
|
||||
@@ -844,6 +866,18 @@ function formatWrnPass(source: string, options: FormatWrnOptions = {}): string {
|
||||
continue;
|
||||
}
|
||||
|
||||
const structuredProp = formatStructuredPropDeclaration(
|
||||
value,
|
||||
unit,
|
||||
codeDepth + htmlDepth + controlDepth,
|
||||
printWidth,
|
||||
);
|
||||
if (structuredProp) {
|
||||
output.push(...structuredProp);
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const leadingClosingBraces = countLeadingClosingBraces(value);
|
||||
|
||||
const closesControlBlock = isControlBlockClose(value);
|
||||
|
||||
@@ -2,6 +2,8 @@ export { Lexer, LexError, skipLiteralOrComment } from "./tokenizer.ts";
|
||||
export { formatWrn } from "./formatter.ts";
|
||||
export type { FormatWrnOptions } from "./formatter.ts";
|
||||
export { parse, parseHtmlView, ParseError, VOID_ELEMENTS } from "./parser.ts";
|
||||
export { extractInterpolations } from "./interpolation.ts";
|
||||
export type { InterpolationSegment } from "./interpolation.ts";
|
||||
export type {
|
||||
ActionBlock,
|
||||
ApiBlock,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
export interface InterpolationSegment {
|
||||
expression: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
/** Extract top-level `{...}` expressions while preserving nested object/array literals. */
|
||||
export function extractInterpolations(value: string): InterpolationSegment[] {
|
||||
const segments: InterpolationSegment[] = [];
|
||||
let index = 0;
|
||||
while (index < value.length) {
|
||||
if (value[index] !== "{") {
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
const start = index++;
|
||||
let depth = 1;
|
||||
let quote: string | null = null;
|
||||
while (index < value.length && depth > 0) {
|
||||
const char = value[index]!;
|
||||
if (quote) {
|
||||
if (char === "\\" && index + 1 < value.length) index += 2;
|
||||
else {
|
||||
if (char === quote) quote = null;
|
||||
index++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (char === '"' || char === "'" || char === "`") quote = char;
|
||||
else if (char === "{") depth++;
|
||||
else if (char === "}") depth--;
|
||||
index++;
|
||||
}
|
||||
if (depth === 0) {
|
||||
segments.push({ expression: value.slice(start + 1, index - 1).trim(), start, end: index });
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
@@ -83,3 +83,18 @@ test("preserves balanced compact sibling markup without indentation drift", () =
|
||||
expect(formatted).toContain('<div class="strip"><span>Page</span><b>→</b><span>API</span></div>');
|
||||
expect(formatWrn(formatted, { tabSize: 2, printWidth: 80 })).toBe(formatted);
|
||||
});
|
||||
|
||||
test("formats large typed JSON prop defaults and remains idempotent", () => {
|
||||
const source = `component Catalog {
|
||||
props {
|
||||
categories: unknown[] = [{"label":"Platform & Core","value":"platform","apps":[{"label":"WRNexus Home / Business Command Center","href":"/products/home","number":"01"},{"label":"SSO & Account","href":"/products/sso","number":"02"}]},{"label":"Sales & Customer","value":"sales","apps":[{"label":"CRM","href":"/products/crm","number":"11"}]}]
|
||||
}
|
||||
view { <div></div> }
|
||||
}`;
|
||||
const formatted = formatWrn(source, options);
|
||||
|
||||
expect(formatted).toContain(`categories: unknown[] = [\n {\n "label": "Platform & Core"`);
|
||||
expect(formatted).toContain(`"apps": [\n {`);
|
||||
expect(formatWrn(formatted, options)).toBe(formatted);
|
||||
expect(() => parse(formatted)).not.toThrow();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { diagnose, formatDiagnostic, parse } from "../src/index.ts";
|
||||
import { diagnose, extractInterpolations, formatDiagnostic, parse } from "../src/index.ts";
|
||||
|
||||
test("parses explicit rendering modes and the never hydration alias", () => {
|
||||
const ast = parse(`page Marketing {
|
||||
@@ -337,3 +337,14 @@ test("a block comment inside props is still refused with its own message", () =>
|
||||
`;
|
||||
expect(() => parse(source)).toThrow("Block comments are not allowed inside props");
|
||||
});
|
||||
|
||||
test("extracts nested object and array interpolation as one expression", () => {
|
||||
const value = `prefix {[{ label: 'One', meta: { count: 1 } }, { label: "Two" }]} suffix`;
|
||||
expect(extractInterpolations(value)).toEqual([
|
||||
{
|
||||
expression: `[{ label: 'One', meta: { count: 1 } }, { label: "Two" }]`,
|
||||
start: 7,
|
||||
end: value.length - 7,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/typecheck",
|
||||
"version": "0.8.15",
|
||||
"version": "0.8.16",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { dirname, join, normalize, resolve } from "node:path";
|
||||
import ts from "typescript";
|
||||
import {
|
||||
containsReadonlyPropMutation,
|
||||
extractInterpolations,
|
||||
inferredRuntimeType,
|
||||
parse,
|
||||
runtimeTypeOf,
|
||||
@@ -332,6 +333,9 @@ export function virtualTypeScriptModule(
|
||||
if (ast.dataApis.length) append(`declare const api: { ${apiType} };`);
|
||||
append(`declare const props: Readonly<${ast.name}Props>;`);
|
||||
append("declare const refs: Record<string, Element | null>;");
|
||||
append(
|
||||
"declare const toast: ((message: string, options?: Record<string, unknown>) => unknown) & { success(message: string, options?: Record<string, unknown>): unknown; error(message: string, options?: Record<string, unknown>): unknown; danger(message: string, options?: Record<string, unknown>): unknown; warning(message: string, options?: Record<string, unknown>): unknown; info(message: string, options?: Record<string, unknown>): unknown; dismiss(id: string | number): void; clear(): void };",
|
||||
);
|
||||
append(
|
||||
"declare function useFetch(path: string, method?: string | { method?: string; query?: unknown; params?: unknown; body?: unknown; data?: unknown }, input?: unknown): Promise<any>;",
|
||||
);
|
||||
@@ -382,7 +386,7 @@ export function virtualTypeScriptModule(
|
||||
for (const attr of node.attrs) {
|
||||
const expressions = attr.event
|
||||
? [attr.value]
|
||||
: [...attr.value.matchAll(/\{([^{}]+)\}/g)].map((match) => match[1]!.trim());
|
||||
: extractInterpolations(attr.value).map((segment) => segment.expression);
|
||||
for (const expression of expressions) {
|
||||
if (!expression.trim()) continue;
|
||||
const declarations = [
|
||||
|
||||
@@ -112,3 +112,32 @@ test("accepts handler payload, loop locals, and the public useFetch helper", ()
|
||||
);
|
||||
expect(diagnostics.filter((diagnostic) => diagnostic.code === "WRN-TYPE-2304")).toEqual([]);
|
||||
});
|
||||
|
||||
test("accepts multiline array props containing nested object literals", () => {
|
||||
const root = app();
|
||||
const diagnostics = checkWrnSource(
|
||||
`page Contact {
|
||||
view {
|
||||
<div data-component="StatsBar" items="{[
|
||||
{ label: 'Product catalog', value: '81', suffix: ' apps' },
|
||||
{ label: 'Starting point', value: 'One workflow' }
|
||||
]}"></div>
|
||||
}
|
||||
}`,
|
||||
{ appRoot: root, filePath: join(root, "app", "pages", "contact.wrn") },
|
||||
);
|
||||
expect(diagnostics.filter((diagnostic) => diagnostic.code.startsWith("WRN-TYPE-"))).toEqual([]);
|
||||
});
|
||||
|
||||
test("provides the toast API to view event expressions", () => {
|
||||
const root = app();
|
||||
const diagnostics = checkWrnSource(
|
||||
`page Contact {
|
||||
view {
|
||||
<form @wrn:success="toast.success('Sent', { title: 'Request sent' })"></form>
|
||||
}
|
||||
}`,
|
||||
{ appRoot: root, filePath: join(root, "app", "pages", "contact.wrn") },
|
||||
);
|
||||
expect(diagnostics.filter((diagnostic) => diagnostic.code.startsWith("WRN-TYPE-"))).toEqual([]);
|
||||
});
|
||||
|
||||
+15
-15
@@ -45,7 +45,7 @@ Secure multi-cell PIN and verification-code input with regex and paste support.
|
||||
Theme-aware, responsive strong password component.
|
||||
|
||||
- Mount: `data-component="StrongPassword"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Password"`, `name: string = "password"`, `value: string = ""`, `placeholder: string = "Create a strong password"`, `autocomplete: string = "new-password"`, `minLength: number = 8`, `specialCharactersSet: string = "!@#$%^&*()_+-=[]{}|;:,.<>?"`, `requireLowercase: boolean = true`, `requireUppercase: boolean = true`, `requireNumber: boolean = true`, `requireSpecialCharacter: boolean = true`, `showRequirements: boolean = true`, `presentation: string = "inline"`, `hintText: string = "Use a unique password you do not use elsewhere."`, `emptyLabel: string = "Enter a password"`, `weakLabel: string = "Weak"`, `fairLabel: string = "Fair"`, `goodLabel: string = "Good"`, `strongLabel: string = "Strong"`, `disabled: boolean = false`, `readonly: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `validationMessage: string = ""`, `class: string = ""`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Password"`, `name: string = "password"`, `value: string = ""`, `placeholder: string = "Create a strong password"`, `autocomplete: string = "new-password"`, `minLength: number = 8`, `specialCharactersSet: string = "!@#$%^&*()_+-=[]{}|;:,.<>?"`, `requireLowercase: boolean = true`, `requireUppercase: boolean = true`, `requireNumber: boolean = true`, `requireSpecialCharacter: boolean = true`, `showRequirements: boolean = true`, `presentation: string = "inline"`, `hintText: string = "Use a unique password you do not use elsewhere."`, `emptyLabel: string = "Enter a password"`, `weakLabel: string = "Weak"`, `fairLabel: string = "Fair"`, `goodLabel: string = "Good"`, `strongLabel: string = "Strong"`, `disabled: boolean = false`, `readonly: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `validationMessage: string = ""`, `showLabel: string = "Show password"`, `hideLabel: string = "Hide password"`, `class: string = ""`
|
||||
- Slots: None
|
||||
- Outputs: `input({ value: string; score: number; maximumScore: number; percent: number; level: string })`, `change({ value: string; score: number; maximumScore: number; percent: number; level: string })`, `strength({ value: string; score: number; maximumScore: number; percent: number; level: string })`
|
||||
|
||||
@@ -146,7 +146,7 @@ Theme-aware, responsive button group component.
|
||||
Group related content in a responsive themed surface with title, description, content, and supporting slots.
|
||||
|
||||
- Mount: `data-component="Card"`
|
||||
- Props: `title: string = "Card title"`, `subtitle: string = ""`, `description: string = ""`, `header: string = ""`, `footer: string = ""`, `imageSrc: string = ""`, `imageAlt: string = ""`, `imagePosition: string = "top"`, `actionLabel: string = ""`, `actionHref: string = ""`, `headerActions: unknown[] = []`, `navigation: unknown[] = []`, `activeNav: string = ""`, `mobileNavigation: boolean = false`, `alertTitle: string = ""`, `alertDescription: string = ""`, `empty: boolean = false`, `emptyTitle: string = "No data to show"`, `emptyIcon: string = "icon-[lucide--inbox]"`, `items: unknown[] = []`, `size: string = "md"`, `color: string = "primary"`, `variant: string = "default"`, `layout: string = "vertical"`, `align: string = "left"`, `hover: string = "none"`, `scrollable: boolean = false`, `maxHeight: string = "18rem"`, `dismissible: boolean = false`, `ariaLabel: string = ""`, `class: string = ""`
|
||||
- Props: `title: string = ""`, `icon: string = ""`, `titleKey: string = ""`, `subtitle: string = ""`, `subtitleKey: string = ""`, `description: string = ""`, `descriptionKey: string = ""`, `header: string = ""`, `footer: string = ""`, `imageSrc: string = ""`, `imageAlt: string = ""`, `imagePosition: string = "top"`, `actionLabel: string = ""`, `actionLabelKey: string = ""`, `actionHref: string = ""`, `headerActions: unknown[] = []`, `navigation: unknown[] = []`, `activeNav: string = ""`, `mobileNavigation: boolean = false`, `alertTitle: string = ""`, `alertDescription: string = ""`, `empty: boolean = false`, `emptyTitle: string = "No data to show"`, `emptyIcon: string = "icon-[lucide--inbox]"`, `items: unknown[] = []`, `size: string = "md"`, `color: string = "primary"`, `variant: string = "default"`, `layout: string = "vertical"`, `align: string = "left"`, `hover: string = "none"`, `radius: string = "default"`, `shadow: string = "default"`, `equalHeight: boolean = false`, `scrollable: boolean = false`, `maxHeight: string = "18rem"`, `dismissible: boolean = false`, `ariaLabel: string = ""`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `click({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `action({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `navigate({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `dismiss({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `load({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `error({ error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)`
|
||||
|
||||
@@ -337,7 +337,7 @@ Reusable portal dashboard component.
|
||||
Reusable preference switcher component.
|
||||
|
||||
- Mount: `data-component="PreferenceSwitcher"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `themeLabel: string = "Theme"`, `colorLabel: string = "Accent color"`, `languageLabel: string = "Language"`, `languages: string = [`, `colors: string = [`, `compact: boolean = true`, `class: string = ""`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `themeLabel: string = "Theme"`, `colorLabel: string = "Accent color"`, `languageLabel: string = "Language"`, `languages: string = [`, `colors: string = [`, `compact: boolean = true`, `iconOnly: boolean = true`, `themeIcon: string = "icon-[lucide--sun-moon]"`, `colorIcon: string = "icon-[lucide--palette]"`, `languageIcon: string = "icon-[lucide--languages]"`, `panelPlacement: string = "bottom-end"`, `mobilePanel: string = "dropdown"`, `showCurrentLanguage: boolean = true`, `class: string = ""`
|
||||
- Slots: None
|
||||
- Outputs: `theme({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `color({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `language({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`
|
||||
|
||||
@@ -375,7 +375,7 @@ Arrange operational metrics, KPIs, public statistics, or service indicators in a
|
||||
Present a compact responsive strip of key facts, counts, performance indicators, or trust signals.
|
||||
|
||||
- Mount: `data-component="StatsBar"`
|
||||
- Props: `items: unknown[] = []`, `columns: number = 4`, `compact: boolean = true`, `dividers: boolean = true`, `icons: boolean = true`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "raised"`, `maxWidth: string = "xl"`, `class: string = ""`
|
||||
- Props: `items: unknown[] = []`, `columns: number = 4`, `compact: boolean = true`, `dividers: boolean = true`, `icons: boolean = true`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "raised"`, `maxWidth: string = "xl"`, `radius: string = "default"`, `shadow: string = "default"`, `gutter: string = "md"`, `outerSpacing: string = "none"`, `mobileColumns: number = 1`, `class: string = ""`
|
||||
- Slots: None
|
||||
- Outputs: None
|
||||
|
||||
@@ -606,7 +606,7 @@ Arrange feature cards, services, solutions, or benefits in a responsive equal-he
|
||||
Render structured responsive footer navigation, pre and post content, copyright content, links, and public events.
|
||||
|
||||
- Mount: `data-component="Footer"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Footer navigation"`, `items: unknown[] = []`, `columns: number = 3`, `maxWidth: string = "compact"`, `copyright: string = ""`, `class: string = ""`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Footer navigation"`, `items: unknown[] = []`, `columns: number = 3`, `maxWidth: string = "compact"`, `mobileColumns: number = 2`, `mobileCopyrightAlign: string = "center"`, `copyright: string = ""`, `copyrightKey: string = ""`, `translationPrefix: string = ""`, `class: string = ""`
|
||||
- Slots: `pre-footer`, `post-footer`, `copyright-left`, `copyright-right`
|
||||
- Outputs: `select({ 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)`, `action({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
|
||||
@@ -698,7 +698,7 @@ Apply consistent readable responsive typography, widths, columns, spacing, and e
|
||||
Publish a responsive notice with badge, icon, supporting copy, action, dismiss behavior, and width controls.
|
||||
|
||||
- Mount: `data-component="AnnouncementBar"`
|
||||
- Props: `badge: string = ""`, `badgeIcon: string = ""`, `message: string = "Announcement"`, `description: string = ""`, `icon: string = "icon-[lucide--megaphone]"`, `actionLabel: string = ""`, `actionHref: string = ""`, `actionIcon: string = ""`, `dismissible: boolean = false`, `dismissLabel: string = "Dismiss announcement"`, `sticky: boolean = false`, `compact: boolean = false`, `size: string = "default"`, `width: string = "default"`, `color: string = "primary"`, `variant: string = "soft"`, `role: string = "status"`, `live: string = "polite"`, `class: string = ""`
|
||||
- Props: `badge: string = ""`, `badgeIcon: string = ""`, `message: string = "Announcement"`, `description: string = ""`, `icon: string = "icon-[lucide--megaphone]"`, `actionLabel: string = ""`, `actionHref: string = ""`, `actionIcon: string = ""`, `dismissible: boolean = false`, `dismissLabel: string = "Dismiss announcement"`, `sticky: boolean = false`, `compact: boolean = false`, `flush: boolean = false`, `mobileCompact: boolean = false`, `mobileMessageLines: number = 2`, `hideBadgeOnMobile: boolean = false`, `hideDescriptionOnMobile: boolean = false`, `mobileActionSize: string = "default"`, `gutter: string = "md"`, `contentGutter: string = "md"`, `flushMobile: boolean = false`, `size: string = "default"`, `width: string = "default"`, `color: string = "primary"`, `variant: string = "soft"`, `role: string = "status"`, `live: string = "polite"`, `class: string = ""`
|
||||
- Slots: None
|
||||
- Outputs: `dismiss({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
|
||||
@@ -707,7 +707,7 @@ Publish a responsive notice with badge, icon, supporting copy, action, dismiss b
|
||||
Close a page or major section with conversion-focused copy, actions, and optional supporting visual content.
|
||||
|
||||
- Mount: `data-component="CTASection"`
|
||||
- Props: `eyebrow: string = ""`, `title: string = "Ready to get started?"`, `description: string = ""`, `icon: string = ""`, `align: string = "center"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "solid"`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualPosition: string = "right"`, `maxWidth: string = "xl"`, `fullBleed: boolean = false`, `class: string = ""`
|
||||
- Props: `eyebrow: string = ""`, `title: string = "Ready to get started?"`, `description: string = ""`, `icon: string = ""`, `align: string = "center"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "solid"`, `spacing: string = "none"`, `radius: string = "default"`, `titleMaxWidth: string = ""`, `contentPadding: string = "default"`, `mobilePadding: string = "default"`, `mobileActions: string = "stack"`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualPosition: string = "right"`, `maxWidth: string = "xl"`, `fullBleed: boolean = false`, `class: string = ""`
|
||||
- Slots: `default`, `actions`, `visual`, `footer`
|
||||
- Outputs: None
|
||||
|
||||
@@ -725,7 +725,7 @@ Present one linked feature or service with media, icon, badge, description, and
|
||||
Present a compact feature or benefit with a styled icon, title, description, badge, and optional link.
|
||||
|
||||
- Mount: `data-component="FeatureIconCard"`
|
||||
- Props: `icon: string = "icon-[lucide--sparkles]"`, `iconSize: string = "md"`, `iconVariant: string = "soft"`, `title: string = "Feature"`, `description: string = ""`, `href: string = ""`, `actionLabel: string = "Explore"`, `badge: string = ""`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "default"`, `align: string = "left"`, `hover: string = "lift"`, `class: string = ""`
|
||||
- Props: `icon: string = "icon-[lucide--sparkles]"`, `iconSize: string = "md"`, `iconVariant: string = "soft"`, `title: string = "Feature"`, `description: string = ""`, `href: string = ""`, `actionLabel: string = "Explore"`, `badge: string = ""`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "default"`, `align: string = "left"`, `hover: string = "lift"`, `radius: string = "default"`, `shadow: string = "default"`, `clickableSurface: boolean = false`, `descriptionTone: string = "default"`, `class: string = ""`
|
||||
- Slots: `default`, `footer`
|
||||
- Outputs: None
|
||||
|
||||
@@ -770,7 +770,7 @@ Introduce an internal or public page with breadcrumbs, icon, title, description,
|
||||
Build a responsive two-column introduction balancing descriptive content with a visual, image, or structured data panel.
|
||||
|
||||
- Mount: `data-component="SplitHero"`
|
||||
- Props: `eyebrow: string = ""`, `eyebrowIcon: string = "icon-[lucide--sparkles]"`, `title: string = "A better digital experience"`, `highlight: string = ""`, `description: string = ""`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `visualPosition: string = "right"`, `reverse: boolean = false`, `ratio: string = "balanced"`, `align: string = "left"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "default"`, `maxWidth: string = "xl"`, `fullBleed: boolean = true`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualEyebrow: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualStyle: string = "panel"`, `trustItems: unknown[] = []`, `class: string = ""`
|
||||
- Props: `eyebrow: string = ""`, `eyebrowIcon: string = "icon-[lucide--sparkles]"`, `title: string = "A better digital experience"`, `highlight: string = ""`, `description: string = ""`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `visualPosition: string = "right"`, `reverse: boolean = false`, `ratio: string = "balanced"`, `align: string = "left"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "default"`, `maxWidth: string = "xl"`, `fullBleed: boolean = true`, `gutter: string = "md"`, `contentGutter: string = "md"`, `flushMobile: boolean = false`, `titleSize: string = "default"`, `titleMaxWidth: string = ""`, `descriptionWeight: string = "default"`, `mobileTitleSize: string = "default"`, `mobileStack: boolean = true`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualEyebrow: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualStyle: string = "panel"`, `trustItems: unknown[] = []`, `class: string = ""`
|
||||
- Slots: `default`, `actions`, `trust`, `visual`
|
||||
- Outputs: None
|
||||
|
||||
@@ -799,7 +799,7 @@ Provide a responsive floating control that returns long pages to the top and can
|
||||
Show responsive hierarchical navigation with home support, separators, current-page state, sizes, and selection events.
|
||||
|
||||
- Mount: `data-component="Breadcrumb"`
|
||||
- Props: `label: string = "Breadcrumb"`, `items: unknown[] = []`, `active: string = ""`, `separator: string = "chevron"`, `showHome: boolean = false`, `homeLabel: string = "Home"`, `homeHref: string = "/"`, `homeIcon: string = "icon-[lucide--house]"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "minimal"`, `class: string = ""`
|
||||
- Props: `label: string = "Breadcrumb"`, `items: unknown[] = []`, `active: string = ""`, `activeIcon: string = "icon-[lucide--file-text]"`, `separator: string = "chevron"`, `showHome: boolean = false`, `homeLabel: string = "Home"`, `homeHref: string = "/"`, `homeIcon: string = "icon-[lucide--house]"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "minimal"`, `class: string = ""`
|
||||
- Slots: None
|
||||
- Outputs: `select({ 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)`
|
||||
|
||||
@@ -808,9 +808,9 @@ Show responsive hierarchical navigation with home support, separators, current-p
|
||||
Theme-aware, responsive mega menu component.
|
||||
|
||||
- Mount: `data-component="MegaMenu"`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `label: string = "Menu"`, `icon: string = ""`, `columns: unknown[] = []`, `footer: string = ""`, `defaultOpen: boolean = false`, `class: string = ""`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `label: string = "Menu"`, `icon: string = ""`, `columns: unknown[] = []`, `rail: unknown[] = []`, `featured: Record<string, unknown> = {}`, `variant: string = "default"`, `density: string = "default"`, `panelWidth: string = "xl"`, `panelAlign: string = "start"`, `columnCount: number = 0`, `fullWidth: boolean = false`, `openOnHover: boolean = true`, `activeCategory: string = ""`, `actionLabel: string = ""`, `actionHref: string = ""`, `actionIcon: string = "icon-[lucide--arrow-right]"`, `footer: string = ""`, `defaultOpen: boolean = false`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `open({ sourceEvent: Event })`, `close({ reason: string })`, `select({ item: object; value: string })`
|
||||
- Outputs: `open({ sourceEvent: Event })`, `close({ reason: string })`, `select({ item: object; value: string })`, `category({ item: object; value: string; index: number })`
|
||||
|
||||
### Nav
|
||||
|
||||
@@ -826,8 +826,8 @@ Theme-aware, responsive nav component.
|
||||
Theme-aware, responsive navbar component.
|
||||
|
||||
- Mount: `data-component="Navbar"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Primary navigation"`, `topbarLabel: string = "Utility navigation"`, `brand: Record<string, unknown> = {}`, `items: unknown[] = []`, `actions: unknown[] = []`, `active: string = ""`, `sticky: boolean = false`, `openOnHover: boolean = false`, `maxWidth: string = "full"`, `mobileLabel: string = "Toggle navigation"`, `class: string = ""`
|
||||
- Slots: `topbar`, `actions`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Primary navigation"`, `topbarLabel: string = "Utility navigation"`, `brand: Record<string, unknown> = {}`, `items: unknown[] = []`, `actions: unknown[] = []`, `active: string = ""`, `sticky: boolean = false`, `floating: boolean = false`, `startFloating: string = "0px"`, `openOnHover: boolean = false`, `maxWidth: string = "full"`, `mobileLabel: string = "Toggle navigation"`, `collapseAt: string = "900px"`, `mobilePanel: string = "content"`, `mobileUtilitiesPosition: string = "header"`, `gutter: string = "md"`, `contentGutter: string = "md"`, `flushMobile: boolean = false`, `translationPrefix: string = ""`, `class: string = ""`
|
||||
- Slots: `topbar`, `actions`, `utilities`
|
||||
- Outputs: `toggle({ open: boolean })`, `open({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `close({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `select({ item: string | number | boolean | null | object; value: string | number | boolean; level: string })`, `action({ item: string | number | boolean | null | object; value: string | number | boolean })`
|
||||
|
||||
### Pagination
|
||||
@@ -844,7 +844,7 @@ Theme-aware, responsive pagination component.
|
||||
Theme-aware, responsive scrollspy component.
|
||||
|
||||
- Mount: `data-component="Scrollspy"`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `items: unknown[] = []`, `active: string = ""`, `label: string = "On this page"`, `heading: string = ""`, `class: string = ""`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `items: unknown[] = []`, `active: string = ""`, `label: string = "On this page"`, `heading: string = ""`, `sticky: boolean = false`, `topOffset: string = "6rem"`, `sectionOffset: string = "7rem"`, `collapseAt: string = "800px"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `change({ href: string; label: string })`
|
||||
|
||||
|
||||
@@ -90,6 +90,62 @@ the component directory at runtime.
|
||||
For the full catalog, see [`COMPONENTS.md`](./COMPONENTS.md). The machine-readable
|
||||
equivalent is exported as `@wrnexus/ui/component-reference.json`.
|
||||
|
||||
### Typed Navbar items
|
||||
|
||||
Keep navigation data in a shared TypeScript module and validate it with the
|
||||
package contract. `NavbarItem` is a discriminated union of `link`, `dropdown`,
|
||||
and `mega`; a mega item accepts every `MegaMenu` layout option.
|
||||
|
||||
```ts
|
||||
import type { NavbarItem } from "@wrnexus/ui";
|
||||
|
||||
export const navigation = [
|
||||
{ type: "link", label: "Pricing", href: "/pricing" },
|
||||
{
|
||||
type: "dropdown",
|
||||
label: "Company",
|
||||
items: [
|
||||
{ label: "About", href: "/about" },
|
||||
{ label: "Contact", href: "/contact" },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "mega",
|
||||
label: "Products",
|
||||
mega: {
|
||||
variant: "icon-grid",
|
||||
panelWidth: "2xl",
|
||||
columnCount: 3,
|
||||
rail: [{ label: "All products", href: "/products" }],
|
||||
columns: [
|
||||
{
|
||||
heading: "Work",
|
||||
items: [
|
||||
{
|
||||
label: "Projects",
|
||||
href: "/products/projects",
|
||||
icon: "icon-[lucide--folders]",
|
||||
description: "Plan and deliver work",
|
||||
badge: "New",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
featured: {
|
||||
title: "What's new",
|
||||
actionLabel: "Explore",
|
||||
actionHref: "/new",
|
||||
},
|
||||
actionLabel: "View all products",
|
||||
actionHref: "/products",
|
||||
},
|
||||
},
|
||||
] satisfies NavbarItem[];
|
||||
```
|
||||
|
||||
Importing from `@wrnexus/ui/navigation` is also supported. Existing untyped
|
||||
Navbar data using `children` remains accepted at runtime for migration.
|
||||
|
||||
## API
|
||||
|
||||
The JS module (`@wrnexus/ui`) exposes five helpers used by the build tooling to
|
||||
|
||||
@@ -806,6 +806,69 @@
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "flush",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileCompact",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileMessageLines",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "2",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "hideBadgeOnMobile",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "hideDescriptionOnMobile",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileActionSize",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "gutter",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"md\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "contentGutter",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"md\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "flushMobile",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
@@ -2180,6 +2243,48 @@
|
||||
"default": "\"solid\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "spacing",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"none\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "radius",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "titleMaxWidth",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "contentPadding",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobilePadding",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileActions",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"stack\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "primaryLabel",
|
||||
"type": "string",
|
||||
@@ -2315,7 +2420,21 @@
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Card title\"",
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "icon",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "titleKey",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -2325,6 +2444,13 @@
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "subtitleKey",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "string",
|
||||
@@ -2332,6 +2458,13 @@
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "descriptionKey",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "header",
|
||||
"type": "string",
|
||||
@@ -2374,6 +2507,13 @@
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "actionLabelKey",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "actionHref",
|
||||
"type": "string",
|
||||
@@ -2493,6 +2633,27 @@
|
||||
"default": "\"none\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "radius",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "shadow",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "equalHeight",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "scrollable",
|
||||
"type": "boolean",
|
||||
@@ -5833,6 +5994,34 @@
|
||||
"default": "\"lift\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "radius",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "shadow",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "clickableSurface",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "descriptionTone",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -6198,6 +6387,20 @@
|
||||
"default": "\"compact\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileColumns",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "2",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileCopyrightAlign",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"center\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "copyright",
|
||||
"type": "string",
|
||||
@@ -6205,6 +6408,20 @@
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "copyrightKey",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "translationPrefix",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -8128,6 +8345,97 @@
|
||||
"default": "[]",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "rail",
|
||||
"type": "unknown[]",
|
||||
"required": false,
|
||||
"default": "[]",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "featured",
|
||||
"type": "Record<string, unknown>",
|
||||
"required": false,
|
||||
"default": "{}",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "variant",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "density",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "panelWidth",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"xl\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "panelAlign",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"start\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "columnCount",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "0",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "fullWidth",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "openOnHover",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "activeCategory",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "actionLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "actionHref",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "actionIcon",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"icon-[lucide--arrow-right]\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "footer",
|
||||
"type": "string",
|
||||
@@ -8163,9 +8471,13 @@
|
||||
{
|
||||
"name": "select",
|
||||
"payloadType": "{ item: object; value: string }"
|
||||
},
|
||||
{
|
||||
"name": "category",
|
||||
"payloadType": "{ item: object; value: string; index: number }"
|
||||
}
|
||||
],
|
||||
"events": ["open", "close", "select"],
|
||||
"events": ["open", "close", "select", "category"],
|
||||
"source": "components/MegaMenu.wrn"
|
||||
},
|
||||
{
|
||||
@@ -8878,6 +9190,20 @@
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "floating",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "startFloating",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"0px\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "openOnHover",
|
||||
"type": "boolean",
|
||||
@@ -8899,6 +9225,55 @@
|
||||
"default": "\"Toggle navigation\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "collapseAt",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"900px\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobilePanel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"content\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileUtilitiesPosition",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"header\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "gutter",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"md\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "contentGutter",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"md\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "flushMobile",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "translationPrefix",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -8907,7 +9282,7 @@
|
||||
"options": []
|
||||
}
|
||||
],
|
||||
"slots": ["topbar", "actions"],
|
||||
"slots": ["topbar", "actions", "utilities"],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "toggle",
|
||||
@@ -9826,6 +10201,55 @@
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "iconOnly",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "themeIcon",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"icon-[lucide--sun-moon]\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "colorIcon",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"icon-[lucide--palette]\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "languageIcon",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"icon-[lucide--languages]\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "panelPlacement",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"bottom-end\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobilePanel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"dropdown\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "showCurrentLanguage",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -10532,6 +10956,34 @@
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "sticky",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "topOffset",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"6rem\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "sectionOffset",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"7rem\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "collapseAt",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"800px\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -11346,6 +11798,62 @@
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "gutter",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"md\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "contentGutter",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"md\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "flushMobile",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "titleSize",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "titleMaxWidth",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "descriptionWeight",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileTitleSize",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileStack",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "backgroundImage",
|
||||
"type": "string",
|
||||
@@ -11498,6 +12006,41 @@
|
||||
"default": "\"xl\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "radius",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "shadow",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "gutter",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"md\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "outerSpacing",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"none\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileColumns",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "1",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -11844,6 +12387,20 @@
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "showLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Show password\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "hideLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Hide password\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
|
||||
@@ -16,6 +16,15 @@ badge: string = ""
|
||||
dismissLabel: string = "Dismiss announcement"
|
||||
sticky: boolean = false
|
||||
compact: boolean = false
|
||||
flush: boolean = false
|
||||
mobileCompact: boolean = false
|
||||
mobileMessageLines: number = 2
|
||||
hideBadgeOnMobile: boolean = false
|
||||
hideDescriptionOnMobile: boolean = false
|
||||
mobileActionSize: string = "default"
|
||||
gutter: string = "md"
|
||||
contentGutter: string = "md"
|
||||
flushMobile: boolean = false
|
||||
size: string = "default"
|
||||
width: string = "default"
|
||||
color: string = "primary"
|
||||
@@ -36,6 +45,7 @@ badge: string = ""
|
||||
data-color='{color}'
|
||||
data-variant='{variant}'
|
||||
data-compact='{compact}'
|
||||
data-flush='{flush ? "true" : "false"}' data-mobile-compact='{mobileCompact ? "true" : "false"}' data-mobile-message-lines='{mobileMessageLines}' data-hide-badge-mobile='{hideBadgeOnMobile ? "true" : "false"}' data-hide-description-mobile='{hideDescriptionOnMobile ? "true" : "false"}' data-mobile-action-size='{mobileActionSize}' data-gutter='{gutter}' data-content-gutter='{contentGutter}' data-flush-mobile='{flushMobile ? "true" : "false"}'
|
||||
data-sticky='{sticky}'
|
||||
data-show='{!dismissed}'
|
||||
aria-hidden='{dismissed ? "true" : "false"}'
|
||||
@@ -152,9 +162,9 @@ badge: string = ""
|
||||
position: relative;
|
||||
z-index: 40;
|
||||
display: block;
|
||||
width: 100vw;
|
||||
max-width: 100vw;
|
||||
margin-inline: calc(50% - 50vw);
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-inline: 0;
|
||||
color: var(--wrn-color-text);
|
||||
}
|
||||
|
||||
@@ -163,6 +173,8 @@ badge: string = ""
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.wrn-announcement[data-flush="true"] .wrn-announcement__surface { border-radius: 0; box-shadow: none; }
|
||||
|
||||
.wrn-announcement[data-width="wide"] .wrn-announcement__surface {
|
||||
width: min(calc(100% - 2rem), 90rem);
|
||||
}
|
||||
@@ -573,6 +585,15 @@ badge: string = ""
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.wrn-announcement[data-mobile-compact="true"] .wrn-announcement__inner,
|
||||
.wrn-announcement[data-compact="true"] .wrn-announcement__inner { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0.65rem; }
|
||||
.wrn-announcement[data-hide-badge-mobile="true"] .wrn-announcement__badge,
|
||||
.wrn-announcement[data-mobile-compact="true"] .wrn-announcement__badge { display: none; }
|
||||
.wrn-announcement[data-hide-description-mobile="true"] .wrn-announcement__description,
|
||||
.wrn-announcement[data-mobile-compact="true"] .wrn-announcement__description { display: none; }
|
||||
.wrn-announcement[data-mobile-message-lines="2"] .wrn-announcement__message { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
|
||||
.wrn-announcement[data-mobile-action-size="sm"] .wrn-announcement__action { min-height: 2.25rem; padding-inline: 0.7rem; }
|
||||
.wrn-announcement[data-flush-mobile="false"] .wrn-announcement__inner { padding-inline: 1rem; }
|
||||
.wrn-announcement__surface,
|
||||
.wrn-announcement[data-width="wide"] .wrn-announcement__surface {
|
||||
width: calc(100% - 1rem);
|
||||
|
||||
@@ -120,11 +120,11 @@ component BackToTop {
|
||||
}
|
||||
|
||||
.wrn-back-to-top[data-position="right"] {
|
||||
right: 1.5rem;
|
||||
right: max(1.5rem, env(safe-area-inset-right));
|
||||
}
|
||||
|
||||
.wrn-back-to-top[data-position="left"] {
|
||||
left: 1.5rem;
|
||||
left: max(1.5rem, env(safe-area-inset-left));
|
||||
}
|
||||
|
||||
.wrn-back-to-top[data-position="center"] {
|
||||
|
||||
@@ -12,6 +12,7 @@ component Blockquote {
|
||||
variant: string = "default"
|
||||
quoteMark: boolean = true
|
||||
italic: boolean = true
|
||||
maxWidth: string = "xl"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
@@ -24,6 +25,7 @@ component Blockquote {
|
||||
data-align="{align}"
|
||||
data-variant="{variant}"
|
||||
data-italic="{italic}"
|
||||
data-max-width="{maxWidth}"
|
||||
>
|
||||
<blockquote cite="{citationUrl}">
|
||||
{#if quoteMark}
|
||||
@@ -73,10 +75,17 @@ component Blockquote {
|
||||
display: grid;
|
||||
gap: 1.25rem;
|
||||
width: 100%;
|
||||
max-width: var(--wrn-blockquote-max-width, 48rem);
|
||||
color: var(--wrn-color-text);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.wrn-next--blockquote[data-max-width="sm"] { --wrn-blockquote-max-width: 32rem; }
|
||||
.wrn-next--blockquote[data-max-width="md"] { --wrn-blockquote-max-width: 40rem; }
|
||||
.wrn-next--blockquote[data-max-width="lg"] { --wrn-blockquote-max-width: 48rem; }
|
||||
.wrn-next--blockquote[data-max-width="xl"] { --wrn-blockquote-max-width: 64rem; }
|
||||
.wrn-next--blockquote[data-max-width="full"] { --wrn-blockquote-max-width: 100%; }
|
||||
|
||||
.wrn-next--blockquote[data-size="xs"] {
|
||||
--wrn-blockquote-font-size: 0.875rem;
|
||||
--wrn-blockquote-mark-size: 3rem;
|
||||
@@ -189,8 +198,19 @@ component Blockquote {
|
||||
|
||||
.wrn-next--blockquote[data-align="center"] {
|
||||
text-align: center;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.wrn-next--blockquote:is([data-variant="contained"], [data-variant="spotlight"], [data-variant="card"]) {
|
||||
padding: clamp(1.5rem, 4vw, 3rem);
|
||||
border: 1px solid color-mix(in srgb, var(--wrn-blockquote-color) 24%, var(--wrn-color-border));
|
||||
border-radius: calc(var(--wrn-radius) * 2);
|
||||
background: linear-gradient(145deg, color-mix(in srgb, var(--wrn-blockquote-color) 8%, var(--wrn-color-surface-raised)), var(--wrn-color-surface-raised));
|
||||
box-shadow: var(--wrn-shadow-1);
|
||||
}
|
||||
|
||||
.wrn-next--blockquote[data-variant="spotlight"] > blockquote { padding-top: calc(var(--wrn-blockquote-mark-size) * 0.55); }
|
||||
|
||||
.wrn-next--blockquote[data-align="right"],
|
||||
.wrn-next--blockquote[data-align="end"] {
|
||||
text-align: right;
|
||||
|
||||
@@ -7,6 +7,7 @@ component Breadcrumb {
|
||||
label: string = "Breadcrumb"
|
||||
items: unknown[] = []
|
||||
active: string = ""
|
||||
activeIcon: string = "icon-[lucide--file-text]"
|
||||
separator: string = "chevron"
|
||||
showHome: boolean = false
|
||||
homeLabel: string = "Home"
|
||||
@@ -61,7 +62,7 @@ label: string = "Breadcrumb"
|
||||
{:else if separator === "arrow"}
|
||||
<span>→</span>
|
||||
{:else}
|
||||
<span class="icon-[lucide--chevron-right] wrn-breadcrumb__separator-icon"></span>
|
||||
<span class="wrn-breadcrumb__separator-icon">›</span>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -99,6 +100,30 @@ label: string = "Breadcrumb"
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
{#if active && !items.some((item) => active === (item.value || item.label || item.title))}
|
||||
<li class="wrn-breadcrumb__item" data-current="true">
|
||||
{#if showHome || items.length > 0}
|
||||
<span class="wrn-breadcrumb__separator" aria-hidden="true">
|
||||
{#if separator === "slash"}
|
||||
<span>/</span>
|
||||
{:else if separator === "dot"}
|
||||
<span>•</span>
|
||||
{:else if separator === "arrow"}
|
||||
<span>→</span>
|
||||
{:else}
|
||||
<span class="wrn-breadcrumb__separator-icon">›</span>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
<span class="wrn-breadcrumb__current" aria-current="page">
|
||||
{#if activeIcon}
|
||||
<span class='{activeIcon} wrn-breadcrumb__icon' aria-hidden="true"></span>
|
||||
{/if}
|
||||
<span class="wrn-breadcrumb__label">{active}</span>
|
||||
</span>
|
||||
</li>
|
||||
{/if}
|
||||
</ol>
|
||||
</nav>
|
||||
}
|
||||
@@ -207,8 +232,14 @@ label: string = "Breadcrumb"
|
||||
}
|
||||
|
||||
.wrn-breadcrumb__separator-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 0.9rem;
|
||||
height: 0.9rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.wrn-breadcrumb__link,
|
||||
|
||||
@@ -8,6 +8,12 @@ component CTASection {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
variant: string = "solid"
|
||||
spacing: string = "none"
|
||||
radius: string = "default"
|
||||
titleMaxWidth: string = ""
|
||||
contentPadding: string = "default"
|
||||
mobilePadding: string = "default"
|
||||
mobileActions: string = "stack"
|
||||
primaryLabel: string = "Get started"
|
||||
primaryHref: string = "#"
|
||||
primaryIcon: string = ""
|
||||
@@ -35,6 +41,9 @@ component CTASection {
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-variant='{variant}'
|
||||
data-spacing='{spacing}'
|
||||
data-radius='{radius}' data-content-padding='{contentPadding}' data-mobile-padding='{mobilePadding}' data-mobile-actions='{mobileActions}'
|
||||
style='--wrn-cta-title-max-width: {titleMaxWidth || "18ch"}'
|
||||
data-max-width='{maxWidth}'
|
||||
data-full-bleed='{fullBleed ? "true" : "false"}'
|
||||
data-visual-position='{visualPosition}'
|
||||
@@ -186,6 +195,18 @@ component CTASection {
|
||||
--cta-contrast: var(--wrn-color-secondary-contrast);
|
||||
}
|
||||
|
||||
.wrn-cta-section[data-spacing="sm"] {
|
||||
padding-block: 2rem;
|
||||
}
|
||||
|
||||
.wrn-cta-section[data-spacing="md"] {
|
||||
padding-block: 3rem;
|
||||
}
|
||||
|
||||
.wrn-cta-section[data-spacing="lg"] {
|
||||
padding-block: 4rem;
|
||||
}
|
||||
|
||||
.wrn-cta-section[data-color="info"] {
|
||||
--cta-accent: var(--wrn-color-info);
|
||||
--cta-accent-hover: var(--wrn-color-info-hover);
|
||||
@@ -223,6 +244,20 @@ component CTASection {
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.wrn-cta-section[data-spacing="sm"] {
|
||||
padding-block: 1.5rem;
|
||||
}
|
||||
|
||||
.wrn-cta-section[data-spacing="md"] {
|
||||
padding-block: 2rem;
|
||||
}
|
||||
|
||||
.wrn-cta-section[data-spacing="lg"] {
|
||||
padding-block: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.wrn-cta-section[data-max-width="compact"] .wrn-cta-section__outer {
|
||||
width: min(calc(100% - 2rem), 64rem);
|
||||
}
|
||||
@@ -260,6 +295,8 @@ component CTASection {
|
||||
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
|
||||
0 24px 70px color-mix(in srgb, black 15%, transparent);
|
||||
}
|
||||
.wrn-cta-section[data-radius="xl"] .wrn-cta-section__surface { border-radius: 1.5rem; }
|
||||
.wrn-cta-section[data-content-padding="lg"] .wrn-cta-section__surface { padding: 3.5rem clamp(1.5rem, 5vw, 4rem); }
|
||||
|
||||
.wrn-cta-section[data-size="compact"] .wrn-cta-section__surface,
|
||||
.wrn-cta-section[data-size="sm"] .wrn-cta-section__surface {
|
||||
@@ -401,7 +438,7 @@ component CTASection {
|
||||
}
|
||||
|
||||
.wrn-cta-section__title {
|
||||
max-width: 18ch;
|
||||
max-width: var(--wrn-cta-title-max-width);
|
||||
margin: 0.75rem 0 0;
|
||||
color: var(--wrn-color-text);
|
||||
font-size: clamp(2rem, 5vw, 4rem);
|
||||
@@ -637,11 +674,11 @@ component CTASection {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.wrn-cta-section__footer:empty {
|
||||
.wrn-cta-section__footer:not(:has(> *)) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wrn-cta-section__footer:not(:empty) {
|
||||
.wrn-cta-section__footer:has(> *) {
|
||||
margin-top: 2rem;
|
||||
padding-top: 1.25rem;
|
||||
border-top: 1px solid color-mix(in srgb, currentColor 14%, transparent);
|
||||
@@ -671,6 +708,8 @@ component CTASection {
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.wrn-cta-section[data-mobile-padding="md"] .wrn-cta-section__surface { padding: 2.5rem 1.25rem; }
|
||||
.wrn-cta-section[data-mobile-actions="inline"] .wrn-cta-section__action { width: auto; }
|
||||
.wrn-cta-section__surface {
|
||||
padding: 1.6rem;
|
||||
border-radius: 1.2rem;
|
||||
|
||||
@@ -9,7 +9,8 @@ component Card {
|
||||
}
|
||||
|
||||
props {
|
||||
title: string = "Card title"
|
||||
title: string = ""
|
||||
icon: string = ""
|
||||
titleKey: string = ""
|
||||
subtitle: string = ""
|
||||
subtitleKey: string = ""
|
||||
@@ -39,6 +40,9 @@ component Card {
|
||||
layout: string = "vertical"
|
||||
align: string = "left"
|
||||
hover: string = "none"
|
||||
radius: string = "default"
|
||||
shadow: string = "default"
|
||||
equalHeight: boolean = false
|
||||
scrollable: boolean = false
|
||||
maxHeight: string = "18rem"
|
||||
dismissible: boolean = false
|
||||
@@ -84,6 +88,7 @@ component Card {
|
||||
data-layout='{layout}'
|
||||
data-hover='{hover}'
|
||||
data-align='{align}'
|
||||
data-radius='{radius}' data-shadow='{shadow}' data-equal-height='{equalHeight ? "true" : "false"}'
|
||||
class='wrn-component wrn-next--card wrn-component--color-{color} wrn-component--size-{size} wrn-next--variant-{variant} {class}'
|
||||
>
|
||||
{#if items.length > 0}
|
||||
@@ -271,6 +276,7 @@ component Card {
|
||||
<p>{emptyTitle}</p>
|
||||
</div>
|
||||
{:else}
|
||||
{#if icon}<span class='wrn-next__card-icon {icon}' aria-hidden="true"></span>{/if}
|
||||
{#if description}
|
||||
<p class="wrn-next__card-description" data-t='{descriptionKey}'>{description}</p>
|
||||
{/if}
|
||||
@@ -343,6 +349,12 @@ component Card {
|
||||
.wrn-next--card[data-size="sm"] {
|
||||
--wrn-card-padding: 0.85rem;
|
||||
}
|
||||
.wrn-next--card[data-size="compact"] .wrn-next__card-body { padding: 1rem; }
|
||||
.wrn-next--card[data-radius="lg"] > .wrn-next__card-panel { border-radius: 1rem; }
|
||||
.wrn-next--card[data-shadow="none"] > .wrn-next__card-panel { box-shadow: none; }
|
||||
.wrn-next--card[data-equal-height="true"], .wrn-next--card[data-equal-height="true"] > .wrn-next__card-panel { height: 100%; }
|
||||
.wrn-next--card[data-layout="horizontal"] .wrn-next__card-body { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 0.8rem; }
|
||||
.wrn-next__card-icon { display: grid; width: 2.35rem; height: 2.35rem; place-items: center; border-radius: 0.7rem; color: var(--wrn-color-primary); background: color-mix(in srgb, var(--wrn-color-primary) 12%, transparent); font-size: 1.15rem; }
|
||||
|
||||
.wrn-next--card[data-size="lg"] {
|
||||
--wrn-card-padding: 1.75rem;
|
||||
|
||||
@@ -205,7 +205,9 @@ component ColorPicker {
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -276,7 +276,9 @@ size: string = "default"
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -15,13 +15,17 @@ component FeatureIconCard {
|
||||
variant: string = "default"
|
||||
align: string = "left"
|
||||
hover: string = "lift"
|
||||
radius: string = "default"
|
||||
shadow: string = "default"
|
||||
clickableSurface: boolean = false
|
||||
descriptionTone: string = "default"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
view {
|
||||
<article
|
||||
data-ui-component="FeatureIconCard"
|
||||
data-size='{size}' data-variant='{variant}' data-hover='{hover}' data-align='{align}'
|
||||
data-size='{size}' data-variant='{variant}' data-hover='{hover}' data-align='{align}' data-radius='{radius}' data-shadow='{shadow}' data-clickable-surface='{clickableSurface ? "true" : "false"}' data-description-tone='{descriptionTone}'
|
||||
class='wrn-feature-icon-card {class}'
|
||||
>
|
||||
<div
|
||||
@@ -62,6 +66,11 @@ component FeatureIconCard {
|
||||
style {
|
||||
.wrn-feature-icon-card { position: relative; display: flex; height: 100%; flex-direction: column; padding: 1.5rem; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 2); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); transition: transform var(--wrn-motion-base), border-color var(--wrn-motion-base), box-shadow var(--wrn-motion-base); }
|
||||
.wrn-feature-icon-card[data-size="sm"] { padding: 1.25rem; } .wrn-feature-icon-card[data-size="lg"] { padding: 2rem; }
|
||||
.wrn-feature-icon-card[data-size="compact"] { min-height: 0; padding: 1.25rem; }
|
||||
.wrn-feature-icon-card[data-radius="lg"] { border-radius: 1rem; }
|
||||
.wrn-feature-icon-card[data-shadow="sm"] { box-shadow: 0 5px 18px rgb(15 23 42 / 0.06); }
|
||||
.wrn-feature-icon-card[data-description-tone="muted"] .wrn-feature-icon-card__description { color: var(--wrn-color-text-muted); font-weight: 500; }
|
||||
.wrn-feature-icon-card[data-clickable-surface="true"] .wrn-feature-icon-card__action::after { position: absolute; inset: 0; content: ""; }
|
||||
.wrn-feature-icon-card[data-variant="soft"] { background: var(--wrn-color-surface-soft); }
|
||||
.wrn-feature-icon-card[data-variant="ghost"] { border-color: transparent; background: transparent; box-shadow: none; }
|
||||
.wrn-feature-icon-card[data-hover="lift"]:hover { transform: translateY(-0.25rem); }
|
||||
|
||||
@@ -230,7 +230,9 @@ size: string = "default"
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -244,7 +244,9 @@ size: string = "default"
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -12,6 +12,8 @@ component Footer {
|
||||
items: unknown[] = []
|
||||
columns: number = 3
|
||||
maxWidth: string = "compact"
|
||||
mobileColumns: number = 2
|
||||
mobileCopyrightAlign: string = "center"
|
||||
copyright: string = ""
|
||||
copyrightKey: string = ""
|
||||
translationPrefix: string = ""
|
||||
@@ -36,6 +38,7 @@ component Footer {
|
||||
data-ui-component="Footer"
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-mobile-columns='{mobileColumns}' data-mobile-copyright-align='{mobileCopyrightAlign}'
|
||||
class='wrn-footer {maxWidth === "full" ? "wrn-footer--width-full" : ""} {class}'
|
||||
>
|
||||
<div
|
||||
@@ -531,6 +534,9 @@ component Footer {
|
||||
.wrn-footer__links {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
.wrn-footer[data-mobile-columns="1"] .wrn-footer__links { grid-template-columns: 1fr; }
|
||||
.wrn-footer[data-mobile-columns="2"] .wrn-footer__links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.wrn-footer[data-mobile-copyright-align="start"] :is(.wrn-footer__copyright-left, .wrn-footer__copyright-right) { justify-content: flex-start; text-align: left; }
|
||||
.wrn-footer__bottom,
|
||||
.wrn-footer__bottom--slots {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
@@ -280,22 +280,14 @@ component Hero {
|
||||
}
|
||||
|
||||
.wrn-hero[data-full-bleed="true"] {
|
||||
width: 100vw;
|
||||
max-width: 100vw;
|
||||
margin-inline: calc(50% - 50vw);
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-inline: 0;
|
||||
border-right-width: 0;
|
||||
border-left-width: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@supports (width: 100dvw) {
|
||||
.wrn-hero[data-full-bleed="true"] {
|
||||
width: 100dvw;
|
||||
max-width: 100dvw;
|
||||
margin-inline: calc(50% - 50dvw);
|
||||
}
|
||||
}
|
||||
|
||||
.wrn-hero[data-color="secondary"] {
|
||||
--wrn-hero-accent: var(--wrn-color-secondary);
|
||||
--wrn-hero-accent-soft: var(--wrn-color-secondary-soft, var(--wrn-color-primary-soft));
|
||||
|
||||
@@ -243,7 +243,9 @@ component InputGroup {
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -18,6 +18,7 @@ component MegaMenu {
|
||||
open(payload: { sourceEvent: Event })
|
||||
close(payload: { reason: string })
|
||||
select(payload: { item: object; value: string })
|
||||
category(payload: { item: object; value: string; index: number })
|
||||
}
|
||||
|
||||
props {
|
||||
@@ -26,12 +27,27 @@ component MegaMenu {
|
||||
label: string = "Menu"
|
||||
icon: string = ""
|
||||
columns: unknown[] = []
|
||||
rail: unknown[] = []
|
||||
featured: Record<string, unknown> = {}
|
||||
variant: string = "default"
|
||||
density: string = "default"
|
||||
panelWidth: string = "xl"
|
||||
panelAlign: string = "start"
|
||||
columnCount: number = 0
|
||||
fullWidth: boolean = false
|
||||
openOnHover: boolean = true
|
||||
activeCategory: string = ""
|
||||
actionLabel: string = ""
|
||||
actionHref: string = ""
|
||||
actionIcon: string = "icon-[lucide--arrow-right]"
|
||||
footer: string = ""
|
||||
defaultOpen: boolean = false
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
state visible = defaultOpen
|
||||
state closeTimer = null
|
||||
state selectedCategory = activeCategory
|
||||
|
||||
functions {
|
||||
shared function columnList() {
|
||||
@@ -42,16 +58,66 @@ component MegaMenu {
|
||||
return column && Array.isArray(column.items) ? column.items : []
|
||||
}
|
||||
|
||||
shared function railList() {
|
||||
return Array.isArray(rail) ? rail : []
|
||||
}
|
||||
|
||||
shared function categoryValue(item) {
|
||||
return item ? String(item.value || item.href || item.label || "") : ""
|
||||
}
|
||||
|
||||
shared function activeCategoryIndex() {
|
||||
const entries = railList()
|
||||
if (!entries.length) {
|
||||
return 0
|
||||
}
|
||||
if (!selectedCategory) {
|
||||
return 0
|
||||
}
|
||||
for (let index = 0; index < entries.length; index += 1) {
|
||||
if (categoryValue(entries[index]) === selectedCategory) {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
shared function visibleColumns() {
|
||||
const entries = columnList()
|
||||
if (variant !== "catalog") {
|
||||
return entries
|
||||
}
|
||||
const selected = entries[activeCategoryIndex()]
|
||||
return selected ? [selected] : []
|
||||
}
|
||||
|
||||
client function showPanel(sourceEvent) {
|
||||
cancelScheduledHide()
|
||||
visible = true
|
||||
output.open({ sourceEvent: sourceEvent })
|
||||
}
|
||||
|
||||
client function hidePanel(reason) {
|
||||
cancelScheduledHide()
|
||||
visible = false
|
||||
output.close({ reason: reason })
|
||||
}
|
||||
|
||||
client function cancelScheduledHide() {
|
||||
if (closeTimer) {
|
||||
window.clearTimeout(closeTimer)
|
||||
closeTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
client function scheduleHide() {
|
||||
cancelScheduledHide()
|
||||
closeTimer = window.setTimeout(function () {
|
||||
closeTimer = null
|
||||
hidePanel("pointer-leave")
|
||||
}, 220)
|
||||
}
|
||||
|
||||
client function togglePanel(sourceEvent) {
|
||||
if (visible) {
|
||||
hidePanel("toggle")
|
||||
@@ -60,6 +126,34 @@ component MegaMenu {
|
||||
}
|
||||
}
|
||||
|
||||
client function activatePanel(sourceEvent) {
|
||||
if (openOnHover) {
|
||||
showPanel(sourceEvent)
|
||||
} else {
|
||||
togglePanel(sourceEvent)
|
||||
}
|
||||
}
|
||||
|
||||
client function hoverPanel(sourceEvent) {
|
||||
if (openOnHover) {
|
||||
showPanel(sourceEvent)
|
||||
}
|
||||
}
|
||||
|
||||
client function chooseCategory(item, index, sourceEvent) {
|
||||
const root = sourceEvent.currentTarget.closest(".wrn-mega")
|
||||
const rail = sourceEvent.currentTarget.closest(".wrn-mega__rail")
|
||||
const scrollTop = rail ? rail.scrollTop : 0
|
||||
selectedCategory = categoryValue(item)
|
||||
output.category({ item: item, value: selectedCategory, index: index })
|
||||
window.requestAnimationFrame(function () {
|
||||
const nextRail = root ? root.querySelector(".wrn-mega__rail") : null
|
||||
if (nextRail) {
|
||||
nextRail.scrollTop = scrollTop
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
client function handleKeydown(sourceEvent) {
|
||||
if (sourceEvent.key === "Escape" && visible) {
|
||||
sourceEvent.preventDefault()
|
||||
@@ -84,20 +178,27 @@ component MegaMenu {
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
data-open='{visible}'
|
||||
data-variant='{variant}'
|
||||
data-density='{density}'
|
||||
data-panel-width='{panelWidth}'
|
||||
data-panel-align='{panelAlign}'
|
||||
data-full-width='{fullWidth ? "true" : "false"}'
|
||||
data-column-count='{columnCount}'
|
||||
@keydown='handleKeydown(event)'
|
||||
@mouseleave='hidePanel("pointer-leave")'
|
||||
@mouseenter='cancelScheduledHide()'
|
||||
@mouseleave='scheduleHide()'
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="wrn-mega__trigger"
|
||||
aria-haspopup="true"
|
||||
aria-expanded='{visible}'
|
||||
@click='togglePanel(event)'
|
||||
@mouseenter='showPanel(event)'
|
||||
@focus='showPanel(event)'
|
||||
aria-label='{label}'
|
||||
@click='activatePanel(event)'
|
||||
@mouseenter='hoverPanel(event)'
|
||||
>
|
||||
<span class='wrn-mega__trigger-icon {icon}' data-show="icon" aria-hidden="true"></span>
|
||||
<span class="wrn-mega__trigger-label">{label}</span>
|
||||
<span class="wrn-mega__trigger-label" data-label='{label}' aria-hidden="true"></span>
|
||||
<span class="wrn-mega__arrow" aria-hidden="true">›</span>
|
||||
</button>
|
||||
|
||||
@@ -105,42 +206,76 @@ component MegaMenu {
|
||||
class="wrn-mega__panel"
|
||||
data-wrn-anchored="true"
|
||||
data-show="visible"
|
||||
style='{visible ? "" : "display:none"}'
|
||||
data-wrn-roving="both"
|
||||
@mouseenter='cancelScheduledHide()'
|
||||
>
|
||||
<div class="wrn-mega__columns">
|
||||
{#each columnList() as column}
|
||||
<section class="wrn-mega__column">
|
||||
<h3 class="wrn-mega__heading" data-show="column.heading">{column.heading}</h3>
|
||||
<ul class="wrn-mega__list">
|
||||
{#each itemsOf(column) as item}
|
||||
<li>
|
||||
<a
|
||||
class="wrn-mega__link"
|
||||
href='{item.href || "#"}'
|
||||
data-wrn-roving-item="true"
|
||||
aria-disabled='{item.disabled ? "true" : "false"}'
|
||||
@click='choose(item)'
|
||||
>
|
||||
<span
|
||||
class='wrn-mega__link-icon {item.icon}'
|
||||
data-show="item.icon"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="wrn-mega__link-body">
|
||||
<span class="wrn-mega__link-label">{item.label}</span>
|
||||
<span class="wrn-mega__link-description" data-show="item.description">
|
||||
{item.description}
|
||||
<div class="wrn-mega__layout">
|
||||
{#if railList().length > 0}
|
||||
<aside class="wrn-mega__rail">
|
||||
{#each railList() as item, index}
|
||||
{#if variant === "catalog"}
|
||||
<button type="button" class="wrn-mega__rail-link" data-active='{activeCategoryIndex() === index ? "true" : "false"}' aria-pressed='{activeCategoryIndex() === index ? "true" : "false"}' @click='chooseCategory(item, index, event)'>
|
||||
{#if item.icon}<span class="wrn-mega__rail-icon" aria-hidden="true"><span class='{item.icon}'></span></span>{/if}
|
||||
<span><strong>{item.label}</strong>{#if item.description}<small>{item.description}</small>{/if}</span>
|
||||
</button>
|
||||
{:else}
|
||||
<a class="wrn-mega__rail-link" href='{item.href || "#"}' @click='choose(item)'>
|
||||
{#if item.icon}<span class="wrn-mega__rail-icon" aria-hidden="true"><span class='{item.icon}'></span></span>{/if}
|
||||
<span><strong>{item.label}</strong>{#if item.description}<small>{item.description}</small>{/if}</span>
|
||||
</a>
|
||||
{/if}
|
||||
{/each}
|
||||
</aside>
|
||||
{/if}
|
||||
|
||||
<div class="wrn-mega__columns">
|
||||
{#each visibleColumns() as column}
|
||||
<section class="wrn-mega__column">
|
||||
<header class="wrn-mega__column-header">
|
||||
{#if column.eyebrow}<p class="wrn-mega__eyebrow">{column.eyebrow}</p>{/if}
|
||||
<h3 class="wrn-mega__heading" data-show="column.heading">{column.heading}</h3>
|
||||
{#if column.description}<p class="wrn-mega__column-description">{column.description}</p>{/if}
|
||||
</header>
|
||||
{#if column.image}<img class="wrn-mega__column-image" src='{column.image}' alt='{column.imageAlt || ""}' loading="lazy" />{/if}
|
||||
<ul class="wrn-mega__list">
|
||||
{#each itemsOf(column) as item}
|
||||
<li>
|
||||
<a class="wrn-mega__link" href='{item.href || "#"}' data-wrn-roving-item="true" aria-disabled='{item.disabled ? "true" : "false"}' @click='choose(item)'>
|
||||
{#if item.image}<img class="wrn-mega__link-image" src='{item.image}' alt="" loading="lazy" />{/if}
|
||||
{#if item.icon}
|
||||
<span class='wrn-mega__link-icon {item.icon}' aria-hidden="true"></span>
|
||||
{:else if variant === "catalog"}
|
||||
<span class="wrn-mega__link-icon wrn-mega__link-icon--fallback" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="M3 9h18"></path><path d="M9 21V9"></path></svg>
|
||||
</span>
|
||||
{/if}
|
||||
<span class="wrn-mega__link-body">
|
||||
{#if item.eyebrow}<span class="wrn-mega__link-eyebrow">{item.eyebrow}</span>{/if}
|
||||
<span class="wrn-mega__link-label">{item.label}{#if item.badge}<span class="wrn-mega__badge">{item.badge}</span>{/if}</span>
|
||||
{#if item.description}<span class="wrn-mega__link-description">{item.description}</span>{/if}
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
{/each}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{#if column.actionLabel}<a class="wrn-mega__column-action" href='{column.actionHref || "#"}'>{column.actionLabel}<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span></a>{/if}
|
||||
</section>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if featured.title || featured.image}
|
||||
<aside class="wrn-mega__featured">
|
||||
{#if featured.eyebrow}<p class="wrn-mega__eyebrow">{featured.eyebrow}</p>{/if}
|
||||
{#if featured.image}<img src='{featured.image}' alt='{featured.imageAlt || ""}' loading="lazy" />{/if}
|
||||
{#if featured.title}<strong>{featured.title}</strong>{/if}
|
||||
{#if featured.description}<p>{featured.description}</p>{/if}
|
||||
{#if featured.actionLabel}<a href='{featured.actionHref || "#"}'>{featured.actionLabel}<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span></a>{/if}
|
||||
</aside>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<p class="wrn-mega__footer" data-show="footer">{footer}</p>
|
||||
{#if footer || actionLabel}<div class="wrn-mega__footer"><span>{footer}</span>{#if actionLabel}<a href='{actionHref || "#"}'>{actionLabel}<span class='{actionIcon}' aria-hidden="true"></span></a>{/if}</div>{/if}
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
@@ -187,7 +322,7 @@ component MegaMenu {
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--wrn-radius-sm);
|
||||
background: transparent;
|
||||
color: var(--wrn-color-text-muted);
|
||||
color: var(--wrn-color-text);
|
||||
font: inherit;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
@@ -200,6 +335,10 @@ component MegaMenu {
|
||||
color: var(--wrn-color-text);
|
||||
}
|
||||
|
||||
.wrn-mega__trigger-label::before {
|
||||
content: attr(data-label);
|
||||
}
|
||||
|
||||
.wrn-mega__trigger:focus-visible {
|
||||
outline: 2px solid var(--mega-accent);
|
||||
outline-offset: 2px;
|
||||
@@ -226,29 +365,78 @@ component MegaMenu {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 100%;
|
||||
height: 0.6rem;
|
||||
height: 0.85rem;
|
||||
}
|
||||
|
||||
.wrn-mega__panel {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
z-index: 40;
|
||||
z-index: 1200;
|
||||
top: calc(100% + 0.4rem);
|
||||
left: 0;
|
||||
width: max-content;
|
||||
max-width: min(60rem, calc(100vw - 2rem));
|
||||
max-height: calc(100dvh - 5.5rem);
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--wrn-color-border);
|
||||
border-radius: var(--wrn-radius-lg);
|
||||
background: var(--wrn-color-surface);
|
||||
box-shadow: var(--wrn-shadow-1);
|
||||
}
|
||||
|
||||
/* Keep SSR output closed before the reactive runtime hydrates it. */
|
||||
.wrn-mega__panel[data-show="false"] { display: none; }
|
||||
.wrn-mega__panel[data-show="true"] { display: flex; }
|
||||
|
||||
.wrn-mega[data-panel-width="lg"] .wrn-mega__panel { width: min(48rem, calc(100vw - 2rem)); }
|
||||
.wrn-mega[data-panel-width="xl"] .wrn-mega__panel { width: min(72rem, calc(100vw - 2rem)); }
|
||||
.wrn-mega[data-panel-width="2xl"] .wrn-mega__panel { width: min(88rem, calc(100vw - 2rem)); }
|
||||
.wrn-mega[data-full-width="true"] .wrn-mega__panel { width: calc(100vw - 2rem); max-width: none; }
|
||||
.wrn-mega[data-panel-align="center"] .wrn-mega__panel { left: 50%; transform: translateX(-50%); }
|
||||
.wrn-mega[data-panel-align="end"] .wrn-mega__panel { right: 0; left: auto; }
|
||||
|
||||
.wrn-mega[data-variant="default"] .wrn-mega__panel {
|
||||
width: max-content;
|
||||
min-width: 15rem;
|
||||
max-width: min(24rem, calc(100dvw - 2rem));
|
||||
height: auto;
|
||||
}
|
||||
.wrn-mega[data-variant="default"] .wrn-mega__layout,
|
||||
.wrn-mega[data-variant="default"] .wrn-mega__columns,
|
||||
.wrn-mega[data-variant="default"][data-column-count] .wrn-mega__columns {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
.wrn-mega[data-variant="default"] .wrn-mega__columns { overflow: visible; }
|
||||
.wrn-mega[data-variant="default"] .wrn-mega__featured { width: auto; padding: 1rem 0 0; border-top: 1px solid var(--wrn-color-border); border-left: 0; }
|
||||
|
||||
.wrn-mega__layout { display: grid; min-height: 0; max-height: calc(100dvh - 8rem); flex: 1; grid-template-columns: auto minmax(0, 1fr) auto; gap: 1.25rem; overflow: hidden; }
|
||||
|
||||
.wrn-mega__columns {
|
||||
display: grid;
|
||||
min-height: 0;
|
||||
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
|
||||
gap: 1.25rem;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.wrn-mega[data-column-count="2"] .wrn-mega__columns { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.wrn-mega[data-column-count="3"] .wrn-mega__columns { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
.wrn-mega[data-column-count="4"] .wrn-mega__columns { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||
.wrn-mega[data-column-count="5"] .wrn-mega__columns { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
||||
|
||||
.wrn-mega__rail { display: grid; min-width: 13rem; min-height: 0; padding: 0.75rem; align-content: start; gap: 0.25rem; overflow-y: auto; overscroll-behavior: contain; border-radius: var(--wrn-radius-lg); background: var(--wrn-color-surface-soft); }
|
||||
.wrn-mega__rail-link { appearance: none; display: flex; width: 100%; padding: 0.65rem; align-items: flex-start; gap: 0.65rem; color: var(--wrn-color-text); border: 0; border-radius: var(--wrn-radius-sm); background: transparent; font: inherit; text-align: left; text-decoration: none; cursor: pointer; }
|
||||
.wrn-mega__rail-link:hover { background: var(--wrn-color-surface); }
|
||||
.wrn-mega__rail-link[data-active="true"] { color: var(--mega-accent); background: var(--wrn-color-surface); box-shadow: inset 3px 0 0 var(--mega-accent); }
|
||||
.wrn-mega__rail-link > span:last-child { display: grid; gap: 0.2rem; }
|
||||
.wrn-mega__rail-link strong { font-size: 0.82rem; }
|
||||
.wrn-mega__rail-link small { max-width: 18ch; color: var(--wrn-color-text-muted); font-size: 0.72rem; line-height: 1.45; }
|
||||
.wrn-mega__rail-icon { display: grid; width: 2.4rem; height: 2.4rem; flex: none; place-items: center; color: var(--mega-accent); border-radius: 0.7rem; background: color-mix(in srgb, var(--mega-accent) 12%, transparent); font-size: 1.15rem; }
|
||||
|
||||
.wrn-mega__column {
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -262,6 +450,10 @@ component MegaMenu {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wrn-mega__eyebrow, .wrn-mega__link-eyebrow { margin: 0 0 0.35rem; color: var(--wrn-color-text-muted); font-size: 0.65rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
|
||||
.wrn-mega__column-description { margin: -0.25rem 0 0.75rem; color: var(--wrn-color-text-muted); font-size: 0.75rem; line-height: 1.5; }
|
||||
.wrn-mega__column-image { width: 100%; aspect-ratio: 16 / 7; margin-bottom: 0.65rem; object-fit: cover; border-radius: var(--wrn-radius-sm); }
|
||||
|
||||
.wrn-mega__list {
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
@@ -270,8 +462,14 @@ component MegaMenu {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wrn-mega__list > li {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wrn-mega__link {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
padding: 0.45rem 0.5rem;
|
||||
@@ -280,6 +478,43 @@ component MegaMenu {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wrn-mega__link-icon { display: grid; width: 2rem; height: 2rem; flex: none; place-items: center; color: var(--mega-accent); border-radius: 999px; background: color-mix(in srgb, var(--mega-accent) 10%, var(--wrn-color-surface-soft)); }
|
||||
.wrn-mega__link-image { width: 2.4rem; height: 2.4rem; flex: none; object-fit: cover; border-radius: 0.65rem; }
|
||||
.wrn-mega__badge { display: inline-flex; margin-left: 0.4rem; padding: 0.12rem 0.35rem; align-items: center; color: var(--mega-accent); border: 1px solid color-mix(in srgb, var(--mega-accent) 35%, transparent); border-radius: 999px; font-size: 0.6rem; font-weight: 700; }
|
||||
.wrn-mega__column-action, .wrn-mega__featured a, .wrn-mega__footer a { display: inline-flex; margin-top: 0.7rem; align-items: center; gap: 0.35rem; color: var(--mega-accent); font-size: 0.78rem; font-weight: 700; text-decoration: none; }
|
||||
|
||||
.wrn-mega__featured { display: grid; width: 14rem; padding-left: 1.25rem; align-content: start; gap: 0.55rem; border-left: 1px solid var(--wrn-color-border); }
|
||||
.wrn-mega__featured img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--wrn-radius-lg); }
|
||||
.wrn-mega__featured strong { font-size: 0.85rem; line-height: 1.35; }
|
||||
.wrn-mega__featured p { margin: 0; color: var(--wrn-color-text-muted); font-size: 0.75rem; line-height: 1.5; }
|
||||
|
||||
.wrn-mega[data-density="compact"] :is(.wrn-mega__panel, .wrn-mega__rail) { padding: 0.7rem; }
|
||||
.wrn-mega[data-density="compact"] .wrn-mega__columns { gap: 0.65rem; }
|
||||
.wrn-mega[data-variant="icon-grid"] .wrn-mega__list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.35rem; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__panel { height: min(36rem, calc(100dvh - 6.5rem)); }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__layout { grid-template-columns: minmax(14rem, 17rem) minmax(0, 1fr); gap: 0; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__rail { min-width: 0; padding: 0.55rem; gap: 0.3rem; border-radius: calc(var(--wrn-radius-lg) - 0.25rem); }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link { min-height: 2.5rem; padding: 0.45rem 0.55rem; align-items: center; gap: 0.7rem; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link strong { font-size: 0.76rem; font-weight: 400; line-height: 1.25; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link[data-active="true"] strong { font-weight: 600; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-icon { display: grid; width: 1.65rem; height: 1.65rem; flex: 0 0 1.65rem; place-items: center; color: currentColor; border: 1px solid color-mix(in srgb, currentColor 35%, transparent); border-radius: 0.45rem; background: color-mix(in srgb, currentColor 8%, transparent); opacity: 0.9; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-icon > span { display: block; width: 0.95rem; height: 0.95rem; color: currentColor !important; font-size: 0.95rem; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link:hover .wrn-mega__rail-icon,
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link[data-active="true"] .wrn-mega__rail-icon { opacity: 1; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__columns { padding: 0.35rem 0.5rem 0.75rem 1.25rem; grid-template-columns: minmax(0, 1fr); }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__column { display: flex; min-height: 100%; flex-direction: column; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__column-header { width: 100%; min-width: 0; margin: 0 0 0.85rem; padding: 0 0 0.65rem; border: 0; border-bottom: 1px solid var(--wrn-color-border); border-radius: 0; background: transparent; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__heading { margin: 0; color: var(--wrn-color-text-muted); font-size: 0.75rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__column-description { margin: 0.3rem 0 0; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__list { grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: 0.3rem; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__link { min-height: 2.7rem; padding: 0.45rem 0.55rem; align-items: center; gap: 0.55rem; border: 1px solid transparent; background: transparent; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__link:hover { border-color: color-mix(in srgb, var(--mega-accent) 28%, var(--wrn-color-border)); background: color-mix(in srgb, var(--mega-accent) 7%, var(--wrn-color-surface)); transform: translateY(-1px); }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__column-action { margin-top: auto; padding-top: 1rem; }
|
||||
.wrn-mega[data-variant="dark"] .wrn-mega__panel { color: #f8fafc; border-color: #2b2b2b; background: #151515; }
|
||||
.wrn-mega[data-variant="dark"] :is(.wrn-mega__heading, .wrn-mega__link, .wrn-mega__featured strong) { color: #f8fafc; }
|
||||
.wrn-mega[data-variant="dark"] :is(.wrn-mega__link-description, .wrn-mega__column-description, .wrn-mega__eyebrow, .wrn-mega__featured p) { color: #a3a3a3; }
|
||||
.wrn-mega[data-variant="dark"] .wrn-mega__link:hover { background: #242424; }
|
||||
|
||||
.wrn-mega__link:hover {
|
||||
background: var(--wrn-color-surface-soft);
|
||||
}
|
||||
@@ -300,9 +535,15 @@ component MegaMenu {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__link-icon { width: 1.65rem; height: 1.65rem; flex: 0 0 1.65rem; border: 1px solid color-mix(in srgb, var(--mega-accent) 22%, var(--wrn-color-border)); border-radius: 0.45rem; font-size: 0.9rem; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__link-icon--fallback svg { width: 0.9rem; height: 0.9rem; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__link-label { font-size: 0.78rem; line-height: 1.25; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__link-description { font-size: 0.7rem; line-height: 1.35; }
|
||||
|
||||
.wrn-mega__link-label {
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.wrn-mega__link-description {
|
||||
@@ -312,8 +553,12 @@ component MegaMenu {
|
||||
}
|
||||
|
||||
.wrn-mega__footer {
|
||||
display: flex;
|
||||
margin: 1rem 0 0;
|
||||
padding-top: 0.75rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: 1px solid var(--wrn-color-border);
|
||||
color: var(--wrn-color-text-muted);
|
||||
font-size: 0.8rem;
|
||||
@@ -333,12 +578,24 @@ component MegaMenu {
|
||||
position: static;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
max-height: none;
|
||||
overflow: visible;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.wrn-mega__panel[data-show="false"] { display: none; }
|
||||
|
||||
.wrn-mega__columns {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.wrn-mega__layout { max-height: none; grid-template-columns: 1fr; overflow: visible; }
|
||||
.wrn-mega__rail, .wrn-mega__columns { overflow: visible; }
|
||||
.wrn-mega__featured { width: auto; padding: 1rem 0 0; border-top: 1px solid var(--wrn-color-border); border-left: 0; }
|
||||
.wrn-mega[data-variant="icon-grid"] .wrn-mega__list { grid-template-columns: 1fr; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__panel { height: auto; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__layout { grid-template-columns: 1fr; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__columns { padding: 1rem 0 0; }
|
||||
.wrn-mega[data-variant="catalog"] .wrn-mega__list { grid-template-columns: 1fr; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,9 @@ component MetricGrid {
|
||||
@action='forwardAction(item, itemIndex, event)'
|
||||
/>
|
||||
{:empty}
|
||||
<slot></slot>
|
||||
<div class="wrn-metric-grid__empty" role="status">
|
||||
<slot>No metrics are available.</slot>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,6 +102,15 @@ component MetricGrid {
|
||||
}
|
||||
|
||||
style {
|
||||
.wrn-metric-grid__empty {
|
||||
grid-column: 1 / -1;
|
||||
padding: 1.25rem;
|
||||
color: var(--wrn-color-text-muted);
|
||||
background: var(--wrn-color-surface-soft);
|
||||
border: 1px dashed var(--wrn-color-border);
|
||||
border-radius: 0.9rem;
|
||||
text-align: center;
|
||||
}
|
||||
.wrn-metric-grid__frame {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import MegaMenu from "./MegaMenu.wrn"
|
||||
|
||||
component Navbar {
|
||||
outputs {
|
||||
toggle(payload: { open: boolean })
|
||||
@@ -17,9 +19,17 @@ size: string = "default"
|
||||
actions: unknown[] = []
|
||||
active: string = ""
|
||||
sticky: boolean = false
|
||||
floating: boolean = false
|
||||
startFloating: string = "0px"
|
||||
openOnHover: boolean = false
|
||||
maxWidth: string = "full"
|
||||
mobileLabel: string = "Toggle navigation"
|
||||
collapseAt: string = "900px"
|
||||
mobilePanel: string = "content"
|
||||
mobileUtilitiesPosition: string = "header"
|
||||
gutter: string = "md"
|
||||
contentGutter: string = "md"
|
||||
flushMobile: boolean = false
|
||||
translationPrefix: string = ""
|
||||
class: string = ""
|
||||
}
|
||||
@@ -36,27 +46,112 @@ size: string = "default"
|
||||
const slug = String(source).replace(/^\/+|\/+$/g, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase()
|
||||
return slug ? translationPrefix + "." + slug + (field === "description" ? ".description" : "") : ""
|
||||
}
|
||||
client function toggleNavigation() {
|
||||
mobileOpen = !mobileOpen
|
||||
output.toggle({ open: mobileOpen })
|
||||
output[mobileOpen ? "open" : "close"]({ source: "mobile" })
|
||||
shared function itemType(item) {
|
||||
if (item && item.type) {
|
||||
return item.type
|
||||
}
|
||||
return item && ((item.items && item.items.length) || (item.children && item.children.length)) ? "dropdown" : "link"
|
||||
}
|
||||
|
||||
client function selectItem(item, level) {
|
||||
mobileOpen = false
|
||||
shared function dropdownItems(item) {
|
||||
if (!item) {
|
||||
return []
|
||||
}
|
||||
if (Array.isArray(item.items)) {
|
||||
return item.items
|
||||
}
|
||||
return Array.isArray(item.children) ? item.children : []
|
||||
}
|
||||
|
||||
shared function dropdownGroupItems(item) {
|
||||
if (!item) {
|
||||
return []
|
||||
}
|
||||
if (Array.isArray(item.items)) {
|
||||
return item.items
|
||||
}
|
||||
return Array.isArray(item.children) ? item.children : []
|
||||
}
|
||||
|
||||
shared function megaData(item) {
|
||||
return item && item.mega && typeof item.mega === "object" ? item.mega : item || {}
|
||||
}
|
||||
|
||||
shared function megaColumns(item) {
|
||||
const data = megaData(item)
|
||||
if (Array.isArray(data.columns)) {
|
||||
return data.columns
|
||||
}
|
||||
const legacy = dropdownItems(item)
|
||||
if (!legacy.length) {
|
||||
return []
|
||||
}
|
||||
const grouped = legacy.some(function (entry) {
|
||||
return dropdownGroupItems(entry).length > 0
|
||||
})
|
||||
if (grouped) {
|
||||
return legacy.map(function (entry) {
|
||||
return {
|
||||
heading: entry.label || "",
|
||||
description: entry.description || "",
|
||||
items: dropdownGroupItems(entry)
|
||||
}
|
||||
})
|
||||
}
|
||||
return [{ heading: item.description || "", items: legacy }]
|
||||
}
|
||||
client function setMobileNavigation(open, trigger) {
|
||||
const root = trigger && trigger.closest ? trigger.closest("[data-wrn-navbar]") : null
|
||||
if (!root) return
|
||||
const collapse = root.querySelector(".wrn-navbar__collapse")
|
||||
const toggle = root.querySelector(".wrn-navbar__toggle")
|
||||
if (collapse) collapse.classList.toggle("is-open", open)
|
||||
if (toggle) toggle.setAttribute("aria-expanded", open ? "true" : "false")
|
||||
}
|
||||
|
||||
client function toggleNavigation(event) {
|
||||
const open = event.currentTarget.getAttribute("aria-expanded") !== "true"
|
||||
setMobileNavigation(open, event.currentTarget)
|
||||
output.toggle({ open: open })
|
||||
output[open ? "open" : "close"]({ source: "mobile" })
|
||||
}
|
||||
|
||||
client function selectItem(item, level, event) {
|
||||
setMobileNavigation(false, event.currentTarget)
|
||||
output.select({ item: item, value: item.value || "", level: level })
|
||||
}
|
||||
|
||||
client function selectAction(item) {
|
||||
mobileOpen = false
|
||||
client function selectAction(item, event) {
|
||||
setMobileNavigation(false, event.currentTarget)
|
||||
output.action({ item: item, value: item.value || "" })
|
||||
}
|
||||
|
||||
shared function isItemActive(item) {
|
||||
if (!item) return false
|
||||
if (item.value && item.value === active) return true
|
||||
if (!item.children || !item.children.length) return false
|
||||
return item.children.some(function (child) {
|
||||
if (!item) {
|
||||
return false
|
||||
}
|
||||
if (item.value && item.value === active) {
|
||||
return true
|
||||
}
|
||||
if (itemType(item) === "mega") {
|
||||
const data = megaData(item)
|
||||
const links = Array.isArray(data.rail) ? data.rail.slice() : []
|
||||
megaColumns(item).forEach(function (column) {
|
||||
if (column && Array.isArray(column.items)) {
|
||||
column.items.forEach(function (child) {
|
||||
links.push(child)
|
||||
})
|
||||
}
|
||||
})
|
||||
return links.some(function (child) {
|
||||
return child.value && child.value === active
|
||||
})
|
||||
}
|
||||
const entries = dropdownItems(item)
|
||||
if (!entries.length) {
|
||||
return false
|
||||
}
|
||||
return entries.some(function (child) {
|
||||
return isItemActive(child)
|
||||
})
|
||||
}
|
||||
@@ -70,12 +165,13 @@ size: string = "default"
|
||||
}
|
||||
|
||||
view {
|
||||
<header class="wrn-navbar wrn-navbar--width-{maxWidth} wrn-component--color-{color} wrn-component--size-{size} {sticky ? 'wrn-navbar--sticky' : ''} {class}" data-wrn-navbar data-open-on-hover="{openOnHover}">
|
||||
<header class="wrn-navbar wrn-navbar--width-{maxWidth} wrn-component--color-{color} wrn-component--size-{size} {sticky ? 'wrn-navbar--sticky' : ''} {floating ? 'wrn-navbar--floating' : ''} {class}" style="--wrn-navbar-floating-start: {startFloating};" data-wrn-navbar data-open-on-hover="{openOnHover}" data-floating="{floating ? 'true' : 'false'}" data-collapse-at="{collapseAt}" data-mobile-panel="{mobilePanel}" data-mobile-utilities-position="{mobileUtilitiesPosition}" data-gutter="{gutter}" data-content-gutter="{contentGutter}" data-flush-mobile="{flushMobile ? 'true' : 'false'}">
|
||||
<div class="wrn-navbar__topbar" aria-label="{topbarLabel}">
|
||||
<slot name="topbar" />
|
||||
</div>
|
||||
|
||||
<div class="wrn-navbar__main">
|
||||
{#if brand.logo || brand.icon || brand.label || brand.description}
|
||||
<a class="wrn-navbar__brand" href="{brand.href || '/'}" aria-label="{brand.ariaLabel || brand.label || label}">
|
||||
{#if brand.logo}
|
||||
<img class="wrn-navbar__brand-logo" src="{brand.logo}" alt="{brand.logoAlt || brand.label || ''}" width="{brand.logoWidth || ''}" height="{brand.logoHeight || ''}" />
|
||||
@@ -90,15 +186,39 @@ size: string = "default"
|
||||
</span>
|
||||
{/if}
|
||||
</a>
|
||||
|
||||
<button type="button" class="wrn-navbar__toggle" aria-label="{mobileLabel}" aria-expanded="{mobileOpen}" @click="toggleNavigation()">
|
||||
<span aria-hidden="true"></span><span aria-hidden="true"></span><span aria-hidden="true"></span>
|
||||
</button>
|
||||
{/if}
|
||||
<slot name="brand" />
|
||||
|
||||
<div class="wrn-navbar__collapse {mobileOpen ? 'is-open' : ''}">
|
||||
<nav class="wrn-navbar__menus" aria-label="{label}" data-wrn-roving="horizontal">
|
||||
{#each items as item}
|
||||
{#if item.children && item.children.length}
|
||||
{#if itemType(item) === "mega"}
|
||||
<MegaMenu
|
||||
class="wrn-navbar__mega"
|
||||
data-active="{isItemActive(item) ? 'true' : 'false'}"
|
||||
label="{item.label}"
|
||||
icon="{item.icon || ''}"
|
||||
columns={megaColumns(item)}
|
||||
rail={megaData(item).rail || []}
|
||||
featured={megaData(item).featured || {}}
|
||||
variant="{megaData(item).variant || 'default'}"
|
||||
density="{megaData(item).density || 'default'}"
|
||||
panelWidth="{megaData(item).panelWidth || 'xl'}"
|
||||
panelAlign="{megaData(item).panelAlign || 'start'}"
|
||||
columnCount={megaData(item).columnCount || 0}
|
||||
fullWidth={megaData(item).fullWidth || false}
|
||||
openOnHover={openOnHover}
|
||||
activeCategory="{megaData(item).activeCategory || ''}"
|
||||
actionLabel="{megaData(item).actionLabel || ''}"
|
||||
actionHref="{megaData(item).actionHref || ''}"
|
||||
actionIcon="{megaData(item).actionIcon || 'icon-[lucide--arrow-right]'}"
|
||||
footer="{megaData(item).footer || ''}"
|
||||
/>
|
||||
<a class="wrn-navbar__menu-link wrn-navbar__mobile-mega-link" data-wrn-roving-item="true" href="{item.href || megaData(item).actionHref || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{isItemActive(item) ? 'page' : 'false'}" @click="selectItem(item, 1, event)">
|
||||
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
||||
<span data-t="{translationKey(item, 'label')}">{item.label}</span>
|
||||
</a>
|
||||
{:else if itemType(item) === "dropdown"}
|
||||
<details class="wrn-navbar__dropdown wrn-navbar__dropdown--{item.type || 'dropdown'}" name="wrn-navbar-menu" @toggle="toggleDropdown(event, item)">
|
||||
<summary data-wrn-roving-item="true" aria-current="{isItemActive(item) ? 'page' : 'false'}">
|
||||
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
||||
@@ -107,19 +227,19 @@ size: string = "default"
|
||||
</summary>
|
||||
<div class="wrn-navbar__panel wrn-navbar__panel--columns-{item.columns || 1}">
|
||||
{#if item.description}<p class="wrn-navbar__panel-intro" data-t="{translationKey(item, 'description')}">{item.description}</p>{/if}
|
||||
{#each item.children as child}
|
||||
{#each dropdownItems(item) as child}
|
||||
<div class="wrn-navbar__group">
|
||||
{#if child.children && child.children.length}
|
||||
{#if dropdownGroupItems(child).length}
|
||||
{#if child.label}<strong class="wrn-navbar__group-title" data-t="{translationKey(child, 'label')}">{child.label}</strong>{/if}
|
||||
{#if child.description}<small data-t="{translationKey(child, 'description')}">{child.description}</small>{/if}
|
||||
{#each child.children as nested}
|
||||
<a href="{nested.href || '#'}" target="{nested.target || ''}" rel="{nested.rel || ''}" aria-current="{nested.value === active ? 'page' : 'false'}" @click="selectItem(nested, 3)">
|
||||
{#each dropdownGroupItems(child) as nested}
|
||||
<a href="{nested.href || '#'}" target="{nested.target || ''}" rel="{nested.rel || ''}" aria-current="{nested.value === active ? 'page' : 'false'}" @click="selectItem(nested, 3, event)">
|
||||
{#if nested.icon}<span class="{nested.icon}" aria-hidden="true"></span>{/if}
|
||||
<span><strong data-t="{translationKey(nested, 'label')}">{nested.label}</strong>{#if nested.description}<small data-t="{translationKey(nested, 'description')}">{nested.description}</small>{/if}</span>
|
||||
</a>
|
||||
{/each}
|
||||
{:else}
|
||||
<a href="{child.href || '#'}" target="{child.target || ''}" rel="{child.rel || ''}" aria-current="{child.value === active ? 'page' : 'false'}" @click="selectItem(child, 2)">
|
||||
<a href="{child.href || '#'}" target="{child.target || ''}" rel="{child.rel || ''}" aria-current="{child.value === active ? 'page' : 'false'}" @click="selectItem(child, 2, event)">
|
||||
{#if child.icon}<span class="{child.icon}" aria-hidden="true"></span>{/if}
|
||||
<span><strong data-t="{translationKey(child, 'label')}">{child.label}</strong>{#if child.description}<small data-t="{translationKey(child, 'description')}">{child.description}</small>{/if}</span>
|
||||
</a>
|
||||
@@ -129,7 +249,7 @@ size: string = "default"
|
||||
</div>
|
||||
</details>
|
||||
{:else}
|
||||
<a class="wrn-navbar__menu-link" data-wrn-roving-item="true" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : 'false'}" @click="selectItem(item, 1)">
|
||||
<a class="wrn-navbar__menu-link" data-wrn-roving-item="true" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : 'false'}" @click="selectItem(item, 1, event)">
|
||||
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
||||
<span data-t="{translationKey(item, 'label')}">{item.label}</span>
|
||||
</a>
|
||||
@@ -138,15 +258,21 @@ size: string = "default"
|
||||
</nav>
|
||||
|
||||
<div class="wrn-navbar__actions">
|
||||
<slot name="actions" />
|
||||
{#each actions as item}
|
||||
<a class="wrn-navbar__action wrn-navbar__action--{item.variant || 'link'}" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" @click="selectAction(item)">
|
||||
<a class="wrn-navbar__action wrn-navbar__action--{item.variant || 'link'}" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" @click="selectAction(item, event)">
|
||||
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
||||
<span data-t="{translationKey(item, 'label')}">{item.label}</span>
|
||||
</a>
|
||||
{/each}
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrn-navbar__utilities"><slot name="utilities" /></div>
|
||||
|
||||
<button type="button" class="wrn-navbar__toggle" aria-label="{mobileLabel}" aria-expanded="{mobileOpen}" @click="toggleNavigation(event)">
|
||||
<span aria-hidden="true"></span><span aria-hidden="true"></span><span aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
}
|
||||
@@ -155,7 +281,7 @@ size: string = "default"
|
||||
/* Full application navigation */
|
||||
.wrn-navbar {
|
||||
position: relative;
|
||||
z-index: 40;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
color: var(--wrn-color-text);
|
||||
background: color-mix(in srgb, var(--wrn-color-surface) 96%, transparent);
|
||||
@@ -168,12 +294,19 @@ size: string = "default"
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.wrn-navbar__topbar:empty,
|
||||
.wrn-navbar__topbar:not(:has(*)) {
|
||||
.wrn-navbar--floating {
|
||||
position: sticky;
|
||||
z-index: 1000;
|
||||
top: var(--wrn-navbar-floating-start, 0px);
|
||||
box-shadow: 0 10px 30px rgb(15 23 42 / 0.08);
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.wrn-navbar__topbar:not(:has(> *)) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wrn-navbar__topbar:not(:empty) {
|
||||
.wrn-navbar__topbar:has(> *) {
|
||||
display: flex;
|
||||
min-height: 2.25rem;
|
||||
align-items: center;
|
||||
@@ -188,6 +321,7 @@ size: string = "default"
|
||||
}
|
||||
|
||||
.wrn-navbar__main {
|
||||
position: relative;
|
||||
display: flex;
|
||||
min-height: 4.25rem;
|
||||
width: min(100%, 90rem);
|
||||
@@ -197,6 +331,17 @@ size: string = "default"
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.wrn-navbar[data-gutter="sm"] .wrn-navbar__main { padding-inline: 1rem; }
|
||||
.wrn-navbar[data-gutter="lg"] .wrn-navbar__main { padding-inline: clamp(1.5rem, 5vw, 4rem); }
|
||||
|
||||
.wrn-navbar__utilities {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.wrn-navbar__utilities:not(:has(> *)) { display: none; }
|
||||
|
||||
.wrn-navbar--width-full .wrn-navbar__main {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
@@ -265,20 +410,59 @@ size: string = "default"
|
||||
.wrn-navbar__collapse {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.wrn-navbar__menus {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
pointer-events: none;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.wrn-navbar__menus > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.wrn-navbar__actions {
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.wrn-navbar__mega {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.wrn-navbar__mega .wrn-mega__trigger {
|
||||
min-height: 2.75rem;
|
||||
padding: 0.65rem 0.8rem;
|
||||
border-radius: 0.65rem;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wrn-navbar__mega[data-full-width="true"] .wrn-mega__panel {
|
||||
position: fixed;
|
||||
top: calc(var(--wrn-navbar-floating-start, 0px) + 4.25rem);
|
||||
right: 1rem;
|
||||
left: 1rem;
|
||||
width: auto;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.wrn-navbar__mega[data-variant="default"][data-full-width="true"] .wrn-mega__panel {
|
||||
position: absolute;
|
||||
top: calc(100% + 0.4rem);
|
||||
right: auto;
|
||||
left: 0;
|
||||
width: max-content;
|
||||
min-width: 15rem;
|
||||
max-width: min(24rem, calc(100dvw - 2rem));
|
||||
}
|
||||
|
||||
.wrn-navbar__menu-link,
|
||||
.wrn-navbar__dropdown > summary,
|
||||
.wrn-navbar__action {
|
||||
@@ -295,13 +479,15 @@ size: string = "default"
|
||||
}
|
||||
|
||||
.wrn-navbar__menu-link:hover,
|
||||
.wrn-navbar__dropdown > summary:hover {
|
||||
.wrn-navbar__dropdown > summary:hover,
|
||||
.wrn-navbar__mega .wrn-mega__trigger:hover {
|
||||
color: var(--wrn-color-text);
|
||||
background: var(--wrn-color-surface-2);
|
||||
}
|
||||
|
||||
.wrn-navbar__menu-link[aria-current="page"],
|
||||
.wrn-navbar__dropdown > summary[aria-current="page"] {
|
||||
.wrn-navbar__dropdown > summary[aria-current="page"],
|
||||
.wrn-navbar__mega[data-active="true"] .wrn-mega__trigger {
|
||||
color: var(--wrn-component-color, var(--wrn-color-primary));
|
||||
font-weight: 600;
|
||||
background: color-mix(
|
||||
@@ -313,7 +499,8 @@ size: string = "default"
|
||||
}
|
||||
|
||||
.wrn-navbar__menu-link[aria-current="page"]:hover,
|
||||
.wrn-navbar__dropdown > summary[aria-current="page"]:hover {
|
||||
.wrn-navbar__dropdown > summary[aria-current="page"]:hover,
|
||||
.wrn-navbar__mega[data-active="true"] .wrn-mega__trigger:hover {
|
||||
color: var(--wrn-component-color, var(--wrn-color-primary));
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
@@ -349,6 +536,7 @@ size: string = "default"
|
||||
|
||||
.wrn-navbar__panel {
|
||||
position: absolute;
|
||||
z-index: 1100;
|
||||
top: calc(100% + 0.55rem);
|
||||
left: 50%;
|
||||
display: grid;
|
||||
@@ -508,7 +696,13 @@ size: string = "default"
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.wrn-navbar__mobile-mega-link { display: none; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.wrn-navbar__main {
|
||||
min-height: 3.5rem;
|
||||
padding-block: 0.4rem;
|
||||
}
|
||||
.wrn-navbar--width-compact .wrn-navbar__main {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -527,20 +721,39 @@ size: string = "default"
|
||||
background: var(--wrn-color-surface);
|
||||
border-bottom: 1px solid var(--wrn-color-border);
|
||||
box-shadow: 0 16px 32px rgb(15 23 42 / 0.12);
|
||||
max-height: calc(100dvh - 4rem);
|
||||
height: auto;
|
||||
max-height: calc(100dvh - 3.5rem);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
.wrn-navbar[data-mobile-panel="sheet"] .wrn-navbar__collapse {
|
||||
height: calc(100dvh - 3.5rem);
|
||||
}
|
||||
.wrn-navbar__collapse.is-open {
|
||||
display: flex;
|
||||
}
|
||||
.wrn-navbar:has(.wrn-navbar__collapse.is-open)::after {
|
||||
position: fixed;
|
||||
z-index: -1;
|
||||
inset: 0;
|
||||
background: rgb(15 23 42 / 0.42);
|
||||
content: "";
|
||||
}
|
||||
.wrn-navbar__menus,
|
||||
.wrn-navbar__actions {
|
||||
position: static;
|
||||
transform: none;
|
||||
width: 100%;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
.wrn-navbar__mega,
|
||||
.wrn-navbar__mega .wrn-mega__trigger {
|
||||
width: 100%;
|
||||
}
|
||||
.wrn-navbar__mega { display: none; }
|
||||
.wrn-navbar__mobile-mega-link { display: flex; }
|
||||
.wrn-navbar__menu-link,
|
||||
.wrn-navbar__dropdown > summary,
|
||||
.wrn-navbar__action {
|
||||
@@ -557,6 +770,12 @@ size: string = "default"
|
||||
overflow: hidden;
|
||||
animation: wrn-navbar-mobile-panel 180ms ease both;
|
||||
}
|
||||
.wrn-navbar__mega[data-full-width="true"] .wrn-mega__panel {
|
||||
position: static;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.wrn-navbar__dropdown:not([open]) > .wrn-navbar__panel,
|
||||
.wrn-navbar__dropdown[open] > .wrn-navbar__panel {
|
||||
transform: none;
|
||||
@@ -569,7 +788,43 @@ size: string = "default"
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) and (min-width: 901px) {
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__toggle { display: block; }
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__collapse {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
max-height: calc(100dvh - 3.5rem);
|
||||
padding: 0.75rem 1rem 1rem;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
background: var(--wrn-color-surface);
|
||||
border-bottom: 1px solid var(--wrn-color-border);
|
||||
box-shadow: 0 16px 32px rgb(15 23 42 / 0.12);
|
||||
}
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__collapse.is-open { display: flex; }
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__menus,
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__actions {
|
||||
position: static;
|
||||
transform: none;
|
||||
width: 100%;
|
||||
align-items: stretch;
|
||||
flex: none;
|
||||
flex-direction: column;
|
||||
}
|
||||
.wrn-navbar[data-collapse-at="1100px"] :is(.wrn-navbar__menu-link, .wrn-navbar__dropdown > summary, .wrn-navbar__action) { width: 100%; }
|
||||
.wrn-navbar[data-collapse-at="1100px"] :is(.wrn-navbar__mega, .wrn-navbar__mega .wrn-mega__trigger) { width: 100%; }
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__mega { display: none; }
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__mobile-mega-link { display: flex; }
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__panel { position: static; width: 100%; max-width: none; transform: none; }
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__mega[data-full-width="true"] .wrn-mega__panel { position: static; width: 100%; max-width: 100%; margin-top: 0.25rem; }
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.wrn-navbar[data-flush-mobile="false"] .wrn-navbar__main { padding-inline: 1rem; }
|
||||
.wrn-navbar__brand-copy small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -576,6 +576,14 @@ current: true
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Compiled empty slots retain inert template markers, so :empty alone
|
||||
cannot collapse their spacing in the rendered document. */
|
||||
.wrn-page-header__meta:not(:has(> :not(template))),
|
||||
.wrn-page-header__body:not(:has(> :not(template))),
|
||||
.wrn-page-header__actions--slot-only:not(:has(> :not(template))) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wrn-page-header__meta {
|
||||
margin-top: 1.1rem;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,13 @@ size: string = "default"
|
||||
{"label": "Amber", "value": "amber", "hex": "#d97706"}
|
||||
]
|
||||
compact: boolean = true
|
||||
iconOnly: boolean = true
|
||||
themeIcon: string = "icon-[lucide--sun-moon]"
|
||||
colorIcon: string = "icon-[lucide--palette]"
|
||||
languageIcon: string = "icon-[lucide--languages]"
|
||||
panelPlacement: string = "bottom-end"
|
||||
mobilePanel: string = "dropdown"
|
||||
showCurrentLanguage: boolean = true
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
@@ -37,11 +44,11 @@ size: string = "default"
|
||||
}
|
||||
|
||||
view {
|
||||
<div class="wrn-preferences wrn-component--color-{color} wrn-component--size-{size} {compact ? 'wrn-preferences--compact' : ''} {class}" data-wrn-preferences aria-label="Display and language preferences">
|
||||
<div class="wrn-preferences wrn-component--color-{color} wrn-component--size-{size} {compact ? 'wrn-preferences--compact' : ''} {class}" data-wrn-preferences data-icon-only="{iconOnly ? 'true' : 'false'}" data-panel-placement="{panelPlacement}" data-mobile-panel="{mobilePanel}" aria-label="Display and language preferences">
|
||||
<details class="wrn-preferences__menu" name="wrn-preference-menu">
|
||||
<summary aria-label="{themeLabel}" title="{themeLabel}">
|
||||
<span class="icon-[lucide--sun-moon]" aria-hidden="true"></span>
|
||||
{#if !compact}<span>{themeLabel}</span>{/if}
|
||||
<span class="{themeIcon}" aria-hidden="true"></span>
|
||||
{#if !iconOnly && !compact}<span>{themeLabel}</span>{/if}
|
||||
</summary>
|
||||
<div class="wrn-preferences__panel">
|
||||
<strong>{themeLabel}</strong>
|
||||
@@ -54,8 +61,8 @@ size: string = "default"
|
||||
|
||||
<details class="wrn-preferences__menu" name="wrn-preference-menu">
|
||||
<summary aria-label="{colorLabel}" title="{colorLabel}">
|
||||
<span class="icon-[lucide--palette]" aria-hidden="true"></span>
|
||||
{#if !compact}<span>{colorLabel}</span>{/if}
|
||||
<span class="{colorIcon}" aria-hidden="true"></span>
|
||||
{#if !iconOnly && !compact}<span>{colorLabel}</span>{/if}
|
||||
</summary>
|
||||
<div class="wrn-preferences__panel wrn-preferences__panel--colors">
|
||||
<strong>{colorLabel}</strong>
|
||||
@@ -69,9 +76,9 @@ size: string = "default"
|
||||
|
||||
<details class="wrn-preferences__menu wrn-preferences__menu--language" name="wrn-preference-menu">
|
||||
<summary aria-label="{languageLabel}" title="{languageLabel}">
|
||||
<span class="icon-[lucide--languages]" aria-hidden="true"></span>
|
||||
<span class="wrn-preferences__current-language" aria-hidden="true">EN</span>
|
||||
{#if !compact}<span>{languageLabel}</span>{/if}
|
||||
<span class="{languageIcon}" aria-hidden="true"></span>
|
||||
{#if showCurrentLanguage}<span class="wrn-preferences__current-language" aria-hidden="true">EN</span>{/if}
|
||||
{#if !iconOnly && !compact}<span>{languageLabel}</span>{/if}
|
||||
</summary>
|
||||
<div class="wrn-preferences__panel wrn-preferences__panel--language">
|
||||
<strong>{languageLabel}</strong>
|
||||
@@ -157,6 +164,9 @@ size: string = "default"
|
||||
animation: wrn-component-enter 160ms ease both;
|
||||
}
|
||||
|
||||
.wrn-preferences[data-panel-placement="bottom-start"] .wrn-preferences__panel { right: auto; left: 0; }
|
||||
.wrn-preferences[data-panel-placement="bottom-center"] .wrn-preferences__panel { right: auto; left: 50%; transform: translateX(-50%); }
|
||||
|
||||
.wrn-preferences__panel > strong {
|
||||
padding-inline: 0.25rem;
|
||||
font-size: 0.7rem;
|
||||
@@ -242,7 +252,7 @@ size: string = "default"
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.wrn-preferences__panel {
|
||||
.wrn-preferences[data-mobile-panel="sheet"] .wrn-preferences__panel {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
right: 1rem;
|
||||
@@ -250,6 +260,15 @@ size: string = "default"
|
||||
left: 1rem;
|
||||
width: auto;
|
||||
}
|
||||
.wrn-preferences[data-mobile-panel="dropdown"] .wrn-preferences__panel {
|
||||
position: absolute;
|
||||
top: calc(100% + 0.5rem);
|
||||
right: 0;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
width: max-content;
|
||||
max-width: min(22rem, calc(100vw - 2rem));
|
||||
}
|
||||
}
|
||||
|
||||
/* Shared component foundation. */
|
||||
|
||||
@@ -365,7 +365,9 @@ component RangeSlider {
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -24,6 +24,10 @@ component Scrollspy {
|
||||
active: string = ""
|
||||
label: string = "On this page"
|
||||
heading: string = ""
|
||||
sticky: boolean = false
|
||||
topOffset: string = "6rem"
|
||||
sectionOffset: string = "7rem"
|
||||
collapseAt: string = "800px"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
@@ -44,6 +48,7 @@ component Scrollspy {
|
||||
class='wrn-scrollspy {class}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
data-sticky='{sticky ? "true" : "false"}' data-collapse-at='{collapseAt}' style='--wrn-scrollspy-top: {topOffset}; --wrn-scrollspy-section-offset: {sectionOffset};'
|
||||
data-wrn-scrollspy="true"
|
||||
role="navigation"
|
||||
aria-label='{label}'
|
||||
@@ -74,6 +79,12 @@ component Scrollspy {
|
||||
--scrollspy-accent: var(--wrn-color-primary);
|
||||
max-width: 100%;
|
||||
}
|
||||
.wrn-scrollspy[data-sticky="true"] { position: sticky; top: var(--wrn-scrollspy-top); height: max-content; }
|
||||
.wrn-scrollspy__link { scroll-margin-top: var(--wrn-scrollspy-section-offset); }
|
||||
|
||||
.wrn-scrollspy:not(:has(.wrn-scrollspy__item)):not(:has(slot > *)) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wrn-scrollspy[data-color="secondary"] {
|
||||
--scrollspy-accent: var(--wrn-color-secondary);
|
||||
@@ -148,6 +159,7 @@ component Scrollspy {
|
||||
* line rather than a screenful.
|
||||
*/
|
||||
@media (max-width: 767px) {
|
||||
.wrn-scrollspy[data-collapse-at="800px"] { position: static; }
|
||||
.wrn-scrollspy__list {
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: max-content;
|
||||
|
||||
@@ -54,6 +54,7 @@ component Section {
|
||||
padding-block: 3rem;
|
||||
background: var(--section-tint);
|
||||
color: var(--section-ink);
|
||||
scroll-margin-top: var(--wrn-sticky-header-offset, 5rem);
|
||||
}
|
||||
|
||||
.wrn-section[data-spacing="sm"] {
|
||||
|
||||
@@ -20,6 +20,14 @@ component SplitHero {
|
||||
variant: string = "default"
|
||||
maxWidth: string = "xl"
|
||||
fullBleed: boolean = true
|
||||
gutter: string = "md"
|
||||
contentGutter: string = "md"
|
||||
flushMobile: boolean = false
|
||||
titleSize: string = "default"
|
||||
titleMaxWidth: string = ""
|
||||
descriptionWeight: string = "default"
|
||||
mobileTitleSize: string = "default"
|
||||
mobileStack: boolean = true
|
||||
backgroundImage: string = ""
|
||||
visualImage: string = ""
|
||||
visualAlt: string = ""
|
||||
@@ -45,6 +53,8 @@ component SplitHero {
|
||||
data-variant='{variant}'
|
||||
data-max-width='{maxWidth}'
|
||||
data-full-bleed='{fullBleed ? "true" : "false"}'
|
||||
data-gutter='{gutter}' data-content-gutter='{contentGutter}' data-flush-mobile='{flushMobile ? "true" : "false"}' data-title-size='{titleSize}' data-description-weight='{descriptionWeight}' data-mobile-title-size='{mobileTitleSize}' data-mobile-stack='{mobileStack ? "true" : "false"}'
|
||||
style='--wrn-split-title-max-width: {titleMaxWidth || "18ch"}'
|
||||
data-visual-style='{visualStyle}'
|
||||
class='wrn-split-hero {class}'
|
||||
>
|
||||
@@ -413,6 +423,7 @@ component SplitHero {
|
||||
}
|
||||
|
||||
.wrn-split-hero__title {
|
||||
max-width: var(--wrn-split-title-max-width);
|
||||
max-width: 15ch;
|
||||
margin: 1.45rem 0 0;
|
||||
color: var(--wrn-color-text);
|
||||
@@ -421,6 +432,8 @@ component SplitHero {
|
||||
line-height: 1.02;
|
||||
letter-spacing: -0.05em;
|
||||
}
|
||||
.wrn-split-hero[data-title-size="xl"] .wrn-split-hero__title { font-size: clamp(3rem, 5.5vw, 5rem); }
|
||||
.wrn-split-hero[data-description-weight="medium"] .wrn-split-hero__description { font-weight: 500; }
|
||||
|
||||
.wrn-split-hero[data-size="compact"] .wrn-split-hero__title,
|
||||
.wrn-split-hero[data-size="sm"] .wrn-split-hero__title {
|
||||
@@ -771,6 +784,7 @@ component SplitHero {
|
||||
.wrn-split-hero[data-ratio="content"] .wrn-split-hero__layout {
|
||||
grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
|
||||
}
|
||||
.wrn-split-hero[data-ratio="wide-copy"] .wrn-split-hero__layout { grid-template-columns: minmax(0, 1.12fr) minmax(20rem, 0.88fr); }
|
||||
|
||||
.wrn-split-hero[data-ratio="visual"] .wrn-split-hero__layout {
|
||||
grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
|
||||
@@ -786,6 +800,8 @@ component SplitHero {
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.wrn-split-hero[data-flush-mobile="false"] .wrn-split-hero__inner { width: calc(100% - 2rem); }
|
||||
.wrn-split-hero[data-mobile-title-size="lg"] .wrn-split-hero__title { font-size: clamp(2.5rem, 12vw, 3.5rem); }
|
||||
.wrn-split-hero__inner {
|
||||
padding-block: 3.25rem;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@ component StatsBar {
|
||||
color: string = "primary"
|
||||
variant: string = "raised"
|
||||
maxWidth: string = "xl"
|
||||
radius: string = "default"
|
||||
shadow: string = "default"
|
||||
gutter: string = "md"
|
||||
outerSpacing: string = "none"
|
||||
mobileColumns: number = 1
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
@@ -24,6 +29,7 @@ component StatsBar {
|
||||
data-color='{color}'
|
||||
data-variant='{variant}'
|
||||
data-max-width='{maxWidth}'
|
||||
data-radius='{radius}' data-shadow='{shadow}' data-gutter='{gutter}' data-outer-spacing='{outerSpacing}' data-mobile-columns='{mobileColumns}'
|
||||
class='wrn-stats-bar {class}'
|
||||
aria-label="Key statistics"
|
||||
>
|
||||
@@ -91,6 +97,8 @@ component StatsBar {
|
||||
</div>
|
||||
{/if}
|
||||
</article>
|
||||
{:empty}
|
||||
<div class="wrn-stats-bar__empty" role="status">No statistics are available.</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,6 +106,15 @@ component StatsBar {
|
||||
}
|
||||
|
||||
style {
|
||||
.wrn-stats-bar__empty {
|
||||
grid-column: 1 / -1;
|
||||
padding: 1.25rem;
|
||||
color: var(--wrn-color-text-muted);
|
||||
background: var(--wrn-color-surface-soft);
|
||||
border: 1px dashed var(--wrn-color-border);
|
||||
border-radius: 0.9rem;
|
||||
text-align: center;
|
||||
}
|
||||
.wrn-stats-bar {
|
||||
--stats-accent: var(--wrn-color-primary);
|
||||
--stats-contrast: var(--wrn-color-primary-contrast);
|
||||
@@ -135,21 +152,43 @@ component StatsBar {
|
||||
}
|
||||
|
||||
.wrn-stats-bar__inner {
|
||||
width: min(calc(100% - 2rem), 80rem);
|
||||
--stats-max-width: 80rem;
|
||||
--stats-gutter-width: 0rem;
|
||||
|
||||
width: min(
|
||||
calc(100% - var(--stats-gutter-width)),
|
||||
calc(var(--stats-max-width) - var(--stats-gutter-width))
|
||||
);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.wrn-stats-bar[data-gutter="sm"] .wrn-stats-bar__inner {
|
||||
--stats-gutter-width: 2rem;
|
||||
}
|
||||
|
||||
.wrn-stats-bar[data-gutter="md"] .wrn-stats-bar__inner {
|
||||
--stats-gutter-width: 4rem;
|
||||
}
|
||||
|
||||
.wrn-stats-bar[data-gutter="lg"] .wrn-stats-bar__inner {
|
||||
--stats-gutter-width: 6rem;
|
||||
}
|
||||
.wrn-stats-bar[data-outer-spacing="md"] { margin-block: 2rem; }
|
||||
.wrn-stats-bar[data-radius="xl"] .wrn-stats-bar__surface { overflow: hidden; border-radius: 1.25rem; }
|
||||
.wrn-stats-bar[data-shadow="sm"] .wrn-stats-bar__surface { box-shadow: 0 10px 30px rgb(15 23 42 / 0.05); }
|
||||
.wrn-stats-bar[data-variant="card"] .wrn-stats-bar__surface { border: 1px solid var(--wrn-color-border); background: var(--wrn-color-surface); }
|
||||
|
||||
.wrn-stats-bar[data-max-width="compact"] .wrn-stats-bar__inner {
|
||||
width: min(calc(100% - 2rem), 64rem);
|
||||
--stats-max-width: 64rem;
|
||||
}
|
||||
|
||||
.wrn-stats-bar[data-max-width="lg"] .wrn-stats-bar__inner {
|
||||
width: min(calc(100% - 2rem), 72rem);
|
||||
--stats-max-width: 72rem;
|
||||
}
|
||||
|
||||
.wrn-stats-bar[data-max-width="wide"] .wrn-stats-bar__inner,
|
||||
.wrn-stats-bar[data-max-width="2xl"] .wrn-stats-bar__inner {
|
||||
width: min(calc(100% - 2rem), 90rem);
|
||||
--stats-max-width: 90rem;
|
||||
}
|
||||
|
||||
.wrn-stats-bar[data-max-width="full"] .wrn-stats-bar__inner {
|
||||
@@ -469,6 +508,10 @@ component StatsBar {
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.wrn-stats-bar[data-mobile-columns="1"] .wrn-stats-bar__surface { grid-template-columns: 1fr; }
|
||||
.wrn-stats-bar[data-gutter="sm"] .wrn-stats-bar__inner,
|
||||
.wrn-stats-bar[data-gutter="md"] .wrn-stats-bar__inner { --stats-gutter-width: 2rem; }
|
||||
.wrn-stats-bar[data-gutter="lg"] .wrn-stats-bar__inner { --stats-gutter-width: 3rem; }
|
||||
.wrn-stats-bar[data-max-width="full"] .wrn-stats-bar__surface {
|
||||
border-inline: 0;
|
||||
border-radius: 0;
|
||||
|
||||
@@ -649,7 +649,9 @@ component StrongPassword {
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -41,6 +41,18 @@ component Tabs {
|
||||
return String(item.value || item.id || index)
|
||||
}
|
||||
|
||||
shared function tabId(item, index) {
|
||||
return "wrn-tab-" + valueOf(item, index)
|
||||
}
|
||||
|
||||
shared function panelId(item, index) {
|
||||
return "wrn-panel-" + valueOf(item, index)
|
||||
}
|
||||
|
||||
shared function panelSlot(item, index) {
|
||||
return "panel-" + valueOf(item, index)
|
||||
}
|
||||
|
||||
/*
|
||||
* In url mode the query parameter is the source of truth, not local state.
|
||||
*
|
||||
@@ -130,8 +142,8 @@ component Tabs {
|
||||
role="tab"
|
||||
data-value='{valueOf(item, index)}'
|
||||
data-wrn-roving-item="true"
|
||||
id='wrn-tab-{valueOf(item, index)}'
|
||||
aria-controls='wrn-panel-{valueOf(item, index)}'
|
||||
id='{tabId(item, index)}'
|
||||
aria-controls='{panelId(item, index)}'
|
||||
aria-selected='{isSelected(item, index) ? "true" : "false"}'
|
||||
aria-disabled='{item.disabled ? "true" : "false"}'
|
||||
@click='selectTab(item, index, event)'
|
||||
@@ -148,8 +160,8 @@ component Tabs {
|
||||
<div
|
||||
class="wrn-tabs__panel"
|
||||
role="tabpanel"
|
||||
id='wrn-panel-{valueOf(item, index)}'
|
||||
aria-labelledby='wrn-tab-{valueOf(item, index)}'
|
||||
id='{panelId(item, index)}'
|
||||
aria-labelledby='{tabId(item, index)}'
|
||||
tabindex="0"
|
||||
data-show='isSelected(item, index)'
|
||||
>
|
||||
@@ -158,7 +170,7 @@ component Tabs {
|
||||
</h3>
|
||||
<p class="wrn-tabs__description" data-show="item.description">{item.description}</p>
|
||||
<div class="wrn-tabs__content" data-show="item.content">{item.content}</div>
|
||||
<slot name="panel-{valueOf(item, index)}"></slot>
|
||||
<slot name='{panelSlot(item, index)}'></slot>
|
||||
</div>
|
||||
{/each}
|
||||
<slot />
|
||||
|
||||
@@ -249,7 +249,9 @@ size: string = "default"
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -10,6 +10,7 @@ component Timeline {
|
||||
description: string = ""
|
||||
items: unknown[] = []
|
||||
variant: string = "default"
|
||||
numbered: boolean = false
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
@@ -18,6 +19,9 @@ component Timeline {
|
||||
data-ui-component="Timeline"
|
||||
aria-label='{title}'
|
||||
class='wrn-timeline {class}'
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-variant='{variant}'
|
||||
>
|
||||
{#if title || description}
|
||||
<header class="wrn-timeline__header">
|
||||
@@ -34,13 +38,18 @@ component Timeline {
|
||||
{#each items as item, index}
|
||||
<li
|
||||
class="wrn-timeline__item"
|
||||
data-interactive='{item.href ? "true" : "false"}'
|
||||
@click='output.select({ item: item, index: index })'
|
||||
>
|
||||
<span
|
||||
class="wrn-timeline__marker"
|
||||
data-status='{item.status || "default"}'
|
||||
>
|
||||
<span class='{item.icon || "icon-[lucide--circle]"}' aria-hidden="true"></span>
|
||||
{#if numbered}
|
||||
<span class="wrn-timeline__number" aria-hidden="true">{index + 1}</span>
|
||||
{:else}
|
||||
<span class='{item.icon || "icon-[lucide--circle-check]"}' aria-hidden="true"></span>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<article
|
||||
@@ -81,22 +90,28 @@ component Timeline {
|
||||
}
|
||||
|
||||
style {
|
||||
.wrn-timeline { width: 100%; }
|
||||
.wrn-timeline { --wrn-timeline-accent: var(--wrn-color-primary); width: 100%; min-width: 0; color: var(--wrn-color-text); }
|
||||
.wrn-timeline[data-color="success"] { --wrn-timeline-accent: var(--wrn-color-success); }
|
||||
.wrn-timeline[data-color="info"] { --wrn-timeline-accent: var(--wrn-color-info); }
|
||||
.wrn-timeline__header { margin-bottom: 1.5rem; }
|
||||
.wrn-timeline__title { color: var(--wrn-color-text); font-size: 1.25rem; font-weight: 700; }
|
||||
.wrn-timeline__description { max-width: 42rem; margin-top: 0.5rem; color: var(--wrn-color-text-muted); line-height: 1.75rem; }
|
||||
.wrn-timeline__list { position: relative; margin-left: 1rem; border-left: 1px solid var(--wrn-color-border); }
|
||||
.wrn-timeline__item { position: relative; padding: 0 0 2rem 2rem; cursor: pointer; }
|
||||
.wrn-timeline__list { position: relative; display: grid; gap: 1rem; margin: 0; padding: 0 0 0 3rem; list-style: none; }
|
||||
.wrn-timeline__list::before { position: absolute; inset-block: 1rem; inset-inline-start: 1rem; width: 2px; border-radius: 999px; background: color-mix(in srgb, var(--wrn-timeline-accent) 30%, var(--wrn-color-border)); content: ""; }
|
||||
.wrn-timeline__item { position: relative; min-width: 0; }
|
||||
.wrn-timeline__item:last-child { padding-bottom: 0; }
|
||||
.wrn-timeline__marker {
|
||||
position: absolute; top: 0; left: -1.05rem; display: flex; width: 2rem; height: 2rem;
|
||||
position: absolute; z-index: 1; top: 1rem; inset-inline-start: -3rem; display: flex; width: 2rem; height: 2rem;
|
||||
align-items: center; justify-content: center; border: 4px solid var(--wrn-color-background);
|
||||
border-radius: 50%; color: var(--wrn-color-on-primary); background: var(--wrn-color-primary); box-shadow: var(--wrn-shadow-1);
|
||||
border-radius: 50%; color: var(--wrn-color-on-primary); background: var(--wrn-timeline-accent); box-shadow: var(--wrn-shadow-1);
|
||||
}
|
||||
.wrn-timeline__marker:is([data-status="complete"], [data-status="success"]) { background: var(--wrn-color-success); }
|
||||
.wrn-timeline__marker:is([data-status="warning"], [data-status="pending"]) { background: var(--wrn-color-warning); }
|
||||
.wrn-timeline__marker:is([data-status="danger"], [data-status="failed"]) { background: var(--wrn-color-danger); }
|
||||
.wrn-timeline__card { padding: 1.25rem; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 1.5); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); }
|
||||
.wrn-timeline__number { font-size: 0.75rem; font-weight: 800; }
|
||||
.wrn-timeline__card { min-width: 0; padding: 1.25rem; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 1.5); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); transition: border-color 160ms ease, transform 160ms ease, box-shadow 160ms ease; }
|
||||
.wrn-timeline__item[data-interactive="true"] { cursor: pointer; }
|
||||
.wrn-timeline__item[data-interactive="true"]:hover .wrn-timeline__card { border-color: color-mix(in srgb, var(--wrn-timeline-accent) 45%, var(--wrn-color-border)); transform: translateY(-2px); box-shadow: var(--wrn-shadow-2); }
|
||||
.wrn-timeline__card[data-variant="minimal"] { padding: 0; border-color: transparent; background: transparent; box-shadow: none; }
|
||||
.wrn-timeline__card-header { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.wrn-timeline__item-title { color: var(--wrn-color-text); font-weight: 700; }
|
||||
@@ -110,5 +125,11 @@ component Timeline {
|
||||
@media (min-width: 40rem) {
|
||||
.wrn-timeline__card-header { flex-direction: row; align-items: flex-start; justify-content: space-between; }
|
||||
}
|
||||
@media (max-width: 39.99rem) {
|
||||
.wrn-timeline__list { padding-inline-start: 2.5rem; }
|
||||
.wrn-timeline__list::before { inset-inline-start: 0.75rem; }
|
||||
.wrn-timeline__marker { inset-inline-start: -2.5rem; }
|
||||
.wrn-timeline__card { padding: 1rem; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +492,9 @@ component TogglePassword {
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -360,7 +360,9 @@ size: string = "default"
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -315,7 +315,9 @@ component Input {
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -221,7 +221,9 @@ size: string = "default"
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -244,7 +244,9 @@ size: string = "default"
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -243,7 +243,9 @@ component Switch {
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -223,7 +223,9 @@ size: string = "default"
|
||||
}
|
||||
.wrn-next__field-heading label {
|
||||
color: var(--wrn-color-text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.wrn-next__field-hint,
|
||||
.wrn-next__field-help {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.8.23",
|
||||
"version": "0.8.56",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./navigation": "./src/navigation.ts",
|
||||
"./registry": "./src/registry.ts",
|
||||
"./components/*": "./components/*",
|
||||
"./component-catalog.json": "./component-catalog.json",
|
||||
|
||||
@@ -24,4 +24,22 @@
|
||||
* server-only subpath. Import from there instead if you need them.
|
||||
*/
|
||||
|
||||
export {};
|
||||
export type {
|
||||
MegaMenuColumn,
|
||||
MegaMenuDensity,
|
||||
MegaMenuFeatured,
|
||||
MegaMenuPanelAlign,
|
||||
MegaMenuPanelWidth,
|
||||
MegaMenuRailItem,
|
||||
MegaMenuVariant,
|
||||
NavbarAction,
|
||||
NavbarBrand,
|
||||
NavbarDropdownGroup,
|
||||
NavbarDropdownItem,
|
||||
NavbarItem,
|
||||
NavbarItemType,
|
||||
NavbarLinkItem,
|
||||
NavbarMegaMenuData,
|
||||
NavbarMegaMenuItem,
|
||||
NavigationLink,
|
||||
} from "./navigation";
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/** Shared, serializable navigation contracts for Navbar and application configs. */
|
||||
|
||||
export type NavbarItemType = "link" | "dropdown" | "mega";
|
||||
export type MegaMenuVariant = "default" | "icon-grid" | "catalog" | "dark";
|
||||
export type MegaMenuDensity = "compact" | "default" | "comfortable";
|
||||
export type MegaMenuPanelWidth = "sm" | "md" | "lg" | "xl" | "2xl" | "full";
|
||||
export type MegaMenuPanelAlign = "start" | "center" | "end";
|
||||
|
||||
export interface NavigationLink {
|
||||
label: string;
|
||||
href: string;
|
||||
value?: string;
|
||||
labelKey?: string;
|
||||
description?: string;
|
||||
descriptionKey?: string;
|
||||
icon?: string;
|
||||
image?: string;
|
||||
imageAlt?: string;
|
||||
eyebrow?: string;
|
||||
badge?: string;
|
||||
target?: string;
|
||||
rel?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface NavbarLinkItem extends NavigationLink {
|
||||
type?: "link";
|
||||
}
|
||||
|
||||
export interface NavbarDropdownGroup {
|
||||
label?: string;
|
||||
labelKey?: string;
|
||||
description?: string;
|
||||
descriptionKey?: string;
|
||||
items: NavigationLink[];
|
||||
/** Compatibility alias accepted by Navbar. Prefer `items`. */
|
||||
children?: NavigationLink[];
|
||||
}
|
||||
|
||||
export interface NavbarDropdownItem {
|
||||
type: "dropdown";
|
||||
label: string;
|
||||
value?: string;
|
||||
labelKey?: string;
|
||||
description?: string;
|
||||
descriptionKey?: string;
|
||||
icon?: string;
|
||||
columns?: number;
|
||||
items: Array<NavigationLink | NavbarDropdownGroup>;
|
||||
/** Compatibility alias accepted by Navbar. Prefer `items`. */
|
||||
children?: Array<NavigationLink | NavbarDropdownGroup>;
|
||||
}
|
||||
|
||||
export interface MegaMenuColumn {
|
||||
heading?: string;
|
||||
eyebrow?: string;
|
||||
description?: string;
|
||||
image?: string;
|
||||
imageAlt?: string;
|
||||
items: NavigationLink[];
|
||||
actionLabel?: string;
|
||||
actionHref?: string;
|
||||
}
|
||||
|
||||
export interface MegaMenuRailItem extends NavigationLink {}
|
||||
|
||||
export interface MegaMenuFeatured {
|
||||
eyebrow?: string;
|
||||
image?: string;
|
||||
imageAlt?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
actionLabel?: string;
|
||||
actionHref?: string;
|
||||
}
|
||||
|
||||
export interface NavbarMegaMenuData {
|
||||
variant?: MegaMenuVariant;
|
||||
density?: MegaMenuDensity;
|
||||
panelWidth?: MegaMenuPanelWidth;
|
||||
panelAlign?: MegaMenuPanelAlign;
|
||||
fullWidth?: boolean;
|
||||
activeCategory?: string;
|
||||
columnCount?: number;
|
||||
columns: MegaMenuColumn[];
|
||||
rail?: MegaMenuRailItem[];
|
||||
featured?: MegaMenuFeatured;
|
||||
actionLabel?: string;
|
||||
actionHref?: string;
|
||||
actionIcon?: string;
|
||||
footer?: string;
|
||||
}
|
||||
|
||||
export interface NavbarMegaMenuItem {
|
||||
type: "mega";
|
||||
label: string;
|
||||
value?: string;
|
||||
labelKey?: string;
|
||||
icon?: string;
|
||||
mega: NavbarMegaMenuData;
|
||||
}
|
||||
|
||||
export type NavbarItem = NavbarLinkItem | NavbarDropdownItem | NavbarMegaMenuItem;
|
||||
|
||||
export interface NavbarAction extends NavigationLink {
|
||||
variant?: "link" | "outline" | "primary";
|
||||
}
|
||||
|
||||
export interface NavbarBrand {
|
||||
label?: string;
|
||||
labelKey?: string;
|
||||
description?: string;
|
||||
descriptionKey?: string;
|
||||
href?: string;
|
||||
ariaLabel?: string;
|
||||
icon?: string;
|
||||
logo?: string;
|
||||
logoAlt?: string;
|
||||
logoWidth?: number | string;
|
||||
logoHeight?: number | string;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import type { NavbarItem } from "../src/navigation";
|
||||
|
||||
const items = [
|
||||
{ type: "link", label: "Pricing", href: "/pricing" },
|
||||
{
|
||||
type: "dropdown",
|
||||
label: "Company",
|
||||
items: [{ label: "About", href: "/about" }],
|
||||
},
|
||||
{
|
||||
type: "mega",
|
||||
label: "Products",
|
||||
mega: {
|
||||
variant: "icon-grid",
|
||||
density: "compact",
|
||||
panelWidth: "2xl",
|
||||
panelAlign: "center",
|
||||
columnCount: 3,
|
||||
fullWidth: true,
|
||||
rail: [{ label: "All products", href: "/products", icon: "icon-products" }],
|
||||
columns: [
|
||||
{
|
||||
heading: "Work",
|
||||
items: [
|
||||
{
|
||||
label: "Projects",
|
||||
href: "/projects",
|
||||
icon: "icon-projects",
|
||||
description: "Plan and deliver work",
|
||||
badge: "New",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
featured: {
|
||||
title: "What's new",
|
||||
image: "/new.png",
|
||||
actionLabel: "Explore",
|
||||
actionHref: "/new",
|
||||
},
|
||||
footer: "One connected platform",
|
||||
actionLabel: "View all products",
|
||||
actionHref: "/products",
|
||||
},
|
||||
},
|
||||
] satisfies NavbarItem[];
|
||||
|
||||
test("navbar item contract covers links, dropdowns, and complete mega menus", () => {
|
||||
expect(items.map((item) => item.type)).toEqual(["link", "dropdown", "mega"]);
|
||||
});
|
||||
@@ -78,6 +78,28 @@ function openingTags(source: string): string[] {
|
||||
}
|
||||
|
||||
describe("@wrnexus/ui redesign component pack", () => {
|
||||
test("Hero full bleed does not use scrollbar-sensitive viewport units", async () => {
|
||||
const source = await readFile(join(componentsDir, "Hero.wrn"), "utf8");
|
||||
expect(source).not.toContain("width: 100vw");
|
||||
expect(source).not.toContain("width: 100dvw");
|
||||
expect(source).toContain('.wrn-hero[data-full-bleed="true"]');
|
||||
});
|
||||
|
||||
test("Timeline supports numbered data items without styling every row as interactive", async () => {
|
||||
const source = await readFile(join(componentsDir, "Timeline.wrn"), "utf8");
|
||||
expect(source).toContain("numbered: boolean = false");
|
||||
expect(source).toContain("data-interactive");
|
||||
expect(source).toContain('data-interactive="true"');
|
||||
expect(source).toContain('.wrn-timeline__item[data-interactive="true"] { cursor: pointer; }');
|
||||
});
|
||||
|
||||
test("Blockquote provides constrained contained presentation", async () => {
|
||||
const source = await readFile(join(componentsDir, "Blockquote.wrn"), "utf8");
|
||||
expect(source).toContain('maxWidth: string = "xl"');
|
||||
expect(source).toContain('data-variant="spotlight"');
|
||||
expect(source).toContain('data-max-width="full"');
|
||||
});
|
||||
|
||||
test("contains every replacement and new component", async () => {
|
||||
const files = new Set(await readdir(componentsDir));
|
||||
|
||||
@@ -118,6 +140,77 @@ describe("@wrnexus/ui redesign component pack", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("Navbar only displays its utility bar when the topbar slot has rendered content", async () => {
|
||||
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
|
||||
|
||||
expect(source).toContain(".wrn-navbar__topbar:not(:has(> *))");
|
||||
expect(source).toContain(".wrn-navbar__topbar:has(> *)");
|
||||
expect(source).not.toContain(".wrn-navbar__topbar:not(:empty)");
|
||||
});
|
||||
|
||||
test("Navbar renders its brand prop independently from the optional brand slot", async () => {
|
||||
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
|
||||
|
||||
expect(source).toContain('<slot name="brand" />');
|
||||
expect(source).toContain("brand.logo || brand.icon || brand.label || brand.description");
|
||||
expect(source.indexOf("brand.logo || brand.icon || brand.label || brand.description")).toBeLessThan(
|
||||
source.indexOf('<slot name="brand" />'),
|
||||
);
|
||||
});
|
||||
|
||||
test("Navbar renders slotted utilities before its built-in actions", async () => {
|
||||
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
|
||||
const actionsStart = source.indexOf('<div class="wrn-navbar__actions">');
|
||||
const slotIndex = source.indexOf('<slot name="actions" />', actionsStart);
|
||||
const actionsLoop = source.indexOf("{#each actions as item}", actionsStart);
|
||||
|
||||
expect(slotIndex).toBeGreaterThan(actionsStart);
|
||||
expect(slotIndex).toBeLessThan(actionsLoop);
|
||||
});
|
||||
|
||||
test("Navbar passes structured MegaMenu props as expressions", async () => {
|
||||
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
|
||||
|
||||
expect(source).toContain("columns={megaColumns(item)}");
|
||||
expect(source).toContain("rail={megaData(item).rail || []}");
|
||||
expect(source).toContain("featured={megaData(item).featured || {}}");
|
||||
expect(source).toContain("fullWidth={megaData(item).fullWidth || false}");
|
||||
expect(source).not.toContain('columns="{megaColumns(item)}"');
|
||||
});
|
||||
|
||||
test("Navbar replaces mega panels with direct links in collapsed navigation", async () => {
|
||||
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
|
||||
|
||||
expect(source).toContain("wrn-navbar__mobile-mega-link");
|
||||
expect(source).toContain("item.href || megaData(item).actionHref");
|
||||
expect(source).toContain('.wrn-navbar__mega { display: none; }');
|
||||
expect(source).toContain('.wrn-navbar__mobile-mega-link { display: flex; }');
|
||||
});
|
||||
|
||||
test("Navbar supports in-flow floating and elevated menu panels", async () => {
|
||||
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
|
||||
const ast = parse(source);
|
||||
const defaults = new Map(ast.props.map((prop) => [prop.name, prop.default]));
|
||||
|
||||
expect(defaults.get("floating")).toBe("false");
|
||||
expect(defaults.get("startFloating")).toBe('"0px"');
|
||||
expect(source).toContain("wrn-navbar--floating");
|
||||
expect(source).toContain("--wrn-navbar-floating-start");
|
||||
expect(source).toContain("z-index: 1100");
|
||||
});
|
||||
|
||||
test("CTASection exposes responsive section spacing", async () => {
|
||||
const source = await readFile(join(componentsDir, "CTASection.wrn"), "utf8");
|
||||
const ast = parse(source);
|
||||
const defaults = new Map(ast.props.map((prop) => [prop.name, prop.default]));
|
||||
|
||||
expect(defaults.get("spacing")).toBe('"none"');
|
||||
expect(source).toContain("data-spacing='{spacing}'");
|
||||
expect(source).toContain('.wrn-cta-section[data-spacing="sm"]');
|
||||
expect(source).toContain('.wrn-cta-section[data-spacing="md"]');
|
||||
expect(source).toContain('.wrn-cta-section[data-spacing="lg"]');
|
||||
});
|
||||
|
||||
test("existing public defaults remain release compatible", async () => {
|
||||
const expectations: Record<string, string[]> = {
|
||||
Container: [
|
||||
|
||||
+194
-10
@@ -32,6 +32,19 @@ test("Textarea binds its value without rendering hydration markup as user conten
|
||||
expect(source).not.toContain(">{value}</textarea>");
|
||||
});
|
||||
|
||||
test("form field labels use a stable control-label size instead of inherited heading scale", () => {
|
||||
const fieldComponents = uiComponentNames().filter((name) =>
|
||||
readFileSync(uiComponentPath(name), "utf8").includes(".wrn-next__field-heading label"),
|
||||
);
|
||||
expect(fieldComponents.length).toBeGreaterThan(0);
|
||||
for (const name of fieldComponents) {
|
||||
const source = readFileSync(uiComponentPath(name), "utf8");
|
||||
expect(source).toMatch(
|
||||
/\.wrn-next__field-heading label\s*\{[^}]*font-size: 0\.8125rem;[^}]*line-height: 1\.35;/s,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("global UI CSS stays below its migration ratchet", () => {
|
||||
const css = uiCss();
|
||||
// Component selectors belong to their owning .wrn files. Keep this asset to
|
||||
@@ -239,7 +252,9 @@ test("navbar supports brands, nested menus, actions, utility slots, and public e
|
||||
expect(defaults.get("openOnHover")).toBe("false");
|
||||
expect(defaults.get("maxWidth")).toBe('"full"');
|
||||
expect(source).toContain("item.children");
|
||||
expect(source).toContain("child.children");
|
||||
expect(source).toContain("dropdownGroupItems(child)");
|
||||
expect(source).toContain('itemType(item) === "mega"');
|
||||
expect(source).toContain("megaData(item).featured");
|
||||
expect(source).toContain("wrn-navbar__dropdown--{item.type || 'dropdown'}");
|
||||
const outputs = new Set(contract.outputs.map((output) => output.name));
|
||||
for (const event of ["toggle", "open", "close", "select", "action"]) {
|
||||
@@ -304,13 +319,19 @@ test("navbar renders a brand, nested dropdowns, mega groups, and actions", async
|
||||
{
|
||||
label: "Safety",
|
||||
type: "mega",
|
||||
columns: 2,
|
||||
children: [
|
||||
{
|
||||
label: "Resources",
|
||||
children: [{ label: "Guidance", href: "/guidance" }],
|
||||
},
|
||||
],
|
||||
mega: {
|
||||
variant: "icon-grid",
|
||||
columnCount: 2,
|
||||
rail: [{ label: "Overview", href: "/safety" }],
|
||||
columns: [
|
||||
{
|
||||
heading: "Resources",
|
||||
items: [{ label: "Guidance", href: "/guidance", badge: "New" }],
|
||||
},
|
||||
],
|
||||
featured: { title: "Safety report", actionLabel: "Read report" },
|
||||
actionLabel: "All safety resources",
|
||||
},
|
||||
},
|
||||
],
|
||||
actions: [{ label: "Sign in", href: "/login", variant: "primary" }],
|
||||
@@ -320,7 +341,9 @@ test("navbar renders a brand, nested dropdowns, mega groups, and actions", async
|
||||
expect(html).toContain("Public portal");
|
||||
expect(html).toContain("Reports");
|
||||
expect(html).toContain("Guidance");
|
||||
expect(html).toContain("wrn-navbar__dropdown--mega");
|
||||
expect(html).toContain("wrn-navbar__mega");
|
||||
expect(html).toContain("Safety report");
|
||||
expect(html).toContain("All safety resources");
|
||||
expect(html).toContain("data-wrn-navbar");
|
||||
expect(html).toContain('name="wrn-navbar-menu"');
|
||||
expect(html).toContain("Sign in");
|
||||
@@ -2856,8 +2879,13 @@ test("tabs use wrn classes and declare real outputs instead of raw events", asyn
|
||||
expect(tabs).toHaveLength(2);
|
||||
expect(tabs[0]!.getAttribute("aria-selected")).toBe("true");
|
||||
expect(tabs[1]!.getAttribute("aria-selected")).toBe("false");
|
||||
expect(tabs[0]!.id).toBe("wrn-tab-overview");
|
||||
expect(tabs[0]!.getAttribute("aria-controls")).toBe("wrn-panel-overview");
|
||||
expect(dom.querySelector(".wrn-tabs")).not.toBeNull();
|
||||
expect(dom.querySelectorAll('[role="tabpanel"]')).toHaveLength(2);
|
||||
const panels = dom.querySelectorAll('[role="tabpanel"]');
|
||||
expect(panels).toHaveLength(2);
|
||||
expect(panels[0]!.id).toBe("wrn-panel-overview");
|
||||
expect(panels[0]!.getAttribute("aria-labelledby")).toBe("wrn-tab-overview");
|
||||
});
|
||||
|
||||
test("tabs in url mode declare the query parameter they sync to", async () => {
|
||||
@@ -2918,6 +2946,10 @@ test("mega menu renders column groups in an anchored panel", async () => {
|
||||
const trigger = dom.querySelector(".wrn-mega__trigger") as HTMLElement;
|
||||
expect(trigger.getAttribute("aria-expanded")).toBe("false");
|
||||
expect(trigger.getAttribute("aria-haspopup")).toBe("true");
|
||||
expect(trigger.getAttribute("aria-label")).toBe("Products");
|
||||
expect(dom.querySelector(".wrn-mega__trigger-label")?.getAttribute("data-label")).toBe(
|
||||
"Products",
|
||||
);
|
||||
|
||||
const panel = dom.querySelector(".wrn-mega__panel") as HTMLElement;
|
||||
expect(panel.getAttribute("data-wrn-anchored")).toBe("true");
|
||||
@@ -2927,6 +2959,14 @@ test("mega menu renders column groups in an anchored panel", async () => {
|
||||
// Single level by design: a mega panel shows breadth flat, so there is no
|
||||
// nested submenu markup to find.
|
||||
expect(source).not.toContain("wrn-mega__submenu");
|
||||
expect(source).toContain('.wrn-mega[data-variant="default"] .wrn-mega__panel');
|
||||
expect(source).toContain("box-sizing: border-box");
|
||||
});
|
||||
|
||||
test("announcement full-width surface does not create viewport scrollbar overflow", () => {
|
||||
const source = readFileSync(uiComponentPath("AnnouncementBar"), "utf8");
|
||||
expect(source).not.toContain("width: 100vw");
|
||||
expect(source).not.toContain("calc(50% - 50vw)");
|
||||
});
|
||||
|
||||
test("mega menu columns take roving focus and survive an empty column list", async () => {
|
||||
@@ -2938,6 +2978,76 @@ test("mega menu columns take roving focus and survive an empty column list", asy
|
||||
expect(dom.querySelector('[data-wrn-roving="both"]')).not.toBeNull();
|
||||
});
|
||||
|
||||
test("catalog mega menu renders only the active rail category", async () => {
|
||||
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
|
||||
expect(source).toContain(".wrn-mega__list > li");
|
||||
expect(source).toContain("overflow-wrap: anywhere;");
|
||||
const html = await renderComponent(source, {
|
||||
label: "Products",
|
||||
variant: "catalog",
|
||||
activeCategory: "sales",
|
||||
rail: [
|
||||
{ label: "Platform", value: "platform" },
|
||||
{ label: "Sales", value: "sales" },
|
||||
],
|
||||
columns: [
|
||||
{ heading: "Platform", items: [{ label: "Home", href: "/home" }] },
|
||||
{ heading: "Sales", items: [{ label: "CRM", href: "/crm" }] },
|
||||
],
|
||||
});
|
||||
const dom = mountHtml(html);
|
||||
|
||||
expect(dom.querySelectorAll(".wrn-mega__rail-link")).toHaveLength(2);
|
||||
expect(dom.querySelectorAll(".wrn-mega__column")).toHaveLength(1);
|
||||
expect(dom.querySelector(".wrn-mega__heading")?.textContent).toContain("Sales");
|
||||
expect(dom.querySelector('[aria-pressed="true"]')?.textContent).toContain("Sales");
|
||||
expect(source).toContain("overflow-y: auto");
|
||||
expect(source).not.toContain("@mouseenter='chooseCategory(item, index)'");
|
||||
expect(dom.querySelector(".wrn-mega__column-header")?.textContent).toContain("Sales");
|
||||
expect(dom.querySelectorAll(".wrn-mega__link-icon")).toHaveLength(1);
|
||||
expect(dom.querySelector(".wrn-mega__link-icon--fallback svg")).not.toBeNull();
|
||||
expect(dom.querySelectorAll(".wrn-mega__link-description")).toHaveLength(0);
|
||||
expect(source).toContain("border-bottom: 1px solid var(--wrn-color-border)");
|
||||
expect(source).toContain(".wrn-mega__rail-icon > span");
|
||||
expect(source).toContain("color: currentColor !important");
|
||||
});
|
||||
|
||||
test("mega menu supports rails, icons, badges, media, featured content and footer actions", async () => {
|
||||
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
|
||||
const html = await renderComponent(source, {
|
||||
label: "Explore",
|
||||
variant: "icon-grid",
|
||||
panelWidth: "2xl",
|
||||
columnCount: 3,
|
||||
rail: [{ label: "Enterprise", description: "For larger teams", icon: "icon-enterprise" }],
|
||||
columns: [
|
||||
{
|
||||
heading: "Products",
|
||||
description: "Choose a workspace",
|
||||
image: "/products.png",
|
||||
items: [{ label: "CRM", description: "Customer records", icon: "icon-crm", badge: "New" }],
|
||||
actionLabel: "All products",
|
||||
},
|
||||
],
|
||||
featured: {
|
||||
eyebrow: "What's new",
|
||||
title: "Release notes",
|
||||
image: "/release.png",
|
||||
actionLabel: "See more",
|
||||
},
|
||||
footer: "One connected platform",
|
||||
actionLabel: "View everything",
|
||||
});
|
||||
const dom = mountHtml(html);
|
||||
|
||||
expect(dom.querySelector(".wrn-mega")?.getAttribute("data-variant")).toBe("icon-grid");
|
||||
expect(dom.querySelector(".wrn-mega__rail-link")?.textContent).toContain("Enterprise");
|
||||
expect(dom.querySelector(".wrn-mega__badge")?.textContent).toContain("New");
|
||||
expect(dom.querySelector(".wrn-mega__column-image")).not.toBeNull();
|
||||
expect(dom.querySelector(".wrn-mega__featured")?.textContent).toContain("Release notes");
|
||||
expect(dom.querySelector(".wrn-mega__footer")?.textContent).toContain("View everything");
|
||||
});
|
||||
|
||||
test("sidebar renders single items, labelled groups and nested levels", async () => {
|
||||
const source = readFileSync(uiComponentPath("Sidebar"), "utf8");
|
||||
const html = await renderComponent(source, {
|
||||
@@ -3027,6 +3137,8 @@ test("navbar uses a valid aria-current and takes roving focus on its menu", asyn
|
||||
*/
|
||||
expect(source).not.toContain("? 'page' : ''");
|
||||
expect(source).toContain("data-wrn-roving");
|
||||
expect(source).toContain("pointer-events: none;");
|
||||
expect(source).toContain(".wrn-navbar__menus > *");
|
||||
|
||||
const html = await renderComponent(source, {
|
||||
label: "Main",
|
||||
@@ -3066,6 +3178,38 @@ test("breadcrumb marks the trail end without emitting an empty aria-current", as
|
||||
expect(dom.querySelectorAll(".wrn-breadcrumb__item").length).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
|
||||
test("breadcrumb renders a standalone active page with its icon", async () => {
|
||||
const source = readFileSync(uiComponentPath("Breadcrumb"), "utf8");
|
||||
const html = await renderComponent(source, {
|
||||
showHome: true,
|
||||
items: [{ label: "Products", href: "/products", icon: "icon-[lucide--blocks]" }],
|
||||
active: "CRM",
|
||||
activeIcon: "icon-[lucide--contact-round]",
|
||||
});
|
||||
expect(html).toContain("CRM");
|
||||
expect(html).toContain("icon-[lucide--contact-round]");
|
||||
expect(html).toMatch(/aria-current="page"/);
|
||||
const dom = mountHtml(html);
|
||||
expect(dom.querySelectorAll(".wrn-breadcrumb__item").length).toBe(3);
|
||||
});
|
||||
|
||||
test("breadcrumb default separators remain visible without generated icon CSS", async () => {
|
||||
const source = readFileSync(uiComponentPath("Breadcrumb"), "utf8");
|
||||
const html = await renderComponent(source, {
|
||||
showHome: true,
|
||||
items: [{ label: "Legal" }, { label: "Terms", current: true }],
|
||||
});
|
||||
expect(html).toContain("›");
|
||||
expect(html).not.toContain("icon-[lucide--chevron-right]");
|
||||
});
|
||||
|
||||
test("stats bar desktop gutters align its surface with container content", () => {
|
||||
const source = readFileSync(uiComponentPath("StatsBar"), "utf8");
|
||||
expect(source).toContain("--stats-max-width: 80rem");
|
||||
expect(source).toContain("--stats-gutter-width: 4rem");
|
||||
expect(source).toContain("calc(var(--stats-max-width) - var(--stats-gutter-width))");
|
||||
});
|
||||
|
||||
test("mega menu bridges the gap between its trigger and panel", async () => {
|
||||
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
|
||||
/*
|
||||
@@ -3078,6 +3222,39 @@ test("mega menu bridges the gap between its trigger and panel", async () => {
|
||||
expect(source).toMatch(/\.wrn-mega__panel::before \{[^}]*bottom: 100%/s);
|
||||
});
|
||||
|
||||
test("mega menu keeps hover activation and click activation from cancelling each other", () => {
|
||||
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
|
||||
expect(source).toContain("openOnHover: boolean = true");
|
||||
expect(source).toContain("@click='activatePanel(event)'");
|
||||
expect(source).toContain("@mouseenter='hoverPanel(event)'");
|
||||
expect(source).toContain("showPanel(sourceEvent)");
|
||||
expect(source).not.toContain("function scheduleOpen");
|
||||
expect(source).toContain('.wrn-mega__panel[data-show="false"] { display: none; }');
|
||||
expect(source).toContain('style=\'{visible ? "" : "display:none"}\'');
|
||||
expect(source).not.toContain("@focus='showPanel(event)'");
|
||||
expect(source).toContain("max-height: calc(100dvh - 5.5rem)");
|
||||
});
|
||||
|
||||
test("mega menu gives the pointer time to cross from its trigger into a floating panel", () => {
|
||||
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
|
||||
expect(source).toContain("state closeTimer = null");
|
||||
expect(source).toContain("@mouseleave='scheduleHide()'");
|
||||
expect(source).toContain("@mouseenter='cancelScheduledHide()'");
|
||||
expect(source).toContain("}, 220)");
|
||||
expect(source).not.toContain("@mouseleave='hidePanel(\"pointer-leave\")'");
|
||||
});
|
||||
|
||||
test("navbar pins full-width mega panels to desktop viewport gutters", () => {
|
||||
const source = readFileSync(uiComponentPath("Navbar"), "utf8");
|
||||
expect(source).toMatch(
|
||||
/\.wrn-navbar__mega\[data-full-width="true"\] \.wrn-mega__panel \{[^}]*position: fixed/s,
|
||||
);
|
||||
expect(source).toMatch(/right: 1rem;\s+left: 1rem;/s);
|
||||
expect(source).toMatch(
|
||||
/data-collapse-at="1100px"[^}]+\.wrn-navbar__mega\[data-full-width="true"\][^}]+position: static/s,
|
||||
);
|
||||
});
|
||||
|
||||
test("stepper shows only the active step content and offers back, next and skip", async () => {
|
||||
const source = readFileSync(uiComponentPath("Stepper"), "utf8");
|
||||
const html = await renderComponent(source, {
|
||||
@@ -3213,6 +3390,13 @@ test("custom scrollbar styles the scrollbar and drops its fake output", async ()
|
||||
expect(style).toContain("18rem");
|
||||
});
|
||||
|
||||
test("page header collapses compiled empty slot markers", () => {
|
||||
const source = readFileSync(uiComponentPath("PageHeader"), "utf8");
|
||||
expect(source).toContain(".wrn-page-header__meta:not(:has(> :not(template)))");
|
||||
expect(source).toContain(".wrn-page-header__body:not(:has(> :not(template)))");
|
||||
expect(source).toContain(".wrn-page-header__actions--slot-only:not(:has(> :not(template)))");
|
||||
});
|
||||
|
||||
test("layout components ship their own styles instead of Tailwind utilities", () => {
|
||||
/*
|
||||
* These were built from utility classes and class: conditionals. That works
|
||||
|
||||
Reference in New Issue
Block a user