@santi020k/lumen-mcp 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/lumen-data.json +2111 -164
- package/package.json +1 -1
package/data/lumen-data.json
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"Attachment": "8b6f84195c49932d047bee9a6ed76fc5bd708d99eea2609ca7a6cd702dbcf9d4",
|
|
10
10
|
"Autocomplete": "b89d75014a5bad6fe8218ec0ea56af5f50bb1c4b1d7741342539e1835e9b97ea",
|
|
11
11
|
"Avatar": "311767692b20b03119196ef27210d585c7424ac4f92ed5e71cdc59cfae979c58",
|
|
12
|
+
"Backdrop": "07df835d13a0b94064c4a354cd624023c047d05b0b450d5c2e2fed4a7ceac279",
|
|
12
13
|
"BackToTop": "48da5a02c213cdd20dfc21301937982108a100c9364b3710893ab3b65aba3c1f",
|
|
13
14
|
"Badge": "853992e0fe8baca97426399ccfbc492c3384001928b705363416b937bf03d9fa",
|
|
14
15
|
"BarChart": "077260e2ffe472e8e6efd995dbbd9bb60a19b7aec1f1391ef2df30f3852a86de",
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
"Dialog": "fc28ce350e649ebac705ed2e4813082b5782386a4f8fce76556f21414a164839",
|
|
45
46
|
"Direction": "47d34c6c9fa03fb4f4e80e5e9be17c9fe283ca1c059c73b482ab6195dfc68752",
|
|
46
47
|
"Drawer": "d1710054d3df8035d344006497d1783d6185023ca1f360afc857dcade44bd494",
|
|
47
|
-
"DropdownMenu": "
|
|
48
|
+
"DropdownMenu": "d667939d52d8351069627fa421a2572d0cacc7d86edead58041a3ef5e84a5cbd",
|
|
48
49
|
"Empty": "0c1e046f7c001e713fdcaecbf3d9f5c8218fd7e9603e626365bde0fbaeea0fc6",
|
|
49
50
|
"ErrorSummary": "63137ef56d5026581f2ecdfeca56028c336664ab080a1a1fb58359d583a36efe",
|
|
50
51
|
"Eyebrow": "236fb44e3a969d3b503aa0090b46f2441246165d8c58eb091d8f30ce67740cc7",
|
|
@@ -53,9 +54,11 @@
|
|
|
53
54
|
"FloatingBadge": "04f46638085a6ab1f6aead011a9a71c1dc53a0e771437b0e69a06dd4b6c17403",
|
|
54
55
|
"Form": "00180eb0c646100cff0a1ba1ca1ec9f4fc80cb064d02e5bb285888a158de37c1",
|
|
55
56
|
"FormattedDate": "53db4de0b1c4503e458638824bdf000c3254463c76d3d701791062e30255e2c8",
|
|
57
|
+
"Graphic": "e0aee1756b667569a51df051bbe62f52db2ee1317a82e2ebc00c8375f8944075",
|
|
56
58
|
"HoverCard": "fc6d0d3f45211f1f1bc7628a601a95ddc76839d980bd1e01478a6b6318f103b4",
|
|
57
59
|
"Icon": "f55d57961c7d46f2d865777ab59150893ac1bf16d8a1f873b45eede1376cf261",
|
|
58
60
|
"Image": "0da04cc0862b901c4091c42b471026352017618692e6b528d597a06e07d41a38",
|
|
61
|
+
"Illustration": "81f92665df919bd9097233f65df61260a68ae2926f041d12c3f3e2879162d447",
|
|
59
62
|
"Input": "16b77d4c44257612108d2790296f9f43813aaa3a7a903b3bf2d8f62a46074705",
|
|
60
63
|
"InputGroup": "1e77e49544647c9aa14c1f8fa3b6b9be56fed7064c296e46ef53190b88bf8f80",
|
|
61
64
|
"InputOTP": "b0b1b7a6e4d61d7dd24360fd8393b3d9129408e97227df6be9d6d2f807f2775b",
|
|
@@ -115,7 +118,7 @@
|
|
|
115
118
|
"TreeGrid": "086263fadebfd36f1fa1fc84f0e7b1ab6272f84f5fd187b4c241687e5f75a71b",
|
|
116
119
|
"Typography": "730c2096e1a1c3dd99f8ef2583aeddb4da41d0eb7019ddb82868d890da366c9c",
|
|
117
120
|
"VirtualList": "5f818f334a0ea75082e60e5dec33356f379c8287f7f593dd221705a95b2e4a14",
|
|
118
|
-
"LanguageToggle": "
|
|
121
|
+
"LanguageToggle": "275da6c082eb6a28b45ddb14967b11bb705a1838466a953512d1695ea17d53b8",
|
|
119
122
|
"Particles": "760681a833d03179255c264266bf722480b041e5983a907c49ff41eb51efc723",
|
|
120
123
|
"AnimatedNumber": "b98a6af875149ff889a35d0669ad8a9049b032bb6fd39a3e7de0ccc8b12324bd",
|
|
121
124
|
"RevealGroup": "7a8325fa6618717e4a90df7f5e0a99699774a56876d8d047e4b0b7272091e0bf",
|
|
@@ -156,44 +159,55 @@
|
|
|
156
159
|
"VisuallyHidden": "17808de2f795be62dbeb1f8c649fedd7dda5acbd8af3bbc1d1bbec20e788e3b3"
|
|
157
160
|
},
|
|
158
161
|
"nativeComponents": {
|
|
159
|
-
"Theme": "
|
|
160
|
-
"Text": "
|
|
161
|
-
"Surface": "
|
|
162
|
-
"Icon": "
|
|
163
|
-
"Icon button": "
|
|
164
|
-
"Button": "
|
|
165
|
-
"Text field": "
|
|
166
|
-
"Badge": "
|
|
167
|
-
"Divider": "
|
|
168
|
-
"Spinner": "
|
|
169
|
-
"Card": "
|
|
170
|
-
"Alert": "
|
|
171
|
-
"Progress": "
|
|
172
|
-
"Skeleton": "
|
|
173
|
-
"Disclosure": "
|
|
174
|
-
"Avatar": "
|
|
175
|
-
"Textarea": "
|
|
176
|
-
"Field group": "
|
|
177
|
-
"Chip": "
|
|
178
|
-
"Button group": "
|
|
179
|
-
"Toast": "
|
|
180
|
-
"Toggle": "
|
|
181
|
-
"Settings row": "
|
|
182
|
-
"Picker": "
|
|
183
|
-
"Slider": "
|
|
184
|
-
"Search field": "
|
|
185
|
-
"Checkbox": "
|
|
186
|
-
"Radio group": "
|
|
187
|
-
"Segmented control": "
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
"
|
|
191
|
-
"
|
|
192
|
-
"
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
"
|
|
196
|
-
"
|
|
162
|
+
"Theme": "527f0afa78e08580631be6b45fb5980ef2b827472dce29190510eea7f7b73e35",
|
|
163
|
+
"Text": "da836769d7734d25de5c5819b3d8eb7cdb0a7347baa4a757ecfcbde2388391ba",
|
|
164
|
+
"Surface": "b0605587e07f7e0d15b6f5adaf3c2f067b388306e279ca32029e8a44201e1135",
|
|
165
|
+
"Icon": "79ed08ed3eb03bdbe69f895932ef1fdf842320015e74428cae2cf7f70c9e97bf",
|
|
166
|
+
"Icon button": "6088ce23834d92200ea6779fc8a435b2beace575f68247d474069c4df539e44d",
|
|
167
|
+
"Button": "57380934403cb9b81a4642949ca603126a93d283a88dc8830b0a5a4722e397e0",
|
|
168
|
+
"Text field": "5609ef9607cec03a5bf48ab2f706f19cbb0750db2697626390e2ecb7cc665afc",
|
|
169
|
+
"Badge": "96c9707c9ea84f47fde14739fbd9a3789d99aa501d7271ca55f0d5fdb330379e",
|
|
170
|
+
"Divider": "6848eae113b3ccaf01aa5d05b3463ab6c208374165423cc94334eb3448cbc597",
|
|
171
|
+
"Spinner": "cb3f28a5162edb83906a181fa64b0557397cf4b921bd12f41a48165c075a8155",
|
|
172
|
+
"Card": "5481383b9baa0e758b4ecb7cedaeff485998c2cddf6a99835ef5f48be25d4648",
|
|
173
|
+
"Alert": "71c9658e4b5426c0da101adc44c60ffc56a82567a59be1f2776da6ab603c86c3",
|
|
174
|
+
"Progress": "b38f105092118eb2a67feeadc6642cb884245321b3c03e6aa8f96e41c3252bfc",
|
|
175
|
+
"Skeleton": "328820880ed1b1fedd86701053fca980e75ef6b0159628a77de60aa3fb9cfafa",
|
|
176
|
+
"Disclosure": "6e940a230839fc34905dd3e629c961179d40bed0c8ed834333e9e71c20c2c56f",
|
|
177
|
+
"Avatar": "35544a2d015338f7b240234619b2eebe5ca97710d3b39af32931c0cb5f544813",
|
|
178
|
+
"Textarea": "221a265a3ed0eca986f1b6e377f92217ab343359a64081a021d0cf079eaa2c6e",
|
|
179
|
+
"Field group": "1d736820d5eabd202090e35555be772a06c26a1c47745417d5a23faa07c1e3fa",
|
|
180
|
+
"Chip": "fcbce7af9bb38a1bce7f5c337a0cdb8eb8721aa0a0f36c510ff764acdef986f5",
|
|
181
|
+
"Button group": "82c35614035d04e4dc0815ee22be89b89856516ed7fe557ac430c9b2f37aa18f",
|
|
182
|
+
"Toast": "654dcf711be6af245dcb4ea8ba8d100d4c5bcea0e281ab2e3009f0dd765f8caf",
|
|
183
|
+
"Toggle": "b2e7e0723a55a6118cf20f978a4f880d814440107827e509b11e8c3c92b00f4b",
|
|
184
|
+
"Settings row": "8bc392c37d446ba1ed276556ac57910335d8dab7bc013e56fc9383819a48baa5",
|
|
185
|
+
"Picker": "ed96336bb1fec3088a1aafebfcb7f0091352de1d47c666c0c54b67ebb3a472c0",
|
|
186
|
+
"Slider": "95d3f6367e952633325dc1214a240424b0299fa4ce1457eadcbc995489ed0828",
|
|
187
|
+
"Search field": "d65de25840be13dc68e30eb29fc7aaa4ebdc0f0d249a2746e980135ac189fd95",
|
|
188
|
+
"Checkbox": "badc66c91eddd344b30058ec62f49e29ca8e3cb4c2fbe7a329f9d87b1dcd4516",
|
|
189
|
+
"Radio group": "16af92535d9ce1d7b899820fcac65cb068e4e14e4f70e72b0eaedc83f3e0b66d",
|
|
190
|
+
"Segmented control": "001c132f08dbaaebd00aee9bc0c68cdb5d198506314b07a6a1e473740abfc868",
|
|
191
|
+
"Navigation bar": "6a196a6f9924057040e7cbf99fec903f864d177aca462199cdfe8d076a782539",
|
|
192
|
+
"Empty state": "40301270a9d34234d0a3500e7d8d29f70f5886d4cb07647c3439c695619ec97a",
|
|
193
|
+
"List row": "ea220c485ae90e6d0670e8f526f7925be568377c467bffb87c68d9ec79392441",
|
|
194
|
+
"Banner": "95f00d14a494253cb4aed2694a883c3b4bffbf2316e7536594a02117b8991808",
|
|
195
|
+
"Stat": "3b357dde8e5c31b1a91df44ebc12c01fdbf2d0e74dfb721a8b47a88722542145",
|
|
196
|
+
"Gauge": "d8bb93ae076c2fe70e2dbc149cf6d99bdb2199c176dff0f6ccd0016eb1b62b49",
|
|
197
|
+
"Section header": "4e1dd5fae63e25270c62b74fc347a18cff71050bb8498b003725cdb532edb734",
|
|
198
|
+
"Status bar": "d67f1a895b5ae0541b4a4e5320858e27a3ec1dda50ef655e122da1373ff1bd90",
|
|
199
|
+
"Refresh control": "2626b04e4dab27f7fecd9210ad6d3a150548c5e8053f94fbb7f3ce05342bceb4",
|
|
200
|
+
"Date field": "2397d6907ac230497077d3b388b330fc43680f6e7cadd2faef3480a71d719ec3",
|
|
201
|
+
"Floating action button": "ba8237274309f2fbd7b78d42ea6862dc018397209111d88d4abd9ce9b877da84",
|
|
202
|
+
"Shortcut recorder": "a5a2fa237d2efe604431bc35d46f063328fdc0fd8c0485d53d1692b1f0a76926",
|
|
203
|
+
"Symbol picker": "57b3383a74549aa62dd97dfa627464818e4b65d5b8c130ecb3697059668fbb27",
|
|
204
|
+
"Graphic": "54a853609b5abdc397d2e209685d816338db10b14b2d112369ecb6e532c2df3a",
|
|
205
|
+
"Backdrop": "89d9f325e636ad8e30f0afc333a1a3c31b761075ca6b83c454abf3f95c427dcc",
|
|
206
|
+
"Illustration": "65c87de0c17a8161645dfa7787c43b8b144f433fbb4a72fe1691afe319750eda",
|
|
207
|
+
"Alert dialog": "8ee22136b2c66aec77df2bd6234f470cd0a8e97f52abeedd10c12ae9d9ec7bd7",
|
|
208
|
+
"Sheet": "1873aeda95cd73a25ae89cb846c0bea94f287fa2559bc6bbe33809a25585f9ca",
|
|
209
|
+
"Menu": "0542e0c7006431c82248b3d2456418504236c23e6cd2092ae65bdc4be94eaab7",
|
|
210
|
+
"Share button": "7dcd8523d024d98679e3a78d05b237e80d26ce0222669fe1a6df588a6006effb"
|
|
197
211
|
},
|
|
198
212
|
"recipes": {
|
|
199
213
|
"all-components": "517dfb03b8ca5483f5412a00ca2a151763d8d4429a44e398d4d74733793f0fd7",
|
|
@@ -1761,6 +1775,204 @@
|
|
|
1761
1775
|
],
|
|
1762
1776
|
"runtimeEvents": []
|
|
1763
1777
|
},
|
|
1778
|
+
{
|
|
1779
|
+
"apiReference": [
|
|
1780
|
+
{
|
|
1781
|
+
"attribute": "variant",
|
|
1782
|
+
"defaultValue": "\"aurora\"",
|
|
1783
|
+
"description": "Selects the ambient background pattern.",
|
|
1784
|
+
"values": "\"aurora\" | \"dots\" | \"grid\" | \"rays\""
|
|
1785
|
+
},
|
|
1786
|
+
{
|
|
1787
|
+
"attribute": "tone",
|
|
1788
|
+
"defaultValue": "\"brand\"",
|
|
1789
|
+
"description": "Uses a semantic Lumen color for the pattern.",
|
|
1790
|
+
"values": "\"brand\" | \"accent\" | \"neutral\""
|
|
1791
|
+
},
|
|
1792
|
+
{
|
|
1793
|
+
"attribute": "intensity",
|
|
1794
|
+
"defaultValue": "\"medium\"",
|
|
1795
|
+
"description": "Controls decoration opacity without fading child content.",
|
|
1796
|
+
"values": "\"subtle\" | \"medium\" | \"strong\""
|
|
1797
|
+
},
|
|
1798
|
+
{
|
|
1799
|
+
"attribute": "children",
|
|
1800
|
+
"defaultValue": "required",
|
|
1801
|
+
"description": "Keeps meaningful content above the decorative layer.",
|
|
1802
|
+
"values": "rendered content"
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
"attribute": "class, className",
|
|
1806
|
+
"defaultValue": "\"\"",
|
|
1807
|
+
"description": "Merges custom classes with the generated ui-* root classes.",
|
|
1808
|
+
"values": "string"
|
|
1809
|
+
},
|
|
1810
|
+
{
|
|
1811
|
+
"attribute": "...native attributes",
|
|
1812
|
+
"defaultValue": "-",
|
|
1813
|
+
"description": "Forwards standard attributes to the root element unless the component consumes them.",
|
|
1814
|
+
"values": "HTML attributes"
|
|
1815
|
+
}
|
|
1816
|
+
],
|
|
1817
|
+
"astroSource": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport { resolveLumenAstroProps } from '@santi020k/lumen-core'\n\ninterface Props extends HTMLAttributes<'div'> {\n class?: string | null\n className?: string\n intensity?: 'medium' | 'strong' | 'subtle'\n tone?: 'accent' | 'brand' | 'neutral'\n variant?: 'aurora' | 'dots' | 'grid' | 'rays'\n}\n\nconst {\n class: classProp = '',\n className = '',\n intensity = 'medium',\n tone = 'brand',\n variant = 'aurora',\n ...rest\n} = Astro.props\n\nconst { classList, passthrough } = resolveLumenAstroProps(rest, [\n 'ui-backdrop',\n `ui-backdrop--${variant}`,\n `ui-backdrop--${tone}`,\n `ui-backdrop--${intensity}`\n], classProp, className)\n---\n\n<div class:list={classList} {...passthrough}>\n <div class=\"ui-backdrop__content\">\n <slot />\n </div>\n</div>\n",
|
|
1818
|
+
"category": "Brand",
|
|
1819
|
+
"collections": [],
|
|
1820
|
+
"dependencies": [
|
|
1821
|
+
"styles"
|
|
1822
|
+
],
|
|
1823
|
+
"description": "Adds a portable ambient pattern behind meaningful content.",
|
|
1824
|
+
"behavior": {
|
|
1825
|
+
"astro": "none",
|
|
1826
|
+
"elements": "registered-element",
|
|
1827
|
+
"react": "component"
|
|
1828
|
+
},
|
|
1829
|
+
"files": [
|
|
1830
|
+
"packages/astro/components/Backdrop.astro",
|
|
1831
|
+
"packages/astro/styles/lumen.css",
|
|
1832
|
+
"packages/react/src/components.tsx"
|
|
1833
|
+
],
|
|
1834
|
+
"frameworkDetails": {
|
|
1835
|
+
"astro": {
|
|
1836
|
+
"available": true,
|
|
1837
|
+
"example": "<Backdrop intensity=\"medium\" tone=\"brand\" variant=\"aurora\"><Card variant=\"glass\">Project overview</Card></Backdrop>",
|
|
1838
|
+
"importStatement": "import { Backdrop, Card } from '@santi020k/lumen-astro'",
|
|
1839
|
+
"language": "astro",
|
|
1840
|
+
"packageName": "@santi020k/lumen-astro",
|
|
1841
|
+
"props": [
|
|
1842
|
+
{
|
|
1843
|
+
"name": "class",
|
|
1844
|
+
"optional": true,
|
|
1845
|
+
"type": "string | null"
|
|
1846
|
+
},
|
|
1847
|
+
{
|
|
1848
|
+
"name": "className",
|
|
1849
|
+
"optional": true,
|
|
1850
|
+
"type": "string"
|
|
1851
|
+
},
|
|
1852
|
+
{
|
|
1853
|
+
"name": "intensity",
|
|
1854
|
+
"optional": true,
|
|
1855
|
+
"type": "'medium' | 'strong' | 'subtle'"
|
|
1856
|
+
},
|
|
1857
|
+
{
|
|
1858
|
+
"name": "tone",
|
|
1859
|
+
"optional": true,
|
|
1860
|
+
"type": "'accent' | 'brand' | 'neutral'"
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
"name": "variant",
|
|
1864
|
+
"optional": true,
|
|
1865
|
+
"type": "'aurora' | 'dots' | 'grid' | 'rays'"
|
|
1866
|
+
}
|
|
1867
|
+
],
|
|
1868
|
+
"propsExtends": "HTMLAttributes<'div'>",
|
|
1869
|
+
"source": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport { resolveLumenAstroProps } from '@santi020k/lumen-core'\n\ninterface Props extends HTMLAttributes<'div'> {\n class?: string | null\n className?: string\n intensity?: 'medium' | 'strong' | 'subtle'\n tone?: 'accent' | 'brand' | 'neutral'\n variant?: 'aurora' | 'dots' | 'grid' | 'rays'\n}\n\nconst {\n class: classProp = '',\n className = '',\n intensity = 'medium',\n tone = 'brand',\n variant = 'aurora',\n ...rest\n} = Astro.props\n\nconst { classList, passthrough } = resolveLumenAstroProps(rest, [\n 'ui-backdrop',\n `ui-backdrop--${variant}`,\n `ui-backdrop--${tone}`,\n `ui-backdrop--${intensity}`\n], classProp, className)\n---\n\n<div class:list={classList} {...passthrough}>\n <div class=\"ui-backdrop__content\">\n <slot />\n </div>\n</div>\n",
|
|
1870
|
+
"styleImport": "import '@santi020k/lumen-astro/styles.css'"
|
|
1871
|
+
},
|
|
1872
|
+
"elements": {
|
|
1873
|
+
"attributes": [
|
|
1874
|
+
"intensity",
|
|
1875
|
+
"tone",
|
|
1876
|
+
"variant",
|
|
1877
|
+
"children",
|
|
1878
|
+
"class, className",
|
|
1879
|
+
"...native attributes"
|
|
1880
|
+
],
|
|
1881
|
+
"available": true,
|
|
1882
|
+
"behavior": {
|
|
1883
|
+
"mode": "registration",
|
|
1884
|
+
"setup": "Call defineLumenElements() once before rendering Lumen custom elements."
|
|
1885
|
+
},
|
|
1886
|
+
"example": "<lumen-backdrop intensity=\"medium\" tone=\"brand\" variant=\"aurora\"><lumen-card variant=\"glass\">Project overview</lumen-card></lumen-backdrop>",
|
|
1887
|
+
"importStatement": "import { defineLumenElements } from '@santi020k/lumen-elements/define'",
|
|
1888
|
+
"language": "html",
|
|
1889
|
+
"packageName": "@santi020k/lumen-elements",
|
|
1890
|
+
"props": [],
|
|
1891
|
+
"propsExtends": "HTMLElement attributes",
|
|
1892
|
+
"registration": "defineLumenElements()",
|
|
1893
|
+
"source": "Backdrop: {\n attributeClasses: {\n intensity: {\n medium: 'ui-backdrop--medium',\n strong: 'ui-backdrop--strong',\n subtle: 'ui-backdrop--subtle'\n },\n tone: {\n accent: 'ui-backdrop--accent',\n brand: 'ui-backdrop--brand',\n neutral: 'ui-backdrop--neutral'\n },\n variant: {\n aurora: 'ui-backdrop--aurora',\n dots: 'ui-backdrop--dots',\n grid: 'ui-backdrop--grid',\n rays: 'ui-backdrop--rays'\n }\n },\n baseClassName: 'ui-backdrop',\n defaults: { intensity: 'medium', tone: 'brand', variant: 'aurora' },\n tagName: 'lumen-backdrop'\n }",
|
|
1894
|
+
"styleImport": "import '@santi020k/lumen-elements/styles.css'",
|
|
1895
|
+
"tagName": "lumen-backdrop"
|
|
1896
|
+
},
|
|
1897
|
+
"react": {
|
|
1898
|
+
"available": true,
|
|
1899
|
+
"example": "<Backdrop intensity=\"medium\" tone=\"brand\" variant=\"aurora\"><Card variant=\"glass\">Project overview</Card></Backdrop>",
|
|
1900
|
+
"importStatement": "import { Backdrop, Card } from '@santi020k/lumen-react'",
|
|
1901
|
+
"language": "tsx",
|
|
1902
|
+
"packageName": "@santi020k/lumen-react",
|
|
1903
|
+
"props": [
|
|
1904
|
+
{
|
|
1905
|
+
"name": "intensity",
|
|
1906
|
+
"optional": true,
|
|
1907
|
+
"type": "'medium' | 'strong' | 'subtle'"
|
|
1908
|
+
},
|
|
1909
|
+
{
|
|
1910
|
+
"name": "tone",
|
|
1911
|
+
"optional": true,
|
|
1912
|
+
"type": "'accent' | 'brand' | 'neutral'"
|
|
1913
|
+
},
|
|
1914
|
+
{
|
|
1915
|
+
"name": "variant",
|
|
1916
|
+
"optional": true,
|
|
1917
|
+
"type": "'aurora' | 'dots' | 'grid' | 'rays'"
|
|
1918
|
+
}
|
|
1919
|
+
],
|
|
1920
|
+
"propsExtends": "ComponentPropsWithoutRef<'div'>",
|
|
1921
|
+
"source": "export interface BackdropProps extends ComponentPropsWithoutRef<'div'> {\n intensity?: 'medium' | 'strong' | 'subtle'\n tone?: 'accent' | 'brand' | 'neutral'\n variant?: 'aurora' | 'dots' | 'grid' | 'rays'\n}\n\nexport const Backdrop = ({\n children,\n className,\n intensity = 'medium',\n tone = 'brand',\n variant = 'aurora',\n ...props\n}: BackdropProps) => (\n <div\n className={composeClassName(\n 'ui-backdrop',\n `ui-backdrop--${variant}`,\n `ui-backdrop--${tone}`,\n `ui-backdrop--${intensity}`,\n className\n )}\n {...props}\n >\n <div className=\"ui-backdrop__content\">{children}</div>\n </div>\n)",
|
|
1922
|
+
"styleImport": "import '@santi020k/lumen-react/styles.css'"
|
|
1923
|
+
}
|
|
1924
|
+
},
|
|
1925
|
+
"frameworks": {
|
|
1926
|
+
"astro": true,
|
|
1927
|
+
"elements": true,
|
|
1928
|
+
"react": true
|
|
1929
|
+
},
|
|
1930
|
+
"guidance": null,
|
|
1931
|
+
"kebab": "backdrop",
|
|
1932
|
+
"keyboardInteractions": [],
|
|
1933
|
+
"keywords": [
|
|
1934
|
+
"adds",
|
|
1935
|
+
"ambient",
|
|
1936
|
+
"backdrop",
|
|
1937
|
+
"behind",
|
|
1938
|
+
"brand",
|
|
1939
|
+
"content",
|
|
1940
|
+
"meaningful",
|
|
1941
|
+
"pattern",
|
|
1942
|
+
"portable"
|
|
1943
|
+
],
|
|
1944
|
+
"name": "Backdrop",
|
|
1945
|
+
"props": [
|
|
1946
|
+
{
|
|
1947
|
+
"name": "class",
|
|
1948
|
+
"optional": true,
|
|
1949
|
+
"type": "string | null"
|
|
1950
|
+
},
|
|
1951
|
+
{
|
|
1952
|
+
"name": "className",
|
|
1953
|
+
"optional": true,
|
|
1954
|
+
"type": "string"
|
|
1955
|
+
},
|
|
1956
|
+
{
|
|
1957
|
+
"name": "intensity",
|
|
1958
|
+
"optional": true,
|
|
1959
|
+
"type": "'medium' | 'strong' | 'subtle'"
|
|
1960
|
+
},
|
|
1961
|
+
{
|
|
1962
|
+
"name": "tone",
|
|
1963
|
+
"optional": true,
|
|
1964
|
+
"type": "'accent' | 'brand' | 'neutral'"
|
|
1965
|
+
},
|
|
1966
|
+
{
|
|
1967
|
+
"name": "variant",
|
|
1968
|
+
"optional": true,
|
|
1969
|
+
"type": "'aurora' | 'dots' | 'grid' | 'rays'"
|
|
1970
|
+
}
|
|
1971
|
+
],
|
|
1972
|
+
"propsExtends": "HTMLAttributes<'div'>",
|
|
1973
|
+
"recipes": [],
|
|
1974
|
+
"runtimeEvents": []
|
|
1975
|
+
},
|
|
1764
1976
|
{
|
|
1765
1977
|
"apiReference": [
|
|
1766
1978
|
{
|
|
@@ -9534,8 +9746,8 @@
|
|
|
9534
9746
|
],
|
|
9535
9747
|
"setup": "Use useDropdownMenu from @santi020k/lumen-react; do not mount Astro UIPrimitives in React."
|
|
9536
9748
|
},
|
|
9537
|
-
"example": "import {
|
|
9538
|
-
"importStatement": "import {
|
|
9749
|
+
"example": "import {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger\n} from '@santi020k/lumen-react'\n\nexport function ActionsMenu() {\n return (\n <DropdownMenu>\n <DropdownMenuTrigger>Actions</DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuItem>Edit</DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem status=\"Admin\">Delete</DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}",
|
|
9750
|
+
"importStatement": "import {",
|
|
9539
9751
|
"language": "tsx",
|
|
9540
9752
|
"packageName": "@santi020k/lumen-react",
|
|
9541
9753
|
"props": [],
|
|
@@ -11085,6 +11297,222 @@
|
|
|
11085
11297
|
"recipes": [],
|
|
11086
11298
|
"runtimeEvents": []
|
|
11087
11299
|
},
|
|
11300
|
+
{
|
|
11301
|
+
"apiReference": [
|
|
11302
|
+
{
|
|
11303
|
+
"attribute": "variant",
|
|
11304
|
+
"defaultValue": "\"orbit\"",
|
|
11305
|
+
"description": "Selects a portable decorative pattern around application-provided artwork.",
|
|
11306
|
+
"values": "\"glow\" | \"grid\" | \"orbit\""
|
|
11307
|
+
},
|
|
11308
|
+
{
|
|
11309
|
+
"attribute": "tone",
|
|
11310
|
+
"defaultValue": "\"brand\"",
|
|
11311
|
+
"description": "Uses the matching semantic Lumen color without introducing a product palette.",
|
|
11312
|
+
"values": "\"brand\" | \"accent\" | \"neutral\""
|
|
11313
|
+
},
|
|
11314
|
+
{
|
|
11315
|
+
"attribute": "size",
|
|
11316
|
+
"defaultValue": "\"md\"",
|
|
11317
|
+
"description": "Uses the shared 160, 240, or 320 unit composition size.",
|
|
11318
|
+
"values": "\"sm\" | \"md\" | \"lg\""
|
|
11319
|
+
},
|
|
11320
|
+
{
|
|
11321
|
+
"attribute": "label",
|
|
11322
|
+
"defaultValue": "-",
|
|
11323
|
+
"description": "Exposes the complete composition as one named image; without it the graphic is decorative.",
|
|
11324
|
+
"values": "string"
|
|
11325
|
+
},
|
|
11326
|
+
{
|
|
11327
|
+
"attribute": "class, className",
|
|
11328
|
+
"defaultValue": "\"\"",
|
|
11329
|
+
"description": "Merges custom classes with the generated ui-* root classes.",
|
|
11330
|
+
"values": "string"
|
|
11331
|
+
},
|
|
11332
|
+
{
|
|
11333
|
+
"attribute": "...native attributes",
|
|
11334
|
+
"defaultValue": "-",
|
|
11335
|
+
"description": "Forwards standard attributes to the root element unless the component consumes them.",
|
|
11336
|
+
"values": "HTML attributes"
|
|
11337
|
+
}
|
|
11338
|
+
],
|
|
11339
|
+
"astroSource": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport { resolveLumenAstroProps } from '@santi020k/lumen-core'\n\ninterface Props extends HTMLAttributes<'div'> {\n class?: string | null\n className?: string\n label?: string\n size?: 'lg' | 'md' | 'sm'\n tone?: 'accent' | 'brand' | 'neutral'\n variant?: 'glow' | 'grid' | 'orbit'\n}\n\nconst {\n class: classProp = '',\n className = '',\n label,\n size = 'md',\n tone = 'brand',\n variant = 'orbit',\n ...rest\n} = Astro.props\n\nconst { classList, passthrough } = resolveLumenAstroProps(rest, [\n 'ui-graphic',\n `ui-graphic--${variant}`,\n `ui-graphic--${tone}`,\n `ui-graphic--${size}`\n], classProp, className)\n---\n\n<div\n aria-hidden={label ? undefined : 'true'}\n aria-label={label}\n class:list={classList}\n role={label ? 'img' : undefined}\n {...passthrough}\n>\n <div class=\"ui-graphic__content\">\n <slot />\n </div>\n</div>\n",
|
|
11340
|
+
"category": "Brand",
|
|
11341
|
+
"collections": [],
|
|
11342
|
+
"dependencies": [
|
|
11343
|
+
"styles"
|
|
11344
|
+
],
|
|
11345
|
+
"description": "Frames product-owned artwork with portable glow, grid, or orbit treatments.",
|
|
11346
|
+
"behavior": {
|
|
11347
|
+
"astro": "none",
|
|
11348
|
+
"elements": "registered-element",
|
|
11349
|
+
"react": "component"
|
|
11350
|
+
},
|
|
11351
|
+
"files": [
|
|
11352
|
+
"packages/astro/components/Graphic.astro",
|
|
11353
|
+
"packages/astro/styles/lumen.css",
|
|
11354
|
+
"packages/react/src/components.tsx"
|
|
11355
|
+
],
|
|
11356
|
+
"frameworkDetails": {
|
|
11357
|
+
"astro": {
|
|
11358
|
+
"available": true,
|
|
11359
|
+
"example": "<Graphic size=\"lg\" tone=\"accent\" variant=\"orbit\"><Icon name=\"sparkles\" decorative size=\"xl\" /></Graphic>",
|
|
11360
|
+
"importStatement": "import { Graphic, Icon } from '@santi020k/lumen-astro'",
|
|
11361
|
+
"language": "astro",
|
|
11362
|
+
"packageName": "@santi020k/lumen-astro",
|
|
11363
|
+
"props": [
|
|
11364
|
+
{
|
|
11365
|
+
"name": "class",
|
|
11366
|
+
"optional": true,
|
|
11367
|
+
"type": "string | null"
|
|
11368
|
+
},
|
|
11369
|
+
{
|
|
11370
|
+
"name": "className",
|
|
11371
|
+
"optional": true,
|
|
11372
|
+
"type": "string"
|
|
11373
|
+
},
|
|
11374
|
+
{
|
|
11375
|
+
"name": "label",
|
|
11376
|
+
"optional": true,
|
|
11377
|
+
"type": "string"
|
|
11378
|
+
},
|
|
11379
|
+
{
|
|
11380
|
+
"name": "size",
|
|
11381
|
+
"optional": true,
|
|
11382
|
+
"type": "'lg' | 'md' | 'sm'"
|
|
11383
|
+
},
|
|
11384
|
+
{
|
|
11385
|
+
"name": "tone",
|
|
11386
|
+
"optional": true,
|
|
11387
|
+
"type": "'accent' | 'brand' | 'neutral'"
|
|
11388
|
+
},
|
|
11389
|
+
{
|
|
11390
|
+
"name": "variant",
|
|
11391
|
+
"optional": true,
|
|
11392
|
+
"type": "'glow' | 'grid' | 'orbit'"
|
|
11393
|
+
}
|
|
11394
|
+
],
|
|
11395
|
+
"propsExtends": "HTMLAttributes<'div'>",
|
|
11396
|
+
"source": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport { resolveLumenAstroProps } from '@santi020k/lumen-core'\n\ninterface Props extends HTMLAttributes<'div'> {\n class?: string | null\n className?: string\n label?: string\n size?: 'lg' | 'md' | 'sm'\n tone?: 'accent' | 'brand' | 'neutral'\n variant?: 'glow' | 'grid' | 'orbit'\n}\n\nconst {\n class: classProp = '',\n className = '',\n label,\n size = 'md',\n tone = 'brand',\n variant = 'orbit',\n ...rest\n} = Astro.props\n\nconst { classList, passthrough } = resolveLumenAstroProps(rest, [\n 'ui-graphic',\n `ui-graphic--${variant}`,\n `ui-graphic--${tone}`,\n `ui-graphic--${size}`\n], classProp, className)\n---\n\n<div\n aria-hidden={label ? undefined : 'true'}\n aria-label={label}\n class:list={classList}\n role={label ? 'img' : undefined}\n {...passthrough}\n>\n <div class=\"ui-graphic__content\">\n <slot />\n </div>\n</div>\n",
|
|
11397
|
+
"styleImport": "import '@santi020k/lumen-astro/styles.css'"
|
|
11398
|
+
},
|
|
11399
|
+
"elements": {
|
|
11400
|
+
"attributes": [
|
|
11401
|
+
"size",
|
|
11402
|
+
"tone",
|
|
11403
|
+
"variant",
|
|
11404
|
+
"label",
|
|
11405
|
+
"class, className",
|
|
11406
|
+
"...native attributes"
|
|
11407
|
+
],
|
|
11408
|
+
"available": true,
|
|
11409
|
+
"behavior": {
|
|
11410
|
+
"mode": "registration",
|
|
11411
|
+
"setup": "Call defineLumenElements() once before rendering Lumen custom elements."
|
|
11412
|
+
},
|
|
11413
|
+
"example": "<lumen-graphic size=\"lg\" tone=\"accent\" variant=\"orbit\"><lumen-icon name=\"sparkles\" decorative size=\"xl\"></lumen-icon></lumen-graphic>",
|
|
11414
|
+
"importStatement": "import { defineLumenElements } from '@santi020k/lumen-elements/define'",
|
|
11415
|
+
"language": "html",
|
|
11416
|
+
"packageName": "@santi020k/lumen-elements",
|
|
11417
|
+
"props": [],
|
|
11418
|
+
"propsExtends": "HTMLElement attributes",
|
|
11419
|
+
"registration": "defineLumenElements()",
|
|
11420
|
+
"source": "Graphic: {\n attributeClasses: {\n size: {\n lg: 'ui-graphic--lg',\n md: 'ui-graphic--md',\n sm: 'ui-graphic--sm'\n },\n tone: {\n accent: 'ui-graphic--accent',\n brand: 'ui-graphic--brand',\n neutral: 'ui-graphic--neutral'\n },\n variant: {\n glow: 'ui-graphic--glow',\n grid: 'ui-graphic--grid',\n orbit: 'ui-graphic--orbit'\n }\n },\n baseClassName: 'ui-graphic',\n defaults: { size: 'md', tone: 'brand', variant: 'orbit' },\n tagName: 'lumen-graphic'\n }",
|
|
11421
|
+
"styleImport": "import '@santi020k/lumen-elements/styles.css'",
|
|
11422
|
+
"tagName": "lumen-graphic"
|
|
11423
|
+
},
|
|
11424
|
+
"react": {
|
|
11425
|
+
"available": true,
|
|
11426
|
+
"example": "<Graphic size=\"lg\" tone=\"accent\" variant=\"orbit\"><Icon name=\"sparkles\" decorative size=\"xl\" /></Graphic>",
|
|
11427
|
+
"importStatement": "import { Graphic, Icon } from '@santi020k/lumen-react'",
|
|
11428
|
+
"language": "tsx",
|
|
11429
|
+
"packageName": "@santi020k/lumen-react",
|
|
11430
|
+
"props": [
|
|
11431
|
+
{
|
|
11432
|
+
"name": "label",
|
|
11433
|
+
"optional": true,
|
|
11434
|
+
"type": "string"
|
|
11435
|
+
},
|
|
11436
|
+
{
|
|
11437
|
+
"name": "size",
|
|
11438
|
+
"optional": true,
|
|
11439
|
+
"type": "'lg' | 'md' | 'sm'"
|
|
11440
|
+
},
|
|
11441
|
+
{
|
|
11442
|
+
"name": "tone",
|
|
11443
|
+
"optional": true,
|
|
11444
|
+
"type": "'accent' | 'brand' | 'neutral'"
|
|
11445
|
+
},
|
|
11446
|
+
{
|
|
11447
|
+
"name": "variant",
|
|
11448
|
+
"optional": true,
|
|
11449
|
+
"type": "'glow' | 'grid' | 'orbit'"
|
|
11450
|
+
}
|
|
11451
|
+
],
|
|
11452
|
+
"propsExtends": "ComponentPropsWithoutRef<'div'>",
|
|
11453
|
+
"source": "export interface GraphicProps extends ComponentPropsWithoutRef<'div'> {\n label?: string\n size?: 'lg' | 'md' | 'sm'\n tone?: 'accent' | 'brand' | 'neutral'\n variant?: 'glow' | 'grid' | 'orbit'\n}\n\nexport const Graphic = ({\n children,\n className,\n label,\n size = 'md',\n tone = 'brand',\n variant = 'orbit',\n ...props\n}: GraphicProps) => (\n <div\n aria-hidden={label ? undefined : true}\n aria-label={label}\n className={composeClassName(\n 'ui-graphic',\n `ui-graphic--${variant}`,\n `ui-graphic--${tone}`,\n `ui-graphic--${size}`,\n className\n )}\n role={label ? 'img' : undefined}\n {...props}\n >\n <div className=\"ui-graphic__content\">{children}</div>\n </div>\n)",
|
|
11454
|
+
"styleImport": "import '@santi020k/lumen-react/styles.css'"
|
|
11455
|
+
}
|
|
11456
|
+
},
|
|
11457
|
+
"frameworks": {
|
|
11458
|
+
"astro": true,
|
|
11459
|
+
"elements": true,
|
|
11460
|
+
"react": true
|
|
11461
|
+
},
|
|
11462
|
+
"guidance": null,
|
|
11463
|
+
"kebab": "graphic",
|
|
11464
|
+
"keyboardInteractions": [],
|
|
11465
|
+
"keywords": [
|
|
11466
|
+
"artwork",
|
|
11467
|
+
"brand",
|
|
11468
|
+
"frames",
|
|
11469
|
+
"glow",
|
|
11470
|
+
"graphic",
|
|
11471
|
+
"grid",
|
|
11472
|
+
"orbit",
|
|
11473
|
+
"owned",
|
|
11474
|
+
"portable",
|
|
11475
|
+
"product",
|
|
11476
|
+
"treatments",
|
|
11477
|
+
"with"
|
|
11478
|
+
],
|
|
11479
|
+
"name": "Graphic",
|
|
11480
|
+
"props": [
|
|
11481
|
+
{
|
|
11482
|
+
"name": "class",
|
|
11483
|
+
"optional": true,
|
|
11484
|
+
"type": "string | null"
|
|
11485
|
+
},
|
|
11486
|
+
{
|
|
11487
|
+
"name": "className",
|
|
11488
|
+
"optional": true,
|
|
11489
|
+
"type": "string"
|
|
11490
|
+
},
|
|
11491
|
+
{
|
|
11492
|
+
"name": "label",
|
|
11493
|
+
"optional": true,
|
|
11494
|
+
"type": "string"
|
|
11495
|
+
},
|
|
11496
|
+
{
|
|
11497
|
+
"name": "size",
|
|
11498
|
+
"optional": true,
|
|
11499
|
+
"type": "'lg' | 'md' | 'sm'"
|
|
11500
|
+
},
|
|
11501
|
+
{
|
|
11502
|
+
"name": "tone",
|
|
11503
|
+
"optional": true,
|
|
11504
|
+
"type": "'accent' | 'brand' | 'neutral'"
|
|
11505
|
+
},
|
|
11506
|
+
{
|
|
11507
|
+
"name": "variant",
|
|
11508
|
+
"optional": true,
|
|
11509
|
+
"type": "'glow' | 'grid' | 'orbit'"
|
|
11510
|
+
}
|
|
11511
|
+
],
|
|
11512
|
+
"propsExtends": "HTMLAttributes<'div'>",
|
|
11513
|
+
"recipes": [],
|
|
11514
|
+
"runtimeEvents": []
|
|
11515
|
+
},
|
|
11088
11516
|
{
|
|
11089
11517
|
"apiReference": [
|
|
11090
11518
|
{
|
|
@@ -11733,6 +12161,222 @@
|
|
|
11733
12161
|
"recipes": [],
|
|
11734
12162
|
"runtimeEvents": []
|
|
11735
12163
|
},
|
|
12164
|
+
{
|
|
12165
|
+
"apiReference": [
|
|
12166
|
+
{
|
|
12167
|
+
"attribute": "variant",
|
|
12168
|
+
"defaultValue": "\"empty\"",
|
|
12169
|
+
"description": "Selects a built-in semantic scene.",
|
|
12170
|
+
"values": "\"empty\" | \"success\" | \"error\" | \"offline\""
|
|
12171
|
+
},
|
|
12172
|
+
{
|
|
12173
|
+
"attribute": "tone",
|
|
12174
|
+
"defaultValue": "\"auto\"",
|
|
12175
|
+
"description": "Uses the variant semantic color automatically or an explicit portable tone.",
|
|
12176
|
+
"values": "\"auto\" | \"brand\" | \"accent\" | \"neutral\""
|
|
12177
|
+
},
|
|
12178
|
+
{
|
|
12179
|
+
"attribute": "size",
|
|
12180
|
+
"defaultValue": "\"md\"",
|
|
12181
|
+
"description": "Uses the shared 96, 128, or 176 unit illustration size.",
|
|
12182
|
+
"values": "\"sm\" | \"md\" | \"lg\""
|
|
12183
|
+
},
|
|
12184
|
+
{
|
|
12185
|
+
"attribute": "label",
|
|
12186
|
+
"defaultValue": "-",
|
|
12187
|
+
"description": "Exposes the illustration as one named image; without it the artwork is decorative.",
|
|
12188
|
+
"values": "string"
|
|
12189
|
+
},
|
|
12190
|
+
{
|
|
12191
|
+
"attribute": "class, className",
|
|
12192
|
+
"defaultValue": "\"\"",
|
|
12193
|
+
"description": "Merges custom classes with the generated ui-* root classes.",
|
|
12194
|
+
"values": "string"
|
|
12195
|
+
},
|
|
12196
|
+
{
|
|
12197
|
+
"attribute": "...native attributes",
|
|
12198
|
+
"defaultValue": "-",
|
|
12199
|
+
"description": "Forwards standard attributes to the root element unless the component consumes them.",
|
|
12200
|
+
"values": "HTML attributes"
|
|
12201
|
+
}
|
|
12202
|
+
],
|
|
12203
|
+
"astroSource": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport { resolveLumenAstroProps } from '@santi020k/lumen-core'\n\ninterface Props extends HTMLAttributes<'span'> {\n class?: string | null\n className?: string\n label?: string\n size?: 'lg' | 'md' | 'sm'\n tone?: 'accent' | 'auto' | 'brand' | 'neutral'\n variant?: 'empty' | 'error' | 'offline' | 'success'\n}\n\nconst {\n class: classProp = '',\n className = '',\n label,\n size = 'md',\n tone = 'auto',\n variant = 'empty',\n ...rest\n} = Astro.props\n\nconst { classList, passthrough } = resolveLumenAstroProps(rest, [\n 'ui-illustration',\n `ui-illustration--${variant}`,\n `ui-illustration--${tone}`,\n `ui-illustration--${size}`\n], classProp, className)\n---\n\n<span\n aria-hidden={label ? undefined : 'true'}\n aria-label={label}\n class:list={classList}\n role={label ? 'img' : undefined}\n {...passthrough}\n>\n <svg aria-hidden=\"true\" fill=\"none\" viewBox=\"0 0 120 120\">\n <circle class=\"ui-illustration__wash\" cx=\"60\" cy=\"60\" r=\"48\" />\n {variant === 'empty' && (\n <g>\n <path d=\"M32 48h56l-7 35H39l-7-35Z\" />\n <path d=\"M43 48 49 36h22l6 12M47 66h26\" />\n </g>\n )}\n {variant === 'success' && (\n <g>\n <circle cx=\"60\" cy=\"60\" r=\"28\" />\n <path d=\"m45 60 10 10 21-23\" />\n </g>\n )}\n {variant === 'error' && (\n <g>\n <circle cx=\"60\" cy=\"60\" r=\"28\" />\n <path d=\"m49 49 22 22m0-22L49 71\" />\n </g>\n )}\n {variant === 'offline' && (\n <g>\n <path d=\"M37 74h44a14 14 0 0 0 1-28 23 23 0 0 0-42-4 16 16 0 0 0-3 32Z\" />\n <path d=\"m38 34 44 52\" />\n </g>\n )}\n </svg>\n</span>\n",
|
|
12204
|
+
"category": "Feedback",
|
|
12205
|
+
"collections": [],
|
|
12206
|
+
"dependencies": [
|
|
12207
|
+
"styles"
|
|
12208
|
+
],
|
|
12209
|
+
"description": "Shows a built-in semantic scene for empty, success, error, or offline states.",
|
|
12210
|
+
"behavior": {
|
|
12211
|
+
"astro": "none",
|
|
12212
|
+
"elements": "registered-element",
|
|
12213
|
+
"react": "component"
|
|
12214
|
+
},
|
|
12215
|
+
"files": [
|
|
12216
|
+
"packages/astro/components/Illustration.astro",
|
|
12217
|
+
"packages/astro/styles/lumen.css",
|
|
12218
|
+
"packages/react/src/components.tsx"
|
|
12219
|
+
],
|
|
12220
|
+
"frameworkDetails": {
|
|
12221
|
+
"astro": {
|
|
12222
|
+
"available": true,
|
|
12223
|
+
"example": "<Illustration label=\"No projects yet\" size=\"md\" variant=\"empty\" />",
|
|
12224
|
+
"importStatement": "import { Illustration } from '@santi020k/lumen-astro'",
|
|
12225
|
+
"language": "astro",
|
|
12226
|
+
"packageName": "@santi020k/lumen-astro",
|
|
12227
|
+
"props": [
|
|
12228
|
+
{
|
|
12229
|
+
"name": "class",
|
|
12230
|
+
"optional": true,
|
|
12231
|
+
"type": "string | null"
|
|
12232
|
+
},
|
|
12233
|
+
{
|
|
12234
|
+
"name": "className",
|
|
12235
|
+
"optional": true,
|
|
12236
|
+
"type": "string"
|
|
12237
|
+
},
|
|
12238
|
+
{
|
|
12239
|
+
"name": "label",
|
|
12240
|
+
"optional": true,
|
|
12241
|
+
"type": "string"
|
|
12242
|
+
},
|
|
12243
|
+
{
|
|
12244
|
+
"name": "size",
|
|
12245
|
+
"optional": true,
|
|
12246
|
+
"type": "'lg' | 'md' | 'sm'"
|
|
12247
|
+
},
|
|
12248
|
+
{
|
|
12249
|
+
"name": "tone",
|
|
12250
|
+
"optional": true,
|
|
12251
|
+
"type": "'accent' | 'auto' | 'brand' | 'neutral'"
|
|
12252
|
+
},
|
|
12253
|
+
{
|
|
12254
|
+
"name": "variant",
|
|
12255
|
+
"optional": true,
|
|
12256
|
+
"type": "'empty' | 'error' | 'offline' | 'success'"
|
|
12257
|
+
}
|
|
12258
|
+
],
|
|
12259
|
+
"propsExtends": "HTMLAttributes<'span'>",
|
|
12260
|
+
"source": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport { resolveLumenAstroProps } from '@santi020k/lumen-core'\n\ninterface Props extends HTMLAttributes<'span'> {\n class?: string | null\n className?: string\n label?: string\n size?: 'lg' | 'md' | 'sm'\n tone?: 'accent' | 'auto' | 'brand' | 'neutral'\n variant?: 'empty' | 'error' | 'offline' | 'success'\n}\n\nconst {\n class: classProp = '',\n className = '',\n label,\n size = 'md',\n tone = 'auto',\n variant = 'empty',\n ...rest\n} = Astro.props\n\nconst { classList, passthrough } = resolveLumenAstroProps(rest, [\n 'ui-illustration',\n `ui-illustration--${variant}`,\n `ui-illustration--${tone}`,\n `ui-illustration--${size}`\n], classProp, className)\n---\n\n<span\n aria-hidden={label ? undefined : 'true'}\n aria-label={label}\n class:list={classList}\n role={label ? 'img' : undefined}\n {...passthrough}\n>\n <svg aria-hidden=\"true\" fill=\"none\" viewBox=\"0 0 120 120\">\n <circle class=\"ui-illustration__wash\" cx=\"60\" cy=\"60\" r=\"48\" />\n {variant === 'empty' && (\n <g>\n <path d=\"M32 48h56l-7 35H39l-7-35Z\" />\n <path d=\"M43 48 49 36h22l6 12M47 66h26\" />\n </g>\n )}\n {variant === 'success' && (\n <g>\n <circle cx=\"60\" cy=\"60\" r=\"28\" />\n <path d=\"m45 60 10 10 21-23\" />\n </g>\n )}\n {variant === 'error' && (\n <g>\n <circle cx=\"60\" cy=\"60\" r=\"28\" />\n <path d=\"m49 49 22 22m0-22L49 71\" />\n </g>\n )}\n {variant === 'offline' && (\n <g>\n <path d=\"M37 74h44a14 14 0 0 0 1-28 23 23 0 0 0-42-4 16 16 0 0 0-3 32Z\" />\n <path d=\"m38 34 44 52\" />\n </g>\n )}\n </svg>\n</span>\n",
|
|
12261
|
+
"styleImport": "import '@santi020k/lumen-astro/styles.css'"
|
|
12262
|
+
},
|
|
12263
|
+
"elements": {
|
|
12264
|
+
"attributes": [
|
|
12265
|
+
"size",
|
|
12266
|
+
"tone",
|
|
12267
|
+
"variant",
|
|
12268
|
+
"label",
|
|
12269
|
+
"class, className",
|
|
12270
|
+
"...native attributes"
|
|
12271
|
+
],
|
|
12272
|
+
"available": true,
|
|
12273
|
+
"behavior": {
|
|
12274
|
+
"mode": "registration",
|
|
12275
|
+
"setup": "Call defineLumenElements() once before rendering Lumen custom elements."
|
|
12276
|
+
},
|
|
12277
|
+
"example": "<lumen-illustration label=\"No projects yet\" size=\"md\" variant=\"empty\"></lumen-illustration>",
|
|
12278
|
+
"importStatement": "import { defineLumenElements } from '@santi020k/lumen-elements/define'",
|
|
12279
|
+
"language": "html",
|
|
12280
|
+
"packageName": "@santi020k/lumen-elements",
|
|
12281
|
+
"props": [],
|
|
12282
|
+
"propsExtends": "HTMLElement attributes",
|
|
12283
|
+
"registration": "defineLumenElements()",
|
|
12284
|
+
"source": "Illustration: {\n attributeClasses: {\n size: {\n lg: 'ui-illustration--lg',\n md: 'ui-illustration--md',\n sm: 'ui-illustration--sm'\n },\n tone: {\n accent: 'ui-illustration--accent',\n auto: 'ui-illustration--auto',\n brand: 'ui-illustration--brand',\n neutral: 'ui-illustration--neutral'\n },\n variant: {\n empty: 'ui-illustration--empty',\n error: 'ui-illustration--error',\n offline: 'ui-illustration--offline',\n success: 'ui-illustration--success'\n }\n },\n baseClassName: 'ui-illustration',\n defaults: { size: 'md', tone: 'auto', variant: 'empty' },\n tagName: 'lumen-illustration'\n }",
|
|
12285
|
+
"styleImport": "import '@santi020k/lumen-elements/styles.css'",
|
|
12286
|
+
"tagName": "lumen-illustration"
|
|
12287
|
+
},
|
|
12288
|
+
"react": {
|
|
12289
|
+
"available": true,
|
|
12290
|
+
"example": "<Illustration label=\"No projects yet\" size=\"md\" variant=\"empty\" />",
|
|
12291
|
+
"importStatement": "import { Illustration } from '@santi020k/lumen-react'",
|
|
12292
|
+
"language": "tsx",
|
|
12293
|
+
"packageName": "@santi020k/lumen-react",
|
|
12294
|
+
"props": [
|
|
12295
|
+
{
|
|
12296
|
+
"name": "label",
|
|
12297
|
+
"optional": true,
|
|
12298
|
+
"type": "string"
|
|
12299
|
+
},
|
|
12300
|
+
{
|
|
12301
|
+
"name": "size",
|
|
12302
|
+
"optional": true,
|
|
12303
|
+
"type": "'lg' | 'md' | 'sm'"
|
|
12304
|
+
},
|
|
12305
|
+
{
|
|
12306
|
+
"name": "tone",
|
|
12307
|
+
"optional": true,
|
|
12308
|
+
"type": "'accent' | 'auto' | 'brand' | 'neutral'"
|
|
12309
|
+
},
|
|
12310
|
+
{
|
|
12311
|
+
"name": "variant",
|
|
12312
|
+
"optional": true,
|
|
12313
|
+
"type": "'empty' | 'error' | 'offline' | 'success'"
|
|
12314
|
+
}
|
|
12315
|
+
],
|
|
12316
|
+
"propsExtends": "ComponentPropsWithoutRef<'span'>",
|
|
12317
|
+
"source": "export interface IllustrationProps extends ComponentPropsWithoutRef<'span'> {\n label?: string\n size?: 'lg' | 'md' | 'sm'\n tone?: 'accent' | 'auto' | 'brand' | 'neutral'\n variant?: 'empty' | 'error' | 'offline' | 'success'\n}\n\nexport const Illustration = ({\n className,\n label,\n size = 'md',\n tone = 'auto',\n variant = 'empty',\n ...props\n}: IllustrationProps) => (\n <span\n aria-hidden={label ? undefined : true}\n aria-label={label}\n className={composeClassName(\n 'ui-illustration',\n `ui-illustration--${variant}`,\n `ui-illustration--${tone}`,\n `ui-illustration--${size}`,\n className\n )}\n role={label ? 'img' : undefined}\n {...props}\n >\n <svg aria-hidden=\"true\" fill=\"none\" viewBox=\"0 0 120 120\">\n <circle className=\"ui-illustration__wash\" cx=\"60\" cy=\"60\" r=\"48\" />\n {variant === 'empty' && (\n <g>\n <path d=\"M32 48h56l-7 35H39l-7-35Z\" />\n <path d=\"M43 48 49 36h22l6 12M47 66h26\" />\n </g>\n )}\n {variant === 'success' && (\n <g>\n <circle cx=\"60\" cy=\"60\" r=\"28\" />\n <path d=\"m45 60 10 10 21-23\" />\n </g>\n )}\n {variant === 'error' && (\n <g>\n <circle cx=\"60\" cy=\"60\" r=\"28\" />\n <path d=\"m49 49 22 22m0-22L49 71\" />\n </g>\n )}\n {variant === 'offline' && (\n <g>\n <path d=\"M37 74h44a14 14 0 0 0 1-28 23 23 0 0 0-42-4 16 16 0 0 0-3 32Z\" />\n <path d=\"m38 34 44 52\" />\n </g>\n )}\n </svg>\n </span>\n)",
|
|
12318
|
+
"styleImport": "import '@santi020k/lumen-react/styles.css'"
|
|
12319
|
+
}
|
|
12320
|
+
},
|
|
12321
|
+
"frameworks": {
|
|
12322
|
+
"astro": true,
|
|
12323
|
+
"elements": true,
|
|
12324
|
+
"react": true
|
|
12325
|
+
},
|
|
12326
|
+
"guidance": null,
|
|
12327
|
+
"kebab": "illustration",
|
|
12328
|
+
"keyboardInteractions": [],
|
|
12329
|
+
"keywords": [
|
|
12330
|
+
"built",
|
|
12331
|
+
"empty",
|
|
12332
|
+
"error",
|
|
12333
|
+
"feedback",
|
|
12334
|
+
"for",
|
|
12335
|
+
"illustration",
|
|
12336
|
+
"offline",
|
|
12337
|
+
"scene",
|
|
12338
|
+
"semantic",
|
|
12339
|
+
"shows",
|
|
12340
|
+
"states",
|
|
12341
|
+
"success"
|
|
12342
|
+
],
|
|
12343
|
+
"name": "Illustration",
|
|
12344
|
+
"props": [
|
|
12345
|
+
{
|
|
12346
|
+
"name": "class",
|
|
12347
|
+
"optional": true,
|
|
12348
|
+
"type": "string | null"
|
|
12349
|
+
},
|
|
12350
|
+
{
|
|
12351
|
+
"name": "className",
|
|
12352
|
+
"optional": true,
|
|
12353
|
+
"type": "string"
|
|
12354
|
+
},
|
|
12355
|
+
{
|
|
12356
|
+
"name": "label",
|
|
12357
|
+
"optional": true,
|
|
12358
|
+
"type": "string"
|
|
12359
|
+
},
|
|
12360
|
+
{
|
|
12361
|
+
"name": "size",
|
|
12362
|
+
"optional": true,
|
|
12363
|
+
"type": "'lg' | 'md' | 'sm'"
|
|
12364
|
+
},
|
|
12365
|
+
{
|
|
12366
|
+
"name": "tone",
|
|
12367
|
+
"optional": true,
|
|
12368
|
+
"type": "'accent' | 'auto' | 'brand' | 'neutral'"
|
|
12369
|
+
},
|
|
12370
|
+
{
|
|
12371
|
+
"name": "variant",
|
|
12372
|
+
"optional": true,
|
|
12373
|
+
"type": "'empty' | 'error' | 'offline' | 'success'"
|
|
12374
|
+
}
|
|
12375
|
+
],
|
|
12376
|
+
"propsExtends": "HTMLAttributes<'span'>",
|
|
12377
|
+
"recipes": [],
|
|
12378
|
+
"runtimeEvents": []
|
|
12379
|
+
},
|
|
11736
12380
|
{
|
|
11737
12381
|
"apiReference": [
|
|
11738
12382
|
{
|
|
@@ -24322,11 +24966,41 @@
|
|
|
24322
24966
|
},
|
|
24323
24967
|
{
|
|
24324
24968
|
"apiReference": [
|
|
24969
|
+
{
|
|
24970
|
+
"attribute": "locales",
|
|
24971
|
+
"defaultValue": "English and Spanish",
|
|
24972
|
+
"description": "Defines the ordered locale values and human-readable labels to cycle through.",
|
|
24973
|
+
"values": "LumenLocaleOption[]"
|
|
24974
|
+
},
|
|
24975
|
+
{
|
|
24976
|
+
"attribute": "defaultValue",
|
|
24977
|
+
"defaultValue": "first locale",
|
|
24978
|
+
"description": "Sets the initial locale for uncontrolled usage.",
|
|
24979
|
+
"values": "string"
|
|
24980
|
+
},
|
|
24981
|
+
{
|
|
24982
|
+
"attribute": "value",
|
|
24983
|
+
"defaultValue": "-",
|
|
24984
|
+
"description": "Enables controlled usage; ui:language-change requests the next locale without mutating document language.",
|
|
24985
|
+
"values": "string"
|
|
24986
|
+
},
|
|
24987
|
+
{
|
|
24988
|
+
"attribute": "storageKey",
|
|
24989
|
+
"defaultValue": "-",
|
|
24990
|
+
"description": "Persists uncontrolled locale selection when browser storage is available.",
|
|
24991
|
+
"values": "string"
|
|
24992
|
+
},
|
|
24993
|
+
{
|
|
24994
|
+
"attribute": "labelTemplate",
|
|
24995
|
+
"defaultValue": "Change language from {current} to {next}",
|
|
24996
|
+
"description": "Localizes the accessible label while retaining current and next locale names.",
|
|
24997
|
+
"values": "string"
|
|
24998
|
+
},
|
|
24325
24999
|
{
|
|
24326
25000
|
"attribute": "children",
|
|
24327
|
-
"defaultValue": "
|
|
24328
|
-
"description": "
|
|
24329
|
-
"values": "locale label"
|
|
25001
|
+
"defaultValue": "next locale label",
|
|
25002
|
+
"description": "Overrides the generated visible next-locale label.",
|
|
25003
|
+
"values": "locale label or custom content"
|
|
24330
25004
|
},
|
|
24331
25005
|
{
|
|
24332
25006
|
"attribute": "type",
|
|
@@ -24347,13 +25021,13 @@
|
|
|
24347
25021
|
"values": "HTML attributes"
|
|
24348
25022
|
}
|
|
24349
25023
|
],
|
|
24350
|
-
"astroSource": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport { resolveLumenAstroProps
|
|
25024
|
+
"astroSource": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport {\n formatLumenLanguageLabel,\n getLumenLocalePair,\n type LumenLocaleOption,\n normalizeLumenLocales,\n resolveLumenAstroProps\n} from '@santi020k/lumen-core'\n\ninterface Props extends HTMLAttributes<'button'> {\n class?: string | null\n className?: string\n defaultValue?: string\n labelTemplate?: string\n locales?: readonly LumenLocaleOption[]\n storageKey?: string\n value?: string\n}\n\nconst {\n class: classProp = '',\n className = '',\n defaultValue,\n labelTemplate = 'Change language from {current} to {next}',\n locales: localeOptions,\n storageKey,\n type = 'button',\n value,\n ...rest\n} = Astro.props\n\nconst locales = normalizeLumenLocales(localeOptions)\nconst initialValue = value ?? defaultValue ?? locales[0]?.value\nconst { current, next } = getLumenLocalePair(locales, initialValue)\nconst accessibleLabel = formatLumenLanguageLabel(labelTemplate, current, next)\n\nconst { classList, passthrough } = resolveLumenAstroProps(rest, [\n 'ui-language-toggle'\n], classProp, className)\n---\n\n<button\n type={type}\n class:list={classList}\n data-ui-language-toggle\n data-ui-language-controlled={value === undefined ? undefined : ''}\n data-ui-language-initial-value={initialValue}\n data-ui-language-label-template={labelTemplate}\n data-ui-language-locales={JSON.stringify(locales)}\n data-ui-language-value={current.value}\n data-ui-language-next-value={next.value}\n data-ui-language-storage-key={storageKey}\n aria-label={accessibleLabel}\n {...passthrough}\n>\n {\n Astro.slots.has('default') ?\n <slot /> :\n <span data-ui-language-label lang={next.value}>{next.label}</span>\n }\n</button>\n",
|
|
24351
25025
|
"category": "Actions",
|
|
24352
25026
|
"collections": [],
|
|
24353
25027
|
"dependencies": [],
|
|
24354
|
-
"description": "
|
|
25028
|
+
"description": "Coordinates controlled or persistent locale selection and document language as a compact accessible control.",
|
|
24355
25029
|
"behavior": {
|
|
24356
|
-
"astro": "
|
|
25030
|
+
"astro": "ui-primitives",
|
|
24357
25031
|
"elements": "registered-element",
|
|
24358
25032
|
"react": "component"
|
|
24359
25033
|
},
|
|
@@ -24364,7 +25038,11 @@
|
|
|
24364
25038
|
"frameworkDetails": {
|
|
24365
25039
|
"astro": {
|
|
24366
25040
|
"available": true,
|
|
24367
|
-
"
|
|
25041
|
+
"behavior": {
|
|
25042
|
+
"mode": "runtime",
|
|
25043
|
+
"setup": "Mount UIPrimitives once in the application's root layout; do not mount it beside each component."
|
|
25044
|
+
},
|
|
25045
|
+
"example": "<LanguageToggle defaultValue=\"en\" storageKey=\"site-language\" locales={[{ label: 'English', value: 'en' }, { label: 'Español', value: 'es' }]} />",
|
|
24368
25046
|
"importStatement": "import { LanguageToggle } from '@santi020k/lumen-astro'",
|
|
24369
25047
|
"language": "astro",
|
|
24370
25048
|
"packageName": "@santi020k/lumen-astro",
|
|
@@ -24378,14 +25056,45 @@
|
|
|
24378
25056
|
"name": "className",
|
|
24379
25057
|
"optional": true,
|
|
24380
25058
|
"type": "string"
|
|
25059
|
+
},
|
|
25060
|
+
{
|
|
25061
|
+
"name": "defaultValue",
|
|
25062
|
+
"optional": true,
|
|
25063
|
+
"type": "string"
|
|
25064
|
+
},
|
|
25065
|
+
{
|
|
25066
|
+
"name": "labelTemplate",
|
|
25067
|
+
"optional": true,
|
|
25068
|
+
"type": "string"
|
|
25069
|
+
},
|
|
25070
|
+
{
|
|
25071
|
+
"name": "locales",
|
|
25072
|
+
"optional": true,
|
|
25073
|
+
"type": "readonly LumenLocaleOption[]"
|
|
25074
|
+
},
|
|
25075
|
+
{
|
|
25076
|
+
"name": "storageKey",
|
|
25077
|
+
"optional": true,
|
|
25078
|
+
"type": "string"
|
|
25079
|
+
},
|
|
25080
|
+
{
|
|
25081
|
+
"name": "value",
|
|
25082
|
+
"optional": true,
|
|
25083
|
+
"type": "string"
|
|
24381
25084
|
}
|
|
24382
25085
|
],
|
|
24383
25086
|
"propsExtends": "HTMLAttributes<'button'>",
|
|
24384
|
-
"source": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport { resolveLumenAstroProps
|
|
25087
|
+
"source": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport {\n formatLumenLanguageLabel,\n getLumenLocalePair,\n type LumenLocaleOption,\n normalizeLumenLocales,\n resolveLumenAstroProps\n} from '@santi020k/lumen-core'\n\ninterface Props extends HTMLAttributes<'button'> {\n class?: string | null\n className?: string\n defaultValue?: string\n labelTemplate?: string\n locales?: readonly LumenLocaleOption[]\n storageKey?: string\n value?: string\n}\n\nconst {\n class: classProp = '',\n className = '',\n defaultValue,\n labelTemplate = 'Change language from {current} to {next}',\n locales: localeOptions,\n storageKey,\n type = 'button',\n value,\n ...rest\n} = Astro.props\n\nconst locales = normalizeLumenLocales(localeOptions)\nconst initialValue = value ?? defaultValue ?? locales[0]?.value\nconst { current, next } = getLumenLocalePair(locales, initialValue)\nconst accessibleLabel = formatLumenLanguageLabel(labelTemplate, current, next)\n\nconst { classList, passthrough } = resolveLumenAstroProps(rest, [\n 'ui-language-toggle'\n], classProp, className)\n---\n\n<button\n type={type}\n class:list={classList}\n data-ui-language-toggle\n data-ui-language-controlled={value === undefined ? undefined : ''}\n data-ui-language-initial-value={initialValue}\n data-ui-language-label-template={labelTemplate}\n data-ui-language-locales={JSON.stringify(locales)}\n data-ui-language-value={current.value}\n data-ui-language-next-value={next.value}\n data-ui-language-storage-key={storageKey}\n aria-label={accessibleLabel}\n {...passthrough}\n>\n {\n Astro.slots.has('default') ?\n <slot /> :\n <span data-ui-language-label lang={next.value}>{next.label}</span>\n }\n</button>\n",
|
|
24385
25088
|
"styleImport": "import '@santi020k/lumen-astro/styles.css'"
|
|
24386
25089
|
},
|
|
24387
25090
|
"elements": {
|
|
24388
25091
|
"attributes": [
|
|
25092
|
+
"data-ui-language-toggle",
|
|
25093
|
+
"locales",
|
|
25094
|
+
"defaultValue",
|
|
25095
|
+
"value",
|
|
25096
|
+
"storageKey",
|
|
25097
|
+
"labelTemplate",
|
|
24389
25098
|
"children",
|
|
24390
25099
|
"type",
|
|
24391
25100
|
"class, className",
|
|
@@ -24394,28 +25103,38 @@
|
|
|
24394
25103
|
"available": true,
|
|
24395
25104
|
"behavior": {
|
|
24396
25105
|
"mode": "registration",
|
|
24397
|
-
"setup": "Call defineLumenElements() once before rendering Lumen custom elements."
|
|
25106
|
+
"setup": "Call defineLumenElements() once before rendering Lumen custom elements. Registration includes this component’s interactive behavior."
|
|
24398
25107
|
},
|
|
24399
|
-
"example": "<lumen-language-toggle
|
|
25108
|
+
"example": "<lumen-language-toggle></lumen-language-toggle>",
|
|
24400
25109
|
"importStatement": "import { defineLumenElements } from '@santi020k/lumen-elements/define'",
|
|
24401
25110
|
"language": "html",
|
|
24402
25111
|
"packageName": "@santi020k/lumen-elements",
|
|
24403
25112
|
"props": [],
|
|
24404
25113
|
"propsExtends": "HTMLElement attributes",
|
|
24405
25114
|
"registration": "defineLumenElements()",
|
|
24406
|
-
"source": "LanguageToggle: {\n baseClassName: 'ui-language-toggle',\n tagName: 'lumen-language-toggle'\n }",
|
|
25115
|
+
"source": "LanguageToggle: {\n baseClassName: 'ui-language-toggle',\n defaults: { 'data-ui-language-toggle': '' },\n tagName: 'lumen-language-toggle'\n }",
|
|
24407
25116
|
"styleImport": "import '@santi020k/lumen-elements/styles.css'",
|
|
24408
25117
|
"tagName": "lumen-language-toggle"
|
|
24409
25118
|
},
|
|
24410
25119
|
"react": {
|
|
24411
25120
|
"available": true,
|
|
24412
|
-
"
|
|
25121
|
+
"behavior": {
|
|
25122
|
+
"mode": "built-in",
|
|
25123
|
+
"setup": "Interactive behavior is implemented by the React component; do not mount Astro UIPrimitives."
|
|
25124
|
+
},
|
|
25125
|
+
"example": "<LanguageToggle defaultValue=\"en\" storageKey=\"site-language\" locales={[{ label: 'English', value: 'en' }, { label: 'Español', value: 'es' }]} />",
|
|
24413
25126
|
"importStatement": "import { LanguageToggle } from '@santi020k/lumen-react'",
|
|
24414
25127
|
"language": "tsx",
|
|
24415
25128
|
"packageName": "@santi020k/lumen-react",
|
|
24416
|
-
"props": [
|
|
24417
|
-
|
|
24418
|
-
|
|
25129
|
+
"props": [
|
|
25130
|
+
{
|
|
25131
|
+
"name": "labelTemplate",
|
|
25132
|
+
"optional": true,
|
|
25133
|
+
"type": "string | undefined"
|
|
25134
|
+
}
|
|
25135
|
+
],
|
|
25136
|
+
"propsExtends": "Omit<ComponentPropsWithoutRef<'button'>, 'defaultValue' | 'value'>, LanguageToggleOptions",
|
|
25137
|
+
"source": "export interface LanguageToggleProps\n extends Omit<ComponentPropsWithoutRef<'button'>, 'defaultValue' | 'value'>,\n LanguageToggleOptions {\n labelTemplate?: string | undefined\n}\n\nexport const LanguageToggle = ({\n children,\n className,\n defaultValue,\n labelTemplate = 'Change language from {current} to {next}',\n locales,\n onClick,\n onValueChange,\n storageKey,\n type = 'button',\n value,\n ...props\n}: LanguageToggleProps) => {\n const language = useLanguageToggle({\n defaultValue,\n locales,\n onValueChange,\n storageKey,\n value\n })\n\n const accessibleLabel = formatLumenLanguageLabel(\n labelTemplate,\n language.currentLocale,\n language.nextLocale\n )\n\n return (\n <button\n aria-label={accessibleLabel}\n className={composeClassName('ui-language-toggle', className)}\n data-ui-language-next-value={language.nextLocale.value}\n data-ui-language-toggle\n data-ui-language-value={language.value}\n onClick={event => {\n onClick?.(event)\n\n if (event.defaultPrevented) return\n\n language.selectNext()\n\n event.currentTarget.dispatchEvent(new CustomEvent('ui:language-change', {\n bubbles: true,\n detail: {\n previousValue: language.currentLocale.value,\n value: language.nextLocale.value\n }\n }))\n }}\n type={type}\n {...props}\n >\n {children ?? (\n <span lang={language.nextLocale.value}>\n {language.nextLocale.label}\n </span>\n )}\n </button>\n )\n}",
|
|
24419
25138
|
"styleImport": "import '@santi020k/lumen-react/styles.css'"
|
|
24420
25139
|
}
|
|
24421
25140
|
},
|
|
@@ -24428,17 +25147,19 @@
|
|
|
24428
25147
|
"kebab": "language-toggle",
|
|
24429
25148
|
"keyboardInteractions": [],
|
|
24430
25149
|
"keywords": [
|
|
25150
|
+
"accessible",
|
|
24431
25151
|
"actions",
|
|
24432
|
-
"
|
|
25152
|
+
"and",
|
|
24433
25153
|
"compact",
|
|
24434
25154
|
"control",
|
|
24435
|
-
"
|
|
24436
|
-
"
|
|
25155
|
+
"controlled",
|
|
25156
|
+
"coordinates",
|
|
25157
|
+
"document",
|
|
24437
25158
|
"language",
|
|
24438
25159
|
"languagetoggle",
|
|
24439
25160
|
"locale",
|
|
24440
|
-
"
|
|
24441
|
-
"
|
|
25161
|
+
"persistent",
|
|
25162
|
+
"selection"
|
|
24442
25163
|
],
|
|
24443
25164
|
"name": "LanguageToggle",
|
|
24444
25165
|
"props": [
|
|
@@ -24451,6 +25172,31 @@
|
|
|
24451
25172
|
"name": "className",
|
|
24452
25173
|
"optional": true,
|
|
24453
25174
|
"type": "string"
|
|
25175
|
+
},
|
|
25176
|
+
{
|
|
25177
|
+
"name": "defaultValue",
|
|
25178
|
+
"optional": true,
|
|
25179
|
+
"type": "string"
|
|
25180
|
+
},
|
|
25181
|
+
{
|
|
25182
|
+
"name": "labelTemplate",
|
|
25183
|
+
"optional": true,
|
|
25184
|
+
"type": "string"
|
|
25185
|
+
},
|
|
25186
|
+
{
|
|
25187
|
+
"name": "locales",
|
|
25188
|
+
"optional": true,
|
|
25189
|
+
"type": "readonly LumenLocaleOption[]"
|
|
25190
|
+
},
|
|
25191
|
+
{
|
|
25192
|
+
"name": "storageKey",
|
|
25193
|
+
"optional": true,
|
|
25194
|
+
"type": "string"
|
|
25195
|
+
},
|
|
25196
|
+
{
|
|
25197
|
+
"name": "value",
|
|
25198
|
+
"optional": true,
|
|
25199
|
+
"type": "string"
|
|
24454
25200
|
}
|
|
24455
25201
|
],
|
|
24456
25202
|
"propsExtends": "HTMLAttributes<'button'>",
|
|
@@ -32189,13 +32935,13 @@
|
|
|
32189
32935
|
}
|
|
32190
32936
|
],
|
|
32191
32937
|
"docs": {
|
|
32192
|
-
"aiUsage": "# AI Usage Guide\n\nUse this guide when an AI agent is building an app with Lumen or updating examples that teach\nanother agent how to use the library.\n\n## Install the Lumen Agent Skill\n\nLumen ships a portable Agent Skill for AI coding tools that support the open `SKILL.md` format:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe source lives in [`skills/lumen-ui`](../skills/lumen-ui). The skill provides the durable\nframework-selection, component-composition, theming, accessibility, and verification workflow.\nUse it by naming `$lumen-ui` in a prompt, or let a compatible agent trigger it from Lumen-specific\nrequests.\n\nThe skill and MCP server complement each other: the skill explains how to work, while\n`@santi020k/lumen-mcp` supplies structured catalog search, framework-specific contracts, recipes,\nsource, tokens, resources, and rules.\n\n## Pick the Right Package\n\nUse the framework requested by the user. Every adapter shares the same Lumen foundation.\n\n| App Target | Install | Import Components From | Load Styles From |\n| --- | --- | --- | --- |\n| Astro | `@santi020k/lumen-astro` | `@santi020k/lumen-astro` | `@santi020k/lumen-astro/styles.css` |\n| React | `@santi020k/lumen-react` | `@santi020k/lumen-react` | `@santi020k/lumen-react/styles.css` |\n| React Hook Form composites | `@santi020k/lumen-react-hook-form` | `@santi020k/lumen-react-hook-form` | Uses React styles |\n| Web Components | `@santi020k/lumen-elements` | `@santi020k/lumen-elements/define` | `@santi020k/lumen-elements/styles.css` |\n| React Native / Expo | `@santi020k/lumen-react-native` | `@santi020k/lumen-react-native` | Not applicable |\n| Apple / SwiftUI | Swift Package `https://github.com/santi020k/lumen` | `LumenUI` | Not applicable |\n| Android / Compose | Local `packages/compose` module | `com.santi020k.lumen` | Not applicable |\n| Package metadata | `@santi020k/lumen-core` | `@santi020k/lumen-core` | Not applicable |\n| Optional brand icons | `@santi020k/lumen-icons-brand` | Register once, then use the framework `Icon` | Uses framework styles |\n\nEach framework package includes the shared foundation. Install `@santi020k/lumen` separately only\nwhen you need its framework-neutral CLI or registry metadata.\n\nFor React Native, mount one `LumenProvider` near the app root. For SwiftUI, attach the `LumenUI`\nSwift Package product to the application target and apply `.lumenTheme(...)` near the root. For\nCompose, include the local `lumen-compose` Gradle module and wrap content in `LumenTheme`. Native\nadapters do not load CSS or the Astro runtime.\n\nBrand marks are intentionally excluded from the default Lucide catalog. When a product needs them,\ninstall and register the optional pack once before rendering a namespaced icon:\n\n```ts\nimport { registerLumenBrandIcons } from '@santi020k/lumen-icons-brand'\n\nregisterLumenBrandIcons()\n```\n\nThen use any canonical Font Awesome Free brand name, such as `brand:github`, `brand:discord`,\n`brand:figma`, `brand:linkedin`, `brand:whatsapp`, or `brand:youtube`, through the existing Astro,\nReact, or Web Component `Icon` API. The package also provides `brand:x` as an alias for\n`brand:x-twitter`.\n\nBrand marks keep their recognizable filled geometry instead of being redrawn as Lucide outlines.\nThey still inherit the same Lumen size, alignment, `currentColor`, and accessible-label behavior as\nthe default icon catalog.\n\n## MCP-Assisted Workflow\n\nConnect `@santi020k/lumen-mcp` when the agent supports Model Context Protocol. The server returns\nvalidated structured content as well as readable text, so agents can select components without\nscraping documentation.\n\nThe MCP per-component catalog covers Astro, React, Web Components, React Native, SwiftUI, and\nCompose. Use `lumen_list_native_components` to discover platform availability and\n`lumen_get_native_component` for installation, setup, API, examples, accessibility guidance, and\noptional reference source. The skill still verifies installed packages when local versions differ.\n\n```bash\ncodex mcp add lumen -- npx -y @santi020k/lumen-mcp\n```\n\nFor Codex project configuration:\n\n```toml\n[mcp_servers.lumen]\nenabled = true\ncommand = \"npx\"\nargs = [\"-y\", \"@santi020k/lumen-mcp\"]\n```\n\nUse this sequence:\n\n1. Read `lumen://meta` and `lumen://diagnostics` to identify and verify the snapshot.\n2. Read `lumen://rules`.\n3. Search for the use case with `lumen_search`, passing the target `framework` when known.\n4. Inspect the selected framework with `lumen_get_component` and `detail: \"usage\"`.\n5. Follow its framework behavior setup and hook/controller contract.\n6. Read related multi-component guidance with `lumen_get_recipe`.\n7. Request `detail: \"source\"` only when the usage contract is insufficient.\n8. Read `lumen://tokens` before introducing custom styling.\n9. Retain `lumen://catalog-manifest` and use `lumen_diff_catalog` after upgrades to identify contracts that need refreshing.\n\nExample framework-aware component request:\n\n```json\n{\n \"name\": \"lumen_get_component\",\n \"arguments\": {\n \"name\": \"DateRangePicker\",\n \"framework\": \"react\",\n \"detail\": \"usage\"\n }\n}\n```\n\nStable resources are available at `lumen://rules`, `lumen://tokens`, `lumen://components`,\n`lumen://meta`, `lumen://catalog-manifest`, `lumen://diagnostics`,\n`lumen://components/{name}`, and `lumen://recipes/{name}`. See\n[`packages/mcp/README.md`](../packages/mcp/README.md) for Claude Desktop, Cursor, generic stdio,\nlocal repository, and programmatic examples.\n\n## Minimal Setup\n\nImport the stylesheet once in the app's global CSS, root layout, or app entry.\n\nThis section applies to web targets. Native adapters use their provider or theme APIs instead of a\nstylesheet.\n\n```css\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nIf Tailwind is present, keep both imports in the same shared CSS entry.\n\n```css\n@import \"@santi020k/lumen-react/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nKeep the layer prelude first. It places Tailwind base styles below Lumen components and Tailwind\nutilities above them, so consumer spacing, display, radius, width, and responsive visibility\nclasses override Lumen defaults predictably.\n\nAstro apps should mount `UIPrimitives` once in the root layout when using interactive primitives\nsuch as dialogs, menus, popovers, tabs, carousels, command lists, toggles, validated forms, or\ntoasts. Do not place `UIPrimitives` in each component example or next to each primitive instance;\none root include enhances all matching `data-ui-*` markup on the page.\n\n```astro\n---\nimport { UIPrimitives } from '@santi020k/lumen-astro'\n---\n\n<UIPrimitives />\n```\n\n## Astro\n\nUse direct named imports from `@santi020k/lumen-astro`.\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n</Card>\n```\n\n`Stat` defaults to a `div`. Choose `as=\"article\"` for a self-contained metric with supporting\ncontent, or `as=\"section\"` for a labeled region. Do not use a semantic root solely for styling.\nIts visual variants are `default`, `accent`, and `glass`; reserve `accent` for featured metrics and\nkeep glass selective.\n\nFor long reading surfaces, use `Anchor` for local section navigation and pass each item’s optional\nheading `depth`, `index`, and `description` when hierarchy matters. Use `activationOffset` for fixed\napplication headers. Use `ScrollProgress` for document position; keep `Progress` for\napplication-controlled task completion. In Astro, dispatch `ui:progress-change` with\n`{ value, max? }` from the `Progress` root when browser state changes after hydration. `Pill`\naccepts `neutral`, `brand`, and `outline`\nvariants, plus native link and count composition.\n\n## React\n\nReact components share the same visual classes and data attributes as Astro components. For\nbehavior-heavy primitives, use the React behavior hooks from `@santi020k/lumen-react` (`useDialog`,\n`usePopover`, `useDropdownMenu`, `useTabs`, `useSelect`, `useToast`, and `useTooltip`), which track\nthe Astro runtime's ARIA, keyboard, Escape, dismissal, and toast controller semantics.\n\nThe React package entry is a client boundary. In Next.js, it can be imported by a Server Component\nwithout evaluating Lumen's contexts and hooks in the React server runtime; the rendered Lumen\nsubtree becomes a Client Component boundary. Vite and other client-rendered React applications use\nthe same entry without additional setup.\n`Button`, `Link`, `ButtonLink`, and `Input` accept React 19 DOM refs directly as props.\n`Button asChild` composes the button contract onto one child element. Across adapters, native\nnumeric `size` remains available on `Input` and `NativeSelect`; use `visualSize` in Astro and React,\nor `visual-size` in Elements, for presentation. The pre-1.0 Astro and Elements `size=\"sm|lg\"`\nvisual alias is deprecated but remains available for incremental migration.\n`Stat as` and `variant` follow the same semantic-root and visual guidance as the Astro component.\n\n```tsx\nimport '@santi020k/lumen-react/styles.css'\nimport { Button, Card, Input, Label } from '@santi020k/lumen-react'\n\nexport function SubscribeForm() {\n return (\n <Card>\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n </Card>\n )\n}\n```\n\nFor React Hook Form, spread `register()` directly onto native-backed controls. Install\n`@santi020k/lumen-react-hook-form` only for controlled composites such as `Select`, `DatePicker`,\n`InputOTP`, and `ListBox`. Do not combine React Hook Form and `useFormValidation` unless the\napplication intentionally coordinates two validation sources. For schema validation, use\n`zodResolver` or `yupResolver` from `@hookform/resolvers`; keep Zod or Yup in the application and\nrender the resulting React Hook Form errors through Lumen's `FieldError` contract.\n\n## Web Components\n\nRegister custom elements once, then use `lumen-*` tags in HTML. The elements adapter includes\ninteractive behavior parity for DataTable, Dialog, Popover, DropdownMenu, ContextMenu, Tabs,\nSelect, ThemeBuilder, Toast, Tooltip, forms, RichTextEditor, Schedule, and VirtualList: selection,\nvalidation, rich text command, context menu, schedule drag/drop, theme, and range events, ARIA\nstate, roving/listbox keyboard paths, Escape/outside dismissal, focus return/trapping, native\nselect form participation, and the same toast controller events as the Astro runtime.\n\n```html\n<script type=\"module\">\n import { defineLumenElements } from '@santi020k/lumen-elements/define'\n\n defineLumenElements()\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input id=\"email\" type=\"email\" placeholder=\"you@example.com\"></lumen-input>\n <lumen-button type=\"submit\">Subscribe</lumen-button>\n</lumen-card>\n```\n\nPut input elements inside a native `<form data-ui-form>`. Scalar Lumen controls participate in\n`FormData`, validity, disabled state, focus, and reset through form-associated custom-element\nbehavior with a native-control fallback. Do not generate `<lumen-form>` as the submission\ncontainer.\n\n## Component Selection\n\nThe shared catalog includes:\n\n`Accordion`, `Alert`, `AlertDialog`, `Agenda`, `AspectRatio`, `Attachment`, `Autocomplete`,\n`Avatar`, `Badge`, `BarChart`, `Breadcrumb`, `Bubble`, `Button`, `ButtonGroup`, `Calendar`, `Card`,\n`Carousel`, `Chart`, `Checkbox`, `CheckboxGroup`, `Collapsible`, `Code`, `Combobox`, `Command`,\n`Container`, `ColorPicker`,\n`ContextMenu`, `DataTable`, `DatePicker`, `DateRangePicker`, `Dialog`, `Direction`, `Drawer`,\n`DropdownMenu`, `Empty`, `ErrorSummary`, `Field`, `FieldError`, `Form`, `Grid`, `HoverCard`, `Icon`,\n`Input`, `InputGroup`, `InputOTP`, `Item`, `KanbanBoard`, `KanbanColumn`, `Kbd`, `Label`, `LineChart`, `ListBox`, `Marker`, `Menubar`,\n`Message`, `MessageScroller`, `NativeSelect`, `ContextNavigation`, `NavigationMenu`, `NumberField`, `Pagination`,\n`PasswordField`, `PieChart`, `Popover`, `Progress`, `CopyButton`, `RadioGroup`, `Resizable`, `RichTextEditor`,\n`ScrollArea`, `Schedule`, `SearchField`, `Select`, `Separator`, `Sheet`, `Sidebar`, `Skeleton`,\n`Slider`, `Sparkline`, `Spinner`, `Stack`, `Switch`, `Table`, `Tabs`, `TagGroup`, `Textarea`, `ThemeBuilder`,\n`TimeField`, `Toast`, `Toggle`, `ToggleGroup`, `Tooltip`, `Tree`, `TreeGrid`, `Typography`, and\n`VirtualList`, and `VisuallyHidden`.\n\n## Product Recipes\n\n- Start full product surfaces from the bundled `analytics-dashboard`, `saas-admin`,\n `commerce-dashboard`, `project-workspace`, or `auth-onboarding` recipe. Install with\n `lumen add <recipe>` for Astro, or add `--target react` / `--target elements`. Keep the generated\n shared template stylesheet, then replace sample data and routes while preserving public Lumen\n components, semantic tokens, accessible names, focus paths, and responsive navigation.\n- Use `Sparkline` for a compact trend beside a labelled metric, `BarChart` for categorical\n comparison, `LineChart` for ordered or time-series data, and `PieChart` for a small\n part-to-whole breakdown. Pass already-aggregated `LumenChartSeries` data; keep statistics,\n resampling, model evaluation, and domain calculations in application code. Keep `showTable`\n enabled unless the same values are already available in a nearby semantic table. Prefer a bar\n chart when precise slice comparison matters or the pie would exceed roughly seven categories.\n Use `Chart` as the custom SVG/canvas escape hatch.\n Keep stable identity in `datum.x` and put display text in `datum.xLabel`. Use `formatCategory`\n and `formatValue` for localized axes, SVG titles, and data tables. Choose\n `presentation=\"bare\"` inside an existing Card, `categoryWidth` for long horizontal labels,\n `markers=\"auto\"` or a numeric marker step for dense lines, and `emptyLabel` for actionable\n source-specific guidance.\n- Use `Icon name=\"search\"` or any Lucide icon name for standard interface pictograms. Kebab-case\n names and Lucide aliases work. Pair decorative icons with `decorative`, and pass `label` only\n when the icon carries meaning on its own. Install and register\n `@santi020k/lumen-icons-brand` for supported brand marks, then use names such as\n `brand:github`. Render project-specific or unsupported brand SVGs through the default `Icon`\n slot. Astro reports unknown named icons during development instead of failing silently.\n- Use `Schedule` with `Agenda`, `Calendar`, and `DatePicker` for booking, CRM, content calendar, and\n event planning screens. Mark slots with `data-ui-schedule-slot` and events with\n `data-ui-schedule-event`; the runtime emits `ui:schedule-change` when a draggable event is moved.\n `Calendar` renders a hidden input plus selectable date grid; Astro `UIPrimitives` and registered\n Web Components wire month navigation and roving keyboard focus directly, while React apps can use\n the `Calendar` component or `useCalendar` for the same date selection contract.\n Astro `UIPrimitives` and registered Web Components bind schedule drag/drop directly; React apps\n can use `useSchedule` for `rootProps`, `getEventProps`, `getSlotProps`, and `ui:schedule-change`\n parity. Use `createScheduleSlots`, `moveScheduleEvent`, and `expandRecurringScheduleEvent` from\n `@santi020k/lumen-core` for reusable schedule state; use `getScheduleConflicts` and\n `canPlaceScheduleEvent` before committing drag/drop changes. Persist local schedule edits with\n `createScheduleStorageKey`, `saveScheduleEvents`, and `loadScheduleEvents` against any\n `localStorage`-compatible adapter. Use `resizeScheduleEvent` and `resizeScheduleEvents` for\n snap-to-grid resize handles.\n- Use `KanbanBoard` and `KanbanColumn` for status-based workspaces while continuing to use `Card` for\n items. Mark items with `data-ui-kanban-item` and put `data-ui-kanban-handle` on a dedicated button;\n do not make the whole card draggable. Astro and Elements emit the cancellable, controlled\n `ui:kanban-move-request` event, and React provides `useKanban`. The event reports `itemId`,\n `fromColumn`, `toColumn`, optional `beforeId`, and keyboard or pointer input, but never moves DOM or\n persists application data. The host owns validation, `aria-busy`, optimistic state, rollback,\n status names, and APIs. Use compact `Empty` states inside columns, preserve every column with\n `Skeleton` blocks while loading, and paginate large columns. See [Kanban composition and movement](kanban.md).\n- Use `DataTable`, `Tree`, `TreeGrid`, `VirtualList`, `Pagination`, and `Command` for dense data\n collection workflows. Astro and Elements wire selectable/sortable `DataTable` behavior and\n `VirtualList` range events; React emits the same data attributes for app-level adapters. `Tree`\n and `TreeGrid` use roving keyboard focus, and `VirtualList` emits `ui:virtual-list-range` as the\n rendered range changes. Use `createDataViewState`,\n `serializeDataViewState`, `parseDataViewState`, `applyDataViewState`, and `getVirtualRange` for\n saved views, filtering, sorting, pagination, and virtualization math. Use\n `createDataViewStorageKey`, `saveDataViewState`, and `loadDataViewState` when saved views should\n survive navigation or reloads. Use `pinDataViewColumn`, `unpinDataViewColumn`,\n `toggleDataViewSelection`, `createDataViewSearchParams`, and `createDataViewServerRequest` when\n connecting dense views to router state or server-backed data.\n- Use `Resizable` for split-pane editors, dashboards, inspectors, and side-by-side review layouts.\n Astro `UIPrimitives` and registered Web Components generate separator handles and pane sizing\n directly; React apps can use the `Resizable` component or `useResizable` for pointer, keyboard,\n min/max, and double-click reset parity.\n- Use `ThemeBuilder`, `ColorPicker`, `Card`, `Button`, and `Input` to build token preview and export\n tools. Add `data-ui-theme-brand-hue`, `data-ui-theme-accent-hue`, `data-ui-theme-scheme`,\n `data-ui-theme-mode`, manual color inputs such as `data-ui-theme-primary-hex`, and\n `data-ui-theme-target` for scoped live previews. Add `data-ui-theme-export-format`,\n `data-ui-theme-export`, and `data-ui-theme-output` to copy CSS, Figma variable JSON, or design\n token JSON. Astro `UIPrimitives` and registered Web Components wire those controls directly;\n React apps can use `useThemeBuilder` for the same tokens, exports, and control props. Use\n `createThemePalette`, `createThemeBuilderTokens`, `exportThemeBuilderValue`, `exportThemeCss`,\n `parseThemeCss`, and `scoreThemeContrast`\n for theme import/export and accessibility checks. Use `suggestReadableInk`, `mergeThemeTokens`,\n and `tuneThemeContrast` when generated palettes need readable foreground repair.\n- Use `RichTextEditor` with `ButtonGroup`, `ToggleGroup`, and `Textarea` for editor compositions.\n Controls with `data-ui-editor-command` dispatch browser editing commands and emit\n `ui:editor-command`; use `data-ui-editor-value` for headings, links, and other value-bearing\n commands. Editable surfaces support common formatting shortcuts, synchronize toggle\n `aria-pressed` state, and emit `ui:editor-change` with HTML and plain text. Astro `UIPrimitives`\n and registered Web Components bind those controls directly; React apps can use\n `useRichTextEditor` for `rootProps`, `getCommandProps`, `getEditableProps`, and `executeCommand`.\n For Tiptap, ProseMirror, Lexical, or Markdown engines, keep the external editor\n package in the app and bridge toolbar buttons through the emitted command event instead of adding\n the editor engine to Lumen.\n- Use `Autocomplete`, `SearchField`, `NumberField`, `TimeField`, `DateRangePicker`, `ColorPicker`,\n and `TagGroup` when forms need more than plain text inputs. Add `data-ui-form` to forms that\n should reflect native Constraint Validation API state into `Field` error slots on blur and submit.\n For `InputOTP`, Astro `UIPrimitives` and registered Web Components render/sync a real native input\n with visual OTP segments; React apps can use the `InputOTP` component or `useInputOTP` for the\n same sanitization, paste handling, and caret navigation.\n For `DateRangePicker`, Astro `UIPrimitives` and registered Web Components sync the paired native\n date input `min`/`max` constraints directly; React apps can use `useDateRangePicker` for the same\n start/end syncing props.\n Use native attributes such as `required`, `pattern`, `min`, `max`, and `type`; add\n `data-error-required`, `data-error-pattern`, `data-error-min`, `data-error-max`, or\n `data-error-custom` when a control needs custom copy. Listen for `ui:validate` to call\n `control.setCustomValidity(message)` before the runtime reads validity, and listen for\n `ui:invalid` or `ui:valid` on the form for submission state. Astro `UIPrimitives` and registered\n Web Components bind `data-ui-form` directly; React apps can use `useFormValidation` with `Field`\n to get the same control props, CustomEvents, and error slot updates.\n- Use `Toast` for transient feedback. Static `<Toast>` markup works without\n JavaScript. With Astro `UIPrimitives` or registered Web Components, create toasts with\n `window.LumenToast.create(detail)`, `LumenToast.create(detail)` from `@santi020k/lumen-elements`,\n or `document.dispatchEvent(new CustomEvent('ui:toast', { detail }))`; update them with\n `.update(id, detail)` or `ui:toast-update`; and dismiss them with `.dismiss(id?)` or\n `ui:toast-dismiss`. `detail` supports `title`, `description`, `variant`, `duration`, `placement`,\n `max`, and `action: { label, value }`. Destructive toasts use `role=\"alert\"`; other variants use\n `role=\"status\"`. Runtime toasts pause their timeout on hover or focus, and Escape dismisses the\n focused toast.\n\n## Runtime CustomEvents\n\nWhen `UIPrimitives` is mounted, or when Web Components are registered for matching behavior, Lumen\nuses these CustomEvents:\n\n| Event | Target | Detail | Fires When |\n| --- | --- | --- | --- |\n| `ui:data-table-selection-change` | `DataTable` root `[data-ui-datatable]` | `{ values: string[] }` | Selectable row checkboxes, the select-all checkbox, or form reset changes selected row values. |\n| `ui:schedule-change` | `Schedule` root `[data-ui-schedule]` | `{ eventId?: string, slot?: string }` | A draggable schedule event is dropped on a `[data-ui-schedule-slot]`. |\n| `ui:virtual-list-range` | `VirtualList` root `[data-ui-virtual-list]` | `{ startIndex: number, endIndex: number }` | The virtual list calculates its visible range on init or scroll. |\n| `ui:kanban-move-request` | `KanbanBoard` root `[data-ui-kanban]` | `{ itemId: string, fromColumn: string, toColumn: string, beforeId?: string, input: 'keyboard' \\| 'pointer' }` | A handle requests a controlled move; the event is cancellable and never moves data or DOM. |\n| `ui:tag-remove` | `TagGroup` root `.ui-tag-group` or `[data-ui-tag-group]` | `{ value?: string }` | A `[data-ui-tag-remove]` control removes its closest tag or list item. |\n| `ui:editor-command` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ command: string, executed: boolean, value?: string }` | A toolbar control or keyboard shortcut runs an editor command. |\n| `ui:editor-change` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ html: string, text: string }` | Editable content changes or an editor command runs. |\n| `ui:theme-change` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ hue: number, accentHue: number, mode: 'generated' \\| 'manual', scheme: 'dark' \\| 'light', tokens: Record<string, string> }` | Hue, manual color, mode, or scheme controls update generated tokens. |\n| `ui:theme-export` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ css?: string, format: 'css' \\| 'figma' \\| 'tokens', tokens: Record<string, string> \\| null, value: string }` | A `[data-ui-theme-export]` control writes the selected export to the output and clipboard when available. |\n| `ui:validate` | Form `[data-ui-form]` | `{ control, form, value }` | Before validation state is read, so custom code can call `control.setCustomValidity(message)`. |\n| `ui:invalid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds invalid controls. Failed submits focus the first invalid control. |\n| `ui:valid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds the checked control or form valid. |\n| `ui:toast` | `document` | `{ id?, title?, description?, variant?, duration?, placement?, action? }` | Creates a runtime toast in the matching notification viewport. |\n| `ui:toast-update` | `document` | `{ id, title?, description?, variant?, duration? }` | Updates a runtime toast. |\n| `ui:toast-dismiss` | `document` | `{ id? }` | Dismisses one runtime toast, or all runtime toasts when `id` is omitted. |\n| `ui:toast-action` | Toast `[data-ui-toast]` | `{ id, value? }` | A runtime toast action button is pressed, unless the action specifies a custom event name. |\n\n## Registry Helper\n\n`@santi020k/lumen` exports typed component, recipe, and documentation metadata from\n`@santi020k/lumen/registry`. The package also ships a small `lumen` binary. Individual components\nare addressable by name, including kebab-case aliases such as `data-table`:\n\n```bash\nlumen list\nlumen show Button\nlumen show data-table\nlumen add Button\nlumen add Button --target react\nlumen add Button --target elements\nlumen add data-table\nlumen show scheduler\nlumen add scheduler\nlumen add scheduler --target react\nlumen add scheduler --target elements\nlumen add scheduler --dry-run\nlumen add scheduler --fail-on-conflict\nlumen add scheduler --force\nlumen add scheduler --merge\nlumen list --registry ./registry/lumen.registry.json\nlumen add custom-recipe --registry ./registry/custom.registry.json\nlumen add private-recipe --registry https://example.com/lumen.registry.json --registry-token \"$TOKEN\"\nlumen install\nlumen doctor --json\nlumen init --framework astro --tailwind\n```\n\n`lumen add <component>` defaults to an Astro wrapper. Use `--target react` for a local React wrapper\nor `--target elements` for a custom-elements starter. Bundled recipes also accept `--target astro`,\n`--target react`, and `--target elements`; external registry inline files keep their authored paths\nand sources.\n\nBefore inventing a wrapper or custom class pattern, check the component source in\n`packages/astro/components`. Astro is the reference implementation for props, class names, data\nattributes, and accessible markup.\n\n## Styling Rules for Generated Code\n\n- Do not require consumers to configure Tailwind for Lumen components.\n- Prefer component props and composition before adding custom CSS.\n- Use the shared token names for custom surfaces: `canvas`, `surface`, `surface-muted`,\n `surface-strong`, `line`, `ink`, `ink-soft`, `ink-muted`, `brand`, `brand-solid`, `brand-soft`,\n `accent`, `success`, `warning`, and `danger`.\n- Use `glass` as a boolean prop or attribute for glass surfaces when supported.\n- Preserve semantic labels, keyboard paths, focus states, and native form attributes.\n\n## Figma Handoff\n\nUse `exportThemeFigmaVariables` from `@santi020k/lumen-core/figma` when a design workflow needs\nFigma variable-friendly color values, and `exportThemeDesignTokens` when exporting standard design\ntoken JSON for token importers. See [docs/figma.md](figma.md) for the recommended Figma workflow.\n\n## When Editing This Repo\n\n- Shared contracts belong in `packages/core`.\n- Astro implementation and standalone CSS belong in `packages/astro`.\n- React-specific primitives belong in `packages/react`.\n- Web Component-specific primitives belong in `packages/elements`.\n- Update the relevant package README when public usage changes.\n- Add tests beside package code as `*.test.ts` when behavior, exported metadata, or component\n contracts change.\n",
|
|
32193
|
-
"readme": "<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">\n <img src=\"./apps/docs/public/logo.svg\" alt=\"Lumen UI\" width=\"239\" height=\"60\">\n </a>\n</p>\n\n<h1 align=\"center\">Lumen UI</h1>\n\n<p align=\"center\">\n A cross-platform UI system with 150+ web primitives and shared native foundations.\n</p>\n\n<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">Documentation</a>\n ·\n <a href=\"https://lumen.santi020k.com/docs/components\">Components</a>\n ·\n <a href=\"https://www.figma.com/community/file/1662337342676541513\">Figma library</a>\n ·\n <a href=\"https://github.com/santi020k/lumen/issues\">Issues</a>\n</p>\n\n<p align=\"center\">\n <a href=\"https://github.com/santi020k/lumen/actions\"><img alt=\"GitHub Actions\" src=\"https://img.shields.io/github/actions/workflow/status/santi020k/lumen/ci.yml?branch=main&style=flat-square\"></a>\n <a href=\"https://www.npmjs.com/package/@santi020k/lumen-astro\"><img alt=\"npm version\" src=\"https://img.shields.io/npm/v/@santi020k/lumen-astro?style=flat-square&label=npm\"></a>\n <a href=\"./LICENSE\"><img alt=\"MIT license\" src=\"https://img.shields.io/badge/license-MIT-111827?style=flat-square\"></a>\n</p>\n\nLumen gives applications a consistent visual language without tying them to one rendering model.\nIts packages share tokens, component contracts, styles, and interaction patterns while keeping each\nframework and platform's native authoring experience.\n\n- **Framework native:** Astro components, React primitives, and standards-based custom elements.\n- **Native foundations:** generated React Native, SwiftUI, and Jetpack Compose tokens preserve the\n same semantic roles without introducing DOM or CSS assumptions.\n- **Accessible by default:** semantic markup, keyboard paths, focus management, and reduced-motion\n support are built into the primitives.\n- **Standalone CSS:** no Tailwind configuration is required. Tailwind users get an explicit layer\n integration.\n- **Progressively enhanced:** Astro interactions use a small client runtime instead of requiring an\n application framework.\n- **Ready for design and AI workflows:** published Figma resources, a portable agent skill, an MCP\n server, `llms.txt`, and a machine-readable registry ship alongside the component system.\n- **Product-ready templates:** five responsive dashboard and application families are available as\n live previews and installable Astro, React, and Elements recipes.\n\n## Dashboards and templates\n\nExplore the [template gallery](https://lumen.santi020k.com/templates) for complete analytics,\nSaaS admin, commerce, project workspace, and authentication/onboarding experiences. Each family\nuses public Lumen primitives and semantic tokens, includes responsive and accessibility coverage,\nand ships through the CLI for all three framework targets:\n\n```bash\nlumen add analytics-dashboard\nlumen add commerce-dashboard --target react\nlumen add auth-onboarding --target elements\n```\n\n## Web quick start\n\nInstall the package for your framework:\n\n```bash\n# Astro\npnpm add @santi020k/lumen-astro\n\n# React\npnpm add @santi020k/lumen-react\n\n# Web Components\npnpm add @santi020k/lumen-elements\n```\n\n### Astro\n\nImport the stylesheet and mount `UIPrimitives` once in your root layout. The runtime enhances all\ninteractive Lumen markup on the page.\n\n```astro\n---\nimport '@santi020k/lumen-astro/styles.css'\nimport { UIPrimitives } from '@santi020k/lumen-astro'\n---\n\n<html lang=\"en\">\n <body>\n <slot />\n <UIPrimitives />\n </body>\n</html>\n```\n\n```astro\n---\nimport { Button, Card, Input } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <label for=\"email\">Email</label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n</Card>\n```\n\n### React\n\nLoad the stylesheet once from your app entry or global CSS:\n\n```tsx\nimport '@santi020k/lumen-react/styles.css'\nimport { Button, Card, Input } from '@santi020k/lumen-react'\n\nexport function SubscribeForm() {\n return (\n <Card>\n <label htmlFor=\"email\">Email</label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n </Card>\n )\n}\n```\n\n### Web Components\n\nImport the styles and register the elements once:\n\n```html\n<style>\n @import \"@santi020k/lumen-elements/styles.css\";\n</style>\n\n<script type=\"module\">\n import { defineLumenElements } from '@santi020k/lumen-elements/define'\n\n defineLumenElements()\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\"></lumen-input>\n <lumen-button>Subscribe</lumen-button>\n</lumen-card>\n```\n\nSee the [Web documentation](https://lumen.santi020k.com/docs/web) for installation, theming,\ncomponent examples, and API details.\n\nFor native applications, choose the [React Native](https://lumen.santi020k.com/docs/react-native),\n[Apple / SwiftUI](https://lumen.santi020k.com/docs/apple), or\n[Android / Compose](https://lumen.santi020k.com/docs/android) guide. The\n[shared foundations](https://lumen.santi020k.com/docs/foundations) explain the cross-platform token\nand component contract; repository contributors can also use the\n[cross-platform architecture](./docs/cross-platform.md) and\n[native component reference](./docs/native-components.md).\n\n> **Native platform status: Beta.** The shared token foundation is stable, while the React Native,\n> SwiftUI, and Compose component APIs are being validated through real applications and may evolve.\n> Review release notes when upgrading and share platform-specific feedback.\n\n## Native playgrounds\n\nThe repository includes three searchable, interactive galleries built from the real native\npackages:\n\n- [`apps/playground-react-native`](./apps/playground-react-native) runs through Expo on the web,\n iOS, and Android and includes EAS profiles for TestFlight, Android App Bundles, and APKs.\n- [`apps/playground-apple`](./apps/playground-apple) builds as an iOS Xcode app for devices and\n TestFlight, or as a macOS Swift Package executable for local exploration.\n- [`apps/playground-android`](./apps/playground-android) builds a native Compose application and a\n directly installable debug APK.\n\nSee the [playground workflow](./docs/playgrounds.md) for run, capture, and distribution commands.\n\n## Tailwind CSS\n\nKeep the layer prelude, Tailwind import, and Lumen stylesheet in the same shared CSS entry. Replace\n`lumen-astro` with the package for your framework.\n\n```css\n@import \"@santi020k/lumen-astro/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-astro/styles.css\";\n```\n\nThis order places Tailwind base styles before Lumen components and Tailwind utilities above Lumen\ncomponent defaults.\n\n## Packages\n\n| Package | Purpose |\n| --- | --- |\n| [`@santi020k/lumen-astro`](./packages/astro) | Reference implementation, Astro components, and progressive-enhancement runtime |\n| [`@santi020k/lumen-react`](./packages/react) | React components and behavior hooks |\n| [`@santi020k/lumen-react-hook-form`](./packages/react-hook-form) | Optional React Hook Form adapters for composite controls |\n| [`@santi020k/lumen-elements`](./packages/elements) | Standards-based Web Components |\n| [`@santi020k/lumen-core`](./packages/core) | Shared tokens, metadata, class helpers, and behavior contracts |\n| [`@santi020k/lumen-tokens`](./packages/tokens) | Canonical platform-neutral design token source |\n| [`@santi020k/lumen-react-native`](./packages/react-native) | React Native foundations and native primitives |\n| [`LumenUI`](./packages/swift) | Swift Package for SwiftUI foundations and native primitives |\n| [`lumen-compose`](./packages/compose) | Android library for Jetpack Compose foundations and native primitives |\n| [`@santi020k/lumen-icons-brand`](./packages/icons-brand) | Optional namespaced brand icons for the shared `Icon` API |\n| [`@santi020k/lumen`](./packages/lumen) | Umbrella package, CLI, registry metadata, and public package map |\n| [`@santi020k/lumen-mcp`](./packages/mcp) | MCP server for component discovery and source retrieval |\n\n## AI and design workflows\n\nInstall the portable Lumen skill in Codex, Claude Code, Cursor, Windsurf, and other compatible\ncoding agents:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe skill teaches agents how to select, compose, theme, and verify Lumen primitives. Pair it with\n[`@santi020k/lumen-mcp`](./packages/mcp) when an agent needs to search the live catalog or retrieve\ncurrent source, props, tokens, and usage rules.\n\nAdditional machine-readable surfaces include:\n\n- [`llms.txt`](./llms.txt) for a concise project map.\n- [`docs/ai-usage.md`](./docs/ai-usage.md) for downstream generation examples.\n- [`docs/styling-contract.md`](./docs/styling-contract.md) for stable parts and component variables.\n- [`docs/import-and-icon-performance.md`](./docs/import-and-icon-performance.md) for repeatable import and icon evidence.\n- [`docs/consumer-regression-fixtures.md`](./docs/consumer-regression-fixtures.md) for production-shaped local coverage.\n- [`docs/project-adoption.md`](./docs/project-adoption.md) for the sibling-project audit and staged\n web and native migration strategy.\n- [`registry/lumen.registry.json`](./registry/lumen.registry.json) for recipes and installable file\n groups.\n- [`docs/figma.md`](./docs/figma.md) for Figma variables and Code Connect workflows.\n- [`docs/cross-platform.md`](./docs/cross-platform.md) for the native architecture, support tiers,\n and component-parity policy.\n\n## Contributing\n\nLumen is a pnpm workspace. Astro is the reference implementation; shared contracts belong in\n`packages/core`, while framework-specific behavior stays in its adapter package.\n\n```bash\npnpm install\npnpm run dev\npnpm run validate\n```\n\nRead [`CONTRIBUTING.md`](./CONTRIBUTING.md) before opening a pull request. User-visible package\nchanges require a changeset.\n\n## Community\n\nLumen is free and MIT licensed while the project focuses on adoption, public examples, and\nreal-world feedback.\n\n- Explore task-oriented [guides](https://lumen.santi020k.com/guides).\n- Share a public project in the [community showcase](https://lumen.santi020k.com/community).\n- Evaluate the system with the open [team adoption path](https://lumen.santi020k.com/teams).\n- Tell us what would improve your next build through the\n [adoption survey](https://github.com/santi020k/lumen/issues/new?template=adoption-feedback.yml).\n\nMaintainers can use the [exposure playbook](./docs/exposure-playbook.md) for publishing cadence,\ncampaign measurement, video scripts, and launch copy.\n\n## License\n\nLumen is available under the [MIT License](./LICENSE).\n"
|
|
32938
|
+
"aiUsage": "# AI Usage Guide\n\nUse this guide when an AI agent is building an app with Lumen or updating examples that teach\nanother agent how to use the library.\n\n## Install the Lumen Agent Skill\n\nLumen ships a portable Agent Skill for AI coding tools that support the open `SKILL.md` format:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe source lives in [`skills/lumen-ui`](../skills/lumen-ui). The skill provides the durable\nframework-selection, component-composition, theming, accessibility, and verification workflow.\nUse it by naming `$lumen-ui` in a prompt, or let a compatible agent trigger it from Lumen-specific\nrequests.\n\nThe skill and MCP server complement each other: the skill explains how to work, while\n`@santi020k/lumen-mcp` supplies structured catalog search, framework-specific contracts, recipes,\nsource, tokens, resources, and rules.\n\n## Pick the Right Package\n\nUse the framework requested by the user. Every adapter shares the same Lumen foundation.\n\n| App Target | Install | Import Components From | Load Styles From |\n| -------------------------- | ---------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------- |\n| Astro | `@santi020k/lumen-astro` | `@santi020k/lumen-astro` | `@santi020k/lumen-astro/styles.css` |\n| React | `@santi020k/lumen-react` | `@santi020k/lumen-react` | `@santi020k/lumen-react/styles.css` |\n| React Hook Form composites | `@santi020k/lumen-react-hook-form` | `@santi020k/lumen-react-hook-form` | Uses React styles |\n| Web Components | `@santi020k/lumen-elements` | `@santi020k/lumen-elements/define` | `@santi020k/lumen-elements/styles.css` |\n| React Native / Expo | `@santi020k/lumen-react-native` | `@santi020k/lumen-react-native` | Not applicable |\n| Apple / SwiftUI | Swift Package `https://github.com/santi020k/lumen`, pinned to `1.2.0` | `LumenUI` | Not applicable |\n| Android / Compose | Maven Central `com.santi020k:lumen-compose:0.3.0` | `com.santi020k.lumen` | Not applicable |\n| Package metadata | `@santi020k/lumen-core` | `@santi020k/lumen-core` | Not applicable |\n| Optional brand icons | `@santi020k/lumen-icons-brand` | Register once, then use the framework `Icon` | Uses framework styles |\n\nEach framework package includes the shared foundation. Install `@santi020k/lumen` separately only\nwhen you need its framework-neutral CLI or registry metadata.\n\nFor React Native, mount one `LumenProvider` near the app root. For SwiftUI, attach the `LumenUI`\nSwift Package product to the application target, use an exact or compatible release-version rule,\nand apply `.lumenTheme(...)` near the root. For Compose, install\n`com.santi020k:lumen-compose:0.3.0` from Maven Central and wrap content in `LumenTheme`. Native\nadapters do not load CSS or the Astro runtime.\n\nBrand marks are intentionally excluded from the default Lucide catalog. When a product needs them,\ninstall and register the optional pack once before rendering a namespaced icon:\n\n```ts\nimport { registerLumenBrandIcons } from \"@santi020k/lumen-icons-brand\";\n\nregisterLumenBrandIcons();\n```\n\nThen use any canonical Font Awesome Free brand name, such as `brand:github`, `brand:discord`,\n`brand:figma`, `brand:linkedin`, `brand:whatsapp`, or `brand:youtube`, through the existing Astro,\nReact, or Web Component `Icon` API. The package also provides `brand:x` as an alias for\n`brand:x-twitter`.\n\nBrand marks keep their recognizable filled geometry instead of being redrawn as Lucide outlines.\nThey still inherit the same Lumen size, alignment, `currentColor`, and accessible-label behavior as\nthe default icon catalog.\n\n## MCP-Assisted Workflow\n\nConnect `@santi020k/lumen-mcp` when the agent supports Model Context Protocol. The server returns\nvalidated structured content as well as readable text, so agents can select components without\nscraping documentation.\n\nThe MCP per-component catalog covers Astro, React, Web Components, React Native, SwiftUI, and\nCompose. Use `lumen_list_native_components` to discover platform availability and\n`lumen_get_native_component` for installation, setup, API, examples, accessibility guidance, and\noptional reference source. The skill still verifies installed packages when local versions differ.\n\n```bash\ncodex mcp add lumen -- npx -y @santi020k/lumen-mcp\n```\n\nFor Codex project configuration:\n\n```toml\n[mcp_servers.lumen]\nenabled = true\ncommand = \"npx\"\nargs = [\"-y\", \"@santi020k/lumen-mcp\"]\n```\n\nUse this sequence:\n\n1. Read `lumen://meta` and `lumen://diagnostics` to identify and verify the snapshot.\n2. Read `lumen://rules`.\n3. Search for the use case with `lumen_search`, passing the target `framework` when known.\n4. Inspect the selected framework with `lumen_get_component` and `detail: \"usage\"`.\n5. Follow its framework behavior setup and hook/controller contract.\n6. Read related multi-component guidance with `lumen_get_recipe`.\n7. Request `detail: \"source\"` only when the usage contract is insufficient.\n8. Read `lumen://tokens` before introducing custom styling.\n9. Retain `lumen://catalog-manifest` and use `lumen_diff_catalog` after upgrades to identify contracts that need refreshing.\n\nExample framework-aware component request:\n\n```json\n{\n \"name\": \"lumen_get_component\",\n \"arguments\": {\n \"name\": \"DateRangePicker\",\n \"framework\": \"react\",\n \"detail\": \"usage\"\n }\n}\n```\n\nStable resources are available at `lumen://rules`, `lumen://tokens`, `lumen://components`,\n`lumen://meta`, `lumen://catalog-manifest`, `lumen://diagnostics`,\n`lumen://components/{name}`, and `lumen://recipes/{name}`. See\n[`packages/mcp/README.md`](../packages/mcp/README.md) for Claude Desktop, Cursor, generic stdio,\nlocal repository, and programmatic examples.\n\n## Minimal Setup\n\nImport the stylesheet once in the app's global CSS, root layout, or app entry.\n\nThis section applies to web targets. Native adapters use their provider or theme APIs instead of a\nstylesheet.\n\n```css\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nIf Tailwind is present, keep both imports in the same shared CSS entry.\n\n```css\n@import \"@santi020k/lumen-react/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nKeep the layer prelude first. It places Tailwind base styles below Lumen components and Tailwind\nutilities above them, so consumer spacing, display, radius, width, and responsive visibility\nclasses override Lumen defaults predictably.\n\nAstro apps should mount `UIPrimitives` once in the root layout when using interactive primitives\nsuch as dialogs, menus, popovers, tabs, carousels, command lists, toggles, validated forms, or\ntoasts. Do not place `UIPrimitives` in each component example or next to each primitive instance;\none root include enhances all matching `data-ui-*` markup on the page.\n\n```astro\n---\nimport { UIPrimitives } from '@santi020k/lumen-astro'\n---\n\n<UIPrimitives />\n```\n\n## Astro\n\nUse direct named imports from `@santi020k/lumen-astro`.\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n</Card>\n```\n\n`Stat` defaults to a `div`. Choose `as=\"article\"` for a self-contained metric with supporting\ncontent, or `as=\"section\"` for a labeled region. Do not use a semantic root solely for styling.\nIts visual variants are `default`, `accent`, and `glass`; reserve `accent` for featured metrics and\nkeep glass selective.\n\nFor long reading surfaces, use `Anchor` for local section navigation and pass each item’s optional\nheading `depth`, `index`, and `description` when hierarchy matters. Use `activationOffset` for fixed\napplication headers. Use `ScrollProgress` for document position; keep `Progress` for\napplication-controlled task completion. In Astro, dispatch `ui:progress-change` with\n`{ value, max? }` from the `Progress` root when browser state changes after hydration. `Pill`\naccepts `neutral`, `brand`, and `outline`\nvariants, plus native link and count composition.\n\n## React\n\nReact components share the same visual classes and data attributes as Astro components. For\nbehavior-heavy primitives, use the React behavior hooks from `@santi020k/lumen-react` (`useDialog`,\n`usePopover`, `useDropdownMenu`, `useTabs`, `useSelect`, `useToast`, and `useTooltip`), which track\nthe Astro runtime's ARIA, keyboard, Escape, dismissal, and toast controller semantics.\n\nThe React package entry is a client boundary. In Next.js, it can be imported by a Server Component\nwithout evaluating Lumen's contexts and hooks in the React server runtime; the rendered Lumen\nsubtree becomes a Client Component boundary. Vite and other client-rendered React applications use\nthe same entry without additional setup.\n`Button`, `Link`, `ButtonLink`, and `Input` accept React 19 DOM refs directly as props.\n`Button asChild` composes the button contract onto one child element. Across adapters, native\nnumeric `size` remains available on `Input` and `NativeSelect`; use `visualSize` in Astro and React,\nor `visual-size` in Elements, for presentation. The pre-1.0 Astro and Elements `size=\"sm|lg\"`\nvisual alias is deprecated but remains available for incremental migration.\n`Stat as` and `variant` follow the same semantic-root and visual guidance as the Astro component.\n\n```tsx\nimport \"@santi020k/lumen-react/styles.css\";\nimport { Button, Card, Input, Label } from \"@santi020k/lumen-react\";\n\nexport function SubscribeForm() {\n return (\n <Card>\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n </Card>\n );\n}\n```\n\nFor React Hook Form, spread `register()` directly onto native-backed controls. Install\n`@santi020k/lumen-react-hook-form` only for controlled composites such as `Select`, `DatePicker`,\n`InputOTP`, and `ListBox`. Do not combine React Hook Form and `useFormValidation` unless the\napplication intentionally coordinates two validation sources. For schema validation, use\n`zodResolver` or `yupResolver` from `@hookform/resolvers`; keep Zod or Yup in the application and\nrender the resulting React Hook Form errors through Lumen's `FieldError` contract.\n\n## Web Components\n\nRegister custom elements once, then use `lumen-*` tags in HTML. The elements adapter includes\ninteractive behavior parity for DataTable, Dialog, Popover, DropdownMenu, ContextMenu, Tabs,\nSelect, ThemeBuilder, Toast, Tooltip, forms, RichTextEditor, Schedule, and VirtualList: selection,\nvalidation, rich text command, context menu, schedule drag/drop, theme, and range events, ARIA\nstate, roving/listbox keyboard paths, Escape/outside dismissal, focus return/trapping, native\nselect form participation, and the same toast controller events as the Astro runtime.\n\n```html\n<script type=\"module\">\n import { defineLumenElements } from \"@santi020k/lumen-elements/define\";\n\n defineLumenElements();\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input\n id=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n ></lumen-input>\n <lumen-button type=\"submit\">Subscribe</lumen-button>\n</lumen-card>\n```\n\nPut input elements inside a native `<form data-ui-form>`. Scalar Lumen controls participate in\n`FormData`, validity, disabled state, focus, and reset through form-associated custom-element\nbehavior with a native-control fallback. Do not generate `<lumen-form>` as the submission\ncontainer.\n\n## Component Selection\n\nThe shared catalog includes:\n\n`Accordion`, `Alert`, `AlertDialog`, `Agenda`, `AspectRatio`, `Attachment`, `Autocomplete`,\n`Avatar`, `Badge`, `BarChart`, `Breadcrumb`, `Bubble`, `Button`, `ButtonGroup`, `Calendar`, `Card`,\n`Carousel`, `Chart`, `Checkbox`, `CheckboxGroup`, `Collapsible`, `Code`, `Combobox`, `Command`,\n`Container`, `ColorPicker`,\n`ContextMenu`, `DataTable`, `DatePicker`, `DateRangePicker`, `Dialog`, `Direction`, `Drawer`,\n`DropdownMenu`, `Empty`, `ErrorSummary`, `Field`, `FieldError`, `Form`, `Grid`, `HoverCard`, `Icon`,\n`Graphic`, `Backdrop`, `Illustration`, `Input`, `InputGroup`, `InputOTP`, `Item`, `KanbanBoard`, `KanbanColumn`, `Kbd`, `Label`, `LineChart`, `ListBox`, `Marker`, `Menubar`,\n`Message`, `MessageScroller`, `NativeSelect`, `ContextNavigation`, `NavigationMenu`, `NumberField`, `Pagination`,\n`PasswordField`, `PieChart`, `Popover`, `Progress`, `CopyButton`, `RadioGroup`, `Resizable`, `RichTextEditor`,\n`ScrollArea`, `Schedule`, `SearchField`, `Select`, `Separator`, `Sheet`, `Sidebar`, `Skeleton`,\n`Slider`, `Sparkline`, `Spinner`, `Stack`, `Switch`, `Table`, `Tabs`, `TagGroup`, `Textarea`, `ThemeBuilder`,\n`TimeField`, `Toast`, `Toggle`, `ToggleGroup`, `Tooltip`, `Tree`, `TreeGrid`, `Typography`, and\n`VirtualList`, and `VisuallyHidden`.\n\n## Product Recipes\n\n- Start full product surfaces from the bundled `analytics-dashboard`, `saas-admin`,\n `commerce-dashboard`, `project-workspace`, or `auth-onboarding` recipe. Install with\n `lumen add <recipe>` for Astro, or add `--target react` / `--target elements`. Keep the generated\n shared template stylesheet, then replace sample data and routes while preserving public Lumen\n components, semantic tokens, accessible names, focus paths, and responsive navigation.\n- Use `Sparkline` for a compact trend beside a labelled metric, `BarChart` for categorical\n comparison, `LineChart` for ordered or time-series data, and `PieChart` for a small\n part-to-whole breakdown. Pass already-aggregated `LumenChartSeries` data; keep statistics,\n resampling, model evaluation, and domain calculations in application code. Keep `showTable`\n enabled unless the same values are already available in a nearby semantic table. Prefer a bar\n chart when precise slice comparison matters or the pie would exceed roughly seven categories.\n Use `Chart` as the custom SVG/canvas escape hatch.\n Keep stable identity in `datum.x` and put display text in `datum.xLabel`. Use `formatCategory`\n and `formatValue` for localized axes, SVG titles, and data tables. Choose\n `presentation=\"bare\"` inside an existing Card, `categoryWidth` for long horizontal labels,\n `markers=\"auto\"` or a numeric marker step for dense lines, and `emptyLabel` for actionable\n source-specific guidance.\n- Use `Icon name=\"search\"` or any Lucide icon name for standard interface pictograms. Kebab-case\n names and Lucide aliases work. Pair decorative icons with `decorative`, and pass `label` only\n when the icon carries meaning on its own. Install and register\n `@santi020k/lumen-icons-brand` for supported brand marks, then use names such as\n `brand:github`. Render project-specific or unsupported brand SVGs through the default `Icon`\n slot. Astro reports unknown named icons during development instead of failing silently.\n- Use `Schedule` with `Agenda`, `Calendar`, and `DatePicker` for booking, CRM, content calendar, and\n event planning screens. Mark slots with `data-ui-schedule-slot` and events with\n `data-ui-schedule-event`; the runtime emits `ui:schedule-change` when a draggable event is moved.\n `Calendar` renders a hidden input plus selectable date grid; Astro `UIPrimitives` and registered\n Web Components wire month navigation and roving keyboard focus directly, while React apps can use\n the `Calendar` component or `useCalendar` for the same date selection contract.\n Astro `UIPrimitives` and registered Web Components bind schedule drag/drop directly; React apps\n can use `useSchedule` for `rootProps`, `getEventProps`, `getSlotProps`, and `ui:schedule-change`\n parity. Use `createScheduleSlots`, `moveScheduleEvent`, and `expandRecurringScheduleEvent` from\n `@santi020k/lumen-core` for reusable schedule state; use `getScheduleConflicts` and\n `canPlaceScheduleEvent` before committing drag/drop changes. Persist local schedule edits with\n `createScheduleStorageKey`, `saveScheduleEvents`, and `loadScheduleEvents` against any\n `localStorage`-compatible adapter. Use `resizeScheduleEvent` and `resizeScheduleEvents` for\n snap-to-grid resize handles.\n- Use `KanbanBoard` and `KanbanColumn` for status-based workspaces while continuing to use `Card` for\n items. Mark items with `data-ui-kanban-item` and put `data-ui-kanban-handle` on a dedicated button;\n do not make the whole card draggable. Astro and Elements emit the cancellable, controlled\n `ui:kanban-move-request` event, and React provides `useKanban`. The event reports `itemId`,\n `fromColumn`, `toColumn`, optional `beforeId`, and keyboard or pointer input, but never moves DOM or\n persists application data. The host owns validation, `aria-busy`, optimistic state, rollback,\n status names, and APIs. Use compact `Empty` states inside columns, preserve every column with\n `Skeleton` blocks while loading, and paginate large columns. See [Kanban composition and movement](kanban.md).\n- Use `DataTable`, `Tree`, `TreeGrid`, `VirtualList`, `Pagination`, and `Command` for dense data\n collection workflows. Astro and Elements wire selectable/sortable `DataTable` behavior and\n `VirtualList` range events; React emits the same data attributes for app-level adapters. `Tree`\n and `TreeGrid` use roving keyboard focus, and `VirtualList` emits `ui:virtual-list-range` as the\n rendered range changes. Use `createDataViewState`,\n `serializeDataViewState`, `parseDataViewState`, `applyDataViewState`, and `getVirtualRange` for\n saved views, filtering, sorting, pagination, and virtualization math. Use\n `createDataViewStorageKey`, `saveDataViewState`, and `loadDataViewState` when saved views should\n survive navigation or reloads. Use `pinDataViewColumn`, `unpinDataViewColumn`,\n `toggleDataViewSelection`, `createDataViewSearchParams`, and `createDataViewServerRequest` when\n connecting dense views to router state or server-backed data.\n- Use `Resizable` for split-pane editors, dashboards, inspectors, and side-by-side review layouts.\n Astro `UIPrimitives` and registered Web Components generate separator handles and pane sizing\n directly; React apps can use the `Resizable` component or `useResizable` for pointer, keyboard,\n min/max, and double-click reset parity.\n- Use `ThemeBuilder`, `ColorPicker`, `Card`, `Button`, and `Input` to build token preview and export\n tools. Add `data-ui-theme-brand-hue`, `data-ui-theme-accent-hue`, `data-ui-theme-scheme`,\n `data-ui-theme-mode`, manual color inputs such as `data-ui-theme-primary-hex`, and\n `data-ui-theme-target` for scoped live previews. Add `data-ui-theme-export-format`,\n `data-ui-theme-export`, and `data-ui-theme-output` to copy CSS, Figma variable JSON, or design\n token JSON. Astro `UIPrimitives` and registered Web Components wire those controls directly;\n React apps can use `useThemeBuilder` for the same tokens, exports, and control props. Use\n `createThemePalette`, `createThemeBuilderTokens`, `exportThemeBuilderValue`, `exportThemeCss`,\n `parseThemeCss`, and `scoreThemeContrast`\n for theme import/export and accessibility checks. Use `suggestReadableInk`, `mergeThemeTokens`,\n and `tuneThemeContrast` when generated palettes need readable foreground repair.\n- Use `RichTextEditor` with `ButtonGroup`, `ToggleGroup`, and `Textarea` for editor compositions.\n Controls with `data-ui-editor-command` dispatch browser editing commands and emit\n `ui:editor-command`; use `data-ui-editor-value` for headings, links, and other value-bearing\n commands. Editable surfaces support common formatting shortcuts, synchronize toggle\n `aria-pressed` state, and emit `ui:editor-change` with HTML and plain text. Astro `UIPrimitives`\n and registered Web Components bind those controls directly; React apps can use\n `useRichTextEditor` for `rootProps`, `getCommandProps`, `getEditableProps`, and `executeCommand`.\n For Tiptap, ProseMirror, Lexical, or Markdown engines, keep the external editor\n package in the app and bridge toolbar buttons through the emitted command event instead of adding\n the editor engine to Lumen.\n- Use `Autocomplete`, `SearchField`, `NumberField`, `TimeField`, `DateRangePicker`, `ColorPicker`,\n and `TagGroup` when forms need more than plain text inputs. Add `data-ui-form` to forms that\n should reflect native Constraint Validation API state into `Field` error slots on blur and submit.\n For `InputOTP`, Astro `UIPrimitives` and registered Web Components render/sync a real native input\n with visual OTP segments; React apps can use the `InputOTP` component or `useInputOTP` for the\n same sanitization, paste handling, and caret navigation.\n For `DateRangePicker`, Astro `UIPrimitives` and registered Web Components sync the paired native\n date input `min`/`max` constraints directly; React apps can use `useDateRangePicker` for the same\n start/end syncing props.\n Use native attributes such as `required`, `pattern`, `min`, `max`, and `type`; add\n `data-error-required`, `data-error-pattern`, `data-error-min`, `data-error-max`, or\n `data-error-custom` when a control needs custom copy. Listen for `ui:validate` to call\n `control.setCustomValidity(message)` before the runtime reads validity, and listen for\n `ui:invalid` or `ui:valid` on the form for submission state. Astro `UIPrimitives` and registered\n Web Components bind `data-ui-form` directly; React apps can use `useFormValidation` with `Field`\n to get the same control props, CustomEvents, and error slot updates.\n- Use `Toast` for transient feedback. Static `<Toast>` markup works without\n JavaScript. With Astro `UIPrimitives` or registered Web Components, create toasts with\n `window.LumenToast.create(detail)`, `LumenToast.create(detail)` from `@santi020k/lumen-elements`,\n or `document.dispatchEvent(new CustomEvent('ui:toast', { detail }))`; update them with\n `.update(id, detail)` or `ui:toast-update`; and dismiss them with `.dismiss(id?)` or\n `ui:toast-dismiss`. `detail` supports `title`, `description`, `variant`, `duration`, `placement`,\n `max`, and `action: { label, value }`. Destructive toasts use `role=\"alert\"`; other variants use\n `role=\"status\"`. Runtime toasts pause their timeout on hover or focus, and Escape dismisses the\n focused toast.\n\n## Runtime CustomEvents\n\nWhen `UIPrimitives` is mounted, or when Web Components are registered for matching behavior, Lumen\nuses these CustomEvents:\n\n| Event | Target | Detail | Fires When |\n| -------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |\n| `ui:data-table-selection-change` | `DataTable` root `[data-ui-datatable]` | `{ values: string[] }` | Selectable row checkboxes, the select-all checkbox, or form reset changes selected row values. |\n| `ui:schedule-change` | `Schedule` root `[data-ui-schedule]` | `{ eventId?: string, slot?: string }` | A draggable schedule event is dropped on a `[data-ui-schedule-slot]`. |\n| `ui:virtual-list-range` | `VirtualList` root `[data-ui-virtual-list]` | `{ startIndex: number, endIndex: number }` | The virtual list calculates its visible range on init or scroll. |\n| `ui:kanban-move-request` | `KanbanBoard` root `[data-ui-kanban]` | `{ itemId: string, fromColumn: string, toColumn: string, beforeId?: string, input: 'keyboard' \\| 'pointer' }` | A handle requests a controlled move; the event is cancellable and never moves data or DOM. |\n| `ui:tag-remove` | `TagGroup` root `.ui-tag-group` or `[data-ui-tag-group]` | `{ value?: string }` | A `[data-ui-tag-remove]` control removes its closest tag or list item. |\n| `ui:editor-command` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ command: string, executed: boolean, value?: string }` | A toolbar control or keyboard shortcut runs an editor command. |\n| `ui:editor-change` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ html: string, text: string }` | Editable content changes or an editor command runs. |\n| `ui:theme-change` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ hue: number, accentHue: number, mode: 'generated' \\| 'manual', scheme: 'dark' \\| 'light', tokens: Record<string, string> }` | Hue, manual color, mode, or scheme controls update generated tokens. |\n| `ui:theme-export` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ css?: string, format: 'css' \\| 'figma' \\| 'tokens', tokens: Record<string, string> \\| null, value: string }` | A `[data-ui-theme-export]` control writes the selected export to the output and clipboard when available. |\n| `ui:language-change` | `LanguageToggle` control | `{ previousValue: string, value: string }` | Requests or applies the next configured locale. |\n| `ui:validate` | Form `[data-ui-form]` | `{ control, form, value }` | Before validation state is read, so custom code can call `control.setCustomValidity(message)`. |\n| `ui:invalid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds invalid controls. Failed submits focus the first invalid control. |\n| `ui:valid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds the checked control or form valid. |\n| `ui:toast` | `document` | `{ id?, title?, description?, variant?, duration?, placement?, action? }` | Creates a runtime toast in the matching notification viewport. |\n| `ui:toast-update` | `document` | `{ id, title?, description?, variant?, duration? }` | Updates a runtime toast. |\n| `ui:toast-dismiss` | `document` | `{ id? }` | Dismisses one runtime toast, or all runtime toasts when `id` is omitted. |\n| `ui:toast-action` | Toast `[data-ui-toast]` | `{ id, value? }` | A runtime toast action button is pressed, unless the action specifies a custom event name. |\n\n## Registry Helper\n\n`@santi020k/lumen` exports typed component, recipe, and documentation metadata from\n`@santi020k/lumen/registry`. The package also ships a small `lumen` binary. Individual components\nare addressable by name, including kebab-case aliases such as `data-table`:\n\n```bash\nlumen list\nlumen show Button\nlumen show data-table\nlumen add Button\nlumen add Button --target react\nlumen add Button --target elements\nlumen add data-table\nlumen show scheduler\nlumen add scheduler\nlumen add scheduler --target react\nlumen add scheduler --target elements\nlumen add scheduler --dry-run\nlumen add scheduler --fail-on-conflict\nlumen add scheduler --force\nlumen add scheduler --merge\nlumen list --registry ./registry/lumen.registry.json\nlumen add custom-recipe --registry ./registry/custom.registry.json\nlumen add private-recipe --registry https://example.com/lumen.registry.json --registry-token \"$TOKEN\"\nlumen install\nlumen doctor --json\nlumen init --framework astro --tailwind\n```\n\n`lumen add <component>` defaults to an Astro wrapper. Use `--target react` for a local React wrapper\nor `--target elements` for a custom-elements starter. Bundled recipes also accept `--target astro`,\n`--target react`, and `--target elements`; external registry inline files keep their authored paths\nand sources.\n\nBefore inventing a wrapper or custom class pattern, check the component source in\n`packages/astro/components`. Astro is the reference implementation for props, class names, data\nattributes, and accessible markup.\n\n## Styling Rules for Generated Code\n\n- Do not require consumers to configure Tailwind for Lumen components.\n- Prefer component props and composition before adding custom CSS.\n- Use the shared token names for custom surfaces: `canvas`, `surface`, `surface-muted`,\n `surface-strong`, `line`, `ink`, `ink-soft`, `ink-muted`, `brand`, `brand-solid`, `brand-soft`,\n `accent`, `success`, `warning`, and `danger`.\n- Use `glass` as a boolean prop or attribute for glass surfaces when supported.\n- Preserve semantic labels, keyboard paths, focus states, and native form attributes.\n\n## Figma Handoff\n\nUse `exportThemeFigmaVariables` from `@santi020k/lumen-core/figma` when a design workflow needs\nFigma variable-friendly color values, and `exportThemeDesignTokens` when exporting standard design\ntoken JSON for token importers. See [docs/figma.md](figma.md) for the recommended Figma workflow.\n\n## When Editing This Repo\n\n- Shared contracts belong in `packages/core`.\n- Astro implementation and standalone CSS belong in `packages/astro`.\n- React-specific primitives belong in `packages/react`.\n- Web Component-specific primitives belong in `packages/elements`.\n- Update the relevant package README when public usage changes.\n- Add tests beside package code as `*.test.ts` when behavior, exported metadata, or component\n contracts change.\n",
|
|
32939
|
+
"readme": "<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">\n <img src=\"./apps/docs/public/logo.svg\" alt=\"Lumen UI\" width=\"239\" height=\"60\">\n </a>\n</p>\n\n<h1 align=\"center\">Lumen UI</h1>\n\n<p align=\"center\">\n A cross-platform UI system with 150+ web primitives and shared native foundations.\n</p>\n\n<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">Documentation</a>\n ·\n <a href=\"https://lumen.santi020k.com/docs/components\">Components</a>\n ·\n <a href=\"https://www.figma.com/community/file/1662337342676541513\">Figma library</a>\n ·\n <a href=\"https://github.com/santi020k/lumen/issues\">Issues</a>\n</p>\n\n<p align=\"center\">\n <a href=\"https://github.com/santi020k/lumen/actions\"><img alt=\"GitHub Actions\" src=\"https://img.shields.io/github/actions/workflow/status/santi020k/lumen/ci.yml?branch=main&style=flat-square\"></a>\n <a href=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml\"><img alt=\"CodeQL\" src=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml/badge.svg\"></a>\n <a href=\"https://www.npmjs.com/package/@santi020k/lumen-astro\"><img alt=\"npm version\" src=\"https://img.shields.io/npm/v/@santi020k/lumen-astro?style=flat-square&label=npm\"></a>\n <a href=\"./LICENSE\"><img alt=\"MIT license\" src=\"https://img.shields.io/badge/license-MIT-111827?style=flat-square\"></a>\n</p>\n\nLumen gives applications a consistent visual language without tying them to one rendering model.\nIts packages share tokens, component contracts, styles, and interaction patterns while keeping each\nframework and platform's native authoring experience.\n\n- **Framework native:** Astro components, React primitives, and standards-based custom elements.\n- **Native foundations:** generated React Native, SwiftUI, and Jetpack Compose tokens preserve the\n same semantic roles without introducing DOM or CSS assumptions.\n- **Accessible by default:** semantic markup, keyboard paths, focus management, and reduced-motion\n support are built into the primitives.\n- **Standalone CSS:** no Tailwind configuration is required. Tailwind users get an explicit layer\n integration.\n- **Progressively enhanced:** Astro interactions use a small client runtime instead of requiring an\n application framework.\n- **Ready for design and AI workflows:** published Figma resources, a portable agent skill, an MCP\n server, `llms.txt`, and a machine-readable registry ship alongside the component system.\n- **Product-ready templates:** five responsive dashboard and application families are available as\n live previews and installable Astro, React, and Elements recipes.\n\n## Dashboards and templates\n\nExplore the [template gallery](https://lumen.santi020k.com/templates) for complete analytics,\nSaaS admin, commerce, project workspace, and authentication/onboarding experiences. Each family\nuses public Lumen primitives and semantic tokens, includes responsive and accessibility coverage,\nand ships through the CLI for all three framework targets:\n\n```bash\nlumen add analytics-dashboard\nlumen add commerce-dashboard --target react\nlumen add auth-onboarding --target elements\n```\n\n## Web quick start\n\nInstall the package for your framework:\n\n```bash\n# Astro\npnpm add @santi020k/lumen-astro\n\n# React\npnpm add @santi020k/lumen-react\n\n# Web Components\npnpm add @santi020k/lumen-elements\n```\n\n### Astro\n\nImport the stylesheet and mount `UIPrimitives` once in your root layout. The runtime enhances all\ninteractive Lumen markup on the page.\n\n```astro\n---\nimport '@santi020k/lumen-astro/styles.css'\nimport { UIPrimitives } from '@santi020k/lumen-astro'\n---\n\n<html lang=\"en\">\n <body>\n <slot />\n <UIPrimitives />\n </body>\n</html>\n```\n\n```astro\n---\nimport { Button, Card, Input } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <label for=\"email\">Email</label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n</Card>\n```\n\n### React\n\nLoad the stylesheet once from your app entry or global CSS:\n\n```tsx\nimport '@santi020k/lumen-react/styles.css'\nimport { Button, Card, Input } from '@santi020k/lumen-react'\n\nexport function SubscribeForm() {\n return (\n <Card>\n <label htmlFor=\"email\">Email</label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n </Card>\n )\n}\n```\n\n### Web Components\n\nImport the styles and register the elements once:\n\n```html\n<style>\n @import \"@santi020k/lumen-elements/styles.css\";\n</style>\n\n<script type=\"module\">\n import { defineLumenElements } from '@santi020k/lumen-elements/define'\n\n defineLumenElements()\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\"></lumen-input>\n <lumen-button>Subscribe</lumen-button>\n</lumen-card>\n```\n\nSee the [Web documentation](https://lumen.santi020k.com/docs/web) for installation, theming,\ncomponent examples, and API details.\n\nFor native applications, choose the [React Native](https://lumen.santi020k.com/docs/react-native),\n[Apple / SwiftUI](https://lumen.santi020k.com/docs/apple), or\n[Android / Compose](https://lumen.santi020k.com/docs/android) guide. The\n[shared foundations](https://lumen.santi020k.com/docs/foundations) explain the cross-platform token\nand component contract; repository contributors can also use the\n[cross-platform architecture](./docs/cross-platform.md) and\n[native component reference](./docs/native-components.md).\n\n> **Native platform status: Beta.** The shared token foundation is stable, while the React Native,\n> SwiftUI, and Compose component APIs are being validated through real applications and may evolve.\n> Review release notes when upgrading and share platform-specific feedback.\n\n## Native playgrounds\n\nThe repository includes three searchable, interactive galleries built from the real native\npackages:\n\n- [`apps/playground-react-native`](./apps/playground-react-native) runs through Expo on the web,\n iOS, and Android and includes EAS profiles for TestFlight, Android App Bundles, and APKs.\n- [`apps/playground-apple`](./apps/playground-apple) builds as an iOS Xcode app for devices and\n TestFlight, or as a macOS Swift Package executable for local exploration.\n- [`apps/playground-android`](./apps/playground-android) builds a native Compose application and a\n directly installable debug APK.\n\nSee the [playground workflow](./docs/playgrounds.md) for run, capture, and distribution commands.\n\n## Tailwind CSS\n\nKeep the layer prelude, Tailwind import, and Lumen stylesheet in the same shared CSS entry. Replace\n`lumen-astro` with the package for your framework.\n\n```css\n@import \"@santi020k/lumen-astro/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-astro/styles.css\";\n```\n\nThis order places Tailwind base styles before Lumen components and Tailwind utilities above Lumen\ncomponent defaults.\n\n## Packages\n\n| Package | Purpose |\n| --- | --- |\n| [`@santi020k/lumen-astro`](./packages/astro) | Reference implementation, Astro components, and progressive-enhancement runtime |\n| [`@santi020k/lumen-react`](./packages/react) | React components and behavior hooks |\n| [`@santi020k/lumen-react-hook-form`](./packages/react-hook-form) | Optional React Hook Form adapters for composite controls |\n| [`@santi020k/lumen-elements`](./packages/elements) | Standards-based Web Components |\n| [`@santi020k/lumen-core`](./packages/core) | Shared tokens, metadata, class helpers, and behavior contracts |\n| [`@santi020k/lumen-tokens`](./packages/tokens) | Canonical platform-neutral design token source |\n| [`@santi020k/lumen-react-native`](./packages/react-native) | React Native foundations and native primitives |\n| [`LumenUI`](./packages/swift) | Swift Package for SwiftUI foundations and native primitives |\n| [`lumen-compose`](./packages/compose) | Android library for Jetpack Compose foundations and native primitives |\n| [`@santi020k/lumen-icons-brand`](./packages/icons-brand) | Optional namespaced brand icons for the shared `Icon` API |\n| [`@santi020k/lumen`](./packages/lumen) | Umbrella package, CLI, registry metadata, and public package map |\n| [`@santi020k/lumen-mcp`](./packages/mcp) | MCP server for component discovery and source retrieval |\n\n## AI and design workflows\n\nInstall the portable Lumen skill in Codex, Claude Code, Cursor, Windsurf, and other compatible\ncoding agents:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe skill teaches agents how to select, compose, theme, and verify Lumen primitives. Pair it with\n[`@santi020k/lumen-mcp`](./packages/mcp) when an agent needs to search the live catalog or retrieve\ncurrent source, props, tokens, and usage rules.\n\nAdditional machine-readable surfaces include:\n\n- [`llms.txt`](./llms.txt) for a concise project map.\n- [`docs/ai-usage.md`](./docs/ai-usage.md) for downstream generation examples.\n- [`docs/styling-contract.md`](./docs/styling-contract.md) for stable parts and component variables.\n- [`docs/import-and-icon-performance.md`](./docs/import-and-icon-performance.md) for repeatable import and icon evidence.\n- [`docs/graphics.md`](./docs/graphics.md) for portable decorative artwork across web and native platforms.\n- [`docs/consumer-regression-fixtures.md`](./docs/consumer-regression-fixtures.md) for production-shaped local coverage.\n- [`docs/project-adoption.md`](./docs/project-adoption.md) for the sibling-project audit and staged\n web and native migration strategy.\n- [`registry/lumen.registry.json`](./registry/lumen.registry.json) for recipes and installable file\n groups.\n- [`docs/figma.md`](./docs/figma.md) for Figma variables and Code Connect workflows.\n- [`docs/cross-platform.md`](./docs/cross-platform.md) for the native architecture, support tiers,\n and component-parity policy.\n\n## Contributing\n\nLumen is a pnpm workspace. Astro is the reference implementation; shared contracts belong in\n`packages/core`, while framework-specific behavior stays in its adapter package.\n\n```bash\npnpm install\npnpm run dev\npnpm run validate\n```\n\nRead [`CONTRIBUTING.md`](./CONTRIBUTING.md) before opening a pull request. User-visible package\nchanges require a changeset.\n\n## Community\n\nLumen is free and MIT licensed while the project focuses on adoption, public examples, and\nreal-world feedback.\n\n- Explore task-oriented [guides](https://lumen.santi020k.com/guides).\n- Share a public project in the [community showcase](https://lumen.santi020k.com/community).\n- Evaluate the system with the open [team adoption path](https://lumen.santi020k.com/teams).\n- Tell us what would improve your next build through the\n [adoption survey](https://github.com/santi020k/lumen/issues/new?template=adoption-feedback.yml).\n\nMaintainers can use the [exposure playbook](./docs/exposure-playbook.md) for publishing cadence,\ncampaign measurement, video scripts, and launch copy.\n\n## License\n\nLumen is available under the [MIT License](./LICENSE).\n"
|
|
32194
32940
|
},
|
|
32195
32941
|
"meta": {
|
|
32196
|
-
"catalogHash": "
|
|
32197
|
-
"componentCount":
|
|
32198
|
-
"nativeComponentCount":
|
|
32942
|
+
"catalogHash": "da62e47060d9710896cd6ca5bfd676b97ae72cea966071535e76f895480cf745",
|
|
32943
|
+
"componentCount": 156,
|
|
32944
|
+
"nativeComponentCount": 49,
|
|
32199
32945
|
"packages": [
|
|
32200
32946
|
"@santi020k/lumen-astro",
|
|
32201
32947
|
"@santi020k/lumen-react",
|
|
@@ -32207,12 +32953,12 @@
|
|
|
32207
32953
|
"com.santi020k:lumen-compose"
|
|
32208
32954
|
],
|
|
32209
32955
|
"packageVersions": {
|
|
32210
|
-
"@santi020k/lumen-astro": "1.
|
|
32211
|
-
"@santi020k/lumen-core": "1.
|
|
32212
|
-
"@santi020k/lumen-elements": "1.
|
|
32213
|
-
"@santi020k/lumen-mcp": "1.
|
|
32214
|
-
"@santi020k/lumen-react": "1.
|
|
32215
|
-
"@santi020k/lumen-react-native": "0.
|
|
32956
|
+
"@santi020k/lumen-astro": "1.4.0",
|
|
32957
|
+
"@santi020k/lumen-core": "1.4.0",
|
|
32958
|
+
"@santi020k/lumen-elements": "1.4.0",
|
|
32959
|
+
"@santi020k/lumen-mcp": "1.4.0",
|
|
32960
|
+
"@santi020k/lumen-react": "1.4.0",
|
|
32961
|
+
"@santi020k/lumen-react-native": "0.3.0",
|
|
32216
32962
|
"@santi020k/lumen-tokens": "0.1.0",
|
|
32217
32963
|
"LumenUI": "workspace",
|
|
32218
32964
|
"com.santi020k:lumen-compose": "workspace"
|
|
@@ -32220,7 +32966,7 @@
|
|
|
32220
32966
|
"registryName": "lumen",
|
|
32221
32967
|
"registryVersion": 1,
|
|
32222
32968
|
"schemaVersion": 4,
|
|
32223
|
-
"serverVersion": "1.
|
|
32969
|
+
"serverVersion": "1.4.0"
|
|
32224
32970
|
},
|
|
32225
32971
|
"nativeComponents": [
|
|
32226
32972
|
{
|
|
@@ -32248,7 +32994,7 @@
|
|
|
32248
32994
|
"example": "LumenTheme(darkTheme = false) {\n AppContent()\n}",
|
|
32249
32995
|
"exportName": "LumenTheme",
|
|
32250
32996
|
"importStatement": "import com.santi020k.lumen.LumenTheme",
|
|
32251
|
-
"install": "Include packages/compose from a
|
|
32997
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
32252
32998
|
"language": "kotlin",
|
|
32253
32999
|
"packageName": "com.santi020k:lumen-compose",
|
|
32254
33000
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -32298,7 +33044,7 @@
|
|
|
32298
33044
|
"example": "AppContent()\n .lumenTheme(.light)",
|
|
32299
33045
|
"exportName": ".lumenTheme",
|
|
32300
33046
|
"importStatement": "import LumenUI",
|
|
32301
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
33047
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
32302
33048
|
"language": "swift",
|
|
32303
33049
|
"packageName": "LumenUI",
|
|
32304
33050
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -32347,7 +33093,7 @@
|
|
|
32347
33093
|
"example": "LumenText(\n \"Welcome back\",\n variant = LumenTextVariant.Title,\n tone = LumenTextTone.Default\n)",
|
|
32348
33094
|
"exportName": "LumenText",
|
|
32349
33095
|
"importStatement": "import com.santi020k.lumen.LumenText",
|
|
32350
|
-
"install": "Include packages/compose from a
|
|
33096
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
32351
33097
|
"language": "kotlin",
|
|
32352
33098
|
"packageName": "com.santi020k:lumen-compose",
|
|
32353
33099
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -32421,7 +33167,7 @@
|
|
|
32421
33167
|
"example": "LumenText(\n \"Welcome back\",\n variant: .title,\n tone: .default\n)",
|
|
32422
33168
|
"exportName": "LumenText",
|
|
32423
33169
|
"importStatement": "import LumenUI",
|
|
32424
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
33170
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
32425
33171
|
"language": "swift",
|
|
32426
33172
|
"packageName": "LumenUI",
|
|
32427
33173
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -32470,7 +33216,7 @@
|
|
|
32470
33216
|
"example": "LumenSurface(\n tone = LumenSurfaceTone.Muted,\n padding = LumenSurfacePadding.Lg\n) {\n LumenText(\"Workspace\")\n}",
|
|
32471
33217
|
"exportName": "LumenSurface",
|
|
32472
33218
|
"importStatement": "import com.santi020k.lumen.LumenSurface",
|
|
32473
|
-
"install": "Include packages/compose from a
|
|
33219
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
32474
33220
|
"language": "kotlin",
|
|
32475
33221
|
"packageName": "com.santi020k:lumen-compose",
|
|
32476
33222
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -32544,7 +33290,7 @@
|
|
|
32544
33290
|
"example": "LumenSurface(tone: .muted, padding: .lg) {\n LumenText(\"Workspace\")\n}",
|
|
32545
33291
|
"exportName": "LumenSurface",
|
|
32546
33292
|
"importStatement": "import LumenUI",
|
|
32547
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
33293
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
32548
33294
|
"language": "swift",
|
|
32549
33295
|
"packageName": "LumenUI",
|
|
32550
33296
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -32593,11 +33339,11 @@
|
|
|
32593
33339
|
"example": "LumenIcon(\n imageVector = Icons.Default.Search,\n contentDescription = \"Search\",\n size = LumenIconSize.Md\n)",
|
|
32594
33340
|
"exportName": "LumenIcon",
|
|
32595
33341
|
"importStatement": "import com.santi020k.lumen.LumenIcon",
|
|
32596
|
-
"install": "Include packages/compose from a
|
|
33342
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
32597
33343
|
"language": "kotlin",
|
|
32598
33344
|
"packageName": "com.santi020k:lumen-compose",
|
|
32599
33345
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
32600
|
-
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/
|
|
33346
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/OverlayComponents.kt",
|
|
32601
33347
|
"symbol": "LumenIcon"
|
|
32602
33348
|
},
|
|
32603
33349
|
"react-native": {
|
|
@@ -32667,7 +33413,7 @@
|
|
|
32667
33413
|
"example": "LumenIcon(\n systemName: \"magnifyingglass\",\n size: .md,\n label: \"Search\"\n)",
|
|
32668
33414
|
"exportName": "LumenIcon",
|
|
32669
33415
|
"importStatement": "import LumenUI",
|
|
32670
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
33416
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
32671
33417
|
"language": "swift",
|
|
32672
33418
|
"packageName": "LumenUI",
|
|
32673
33419
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -32728,7 +33474,7 @@
|
|
|
32728
33474
|
"example": "LumenIconButton(\n imageVector = Icons.Default.Settings,\n contentDescription = \"Settings\",\n onClick = ::openSettings\n)",
|
|
32729
33475
|
"exportName": "LumenIconButton",
|
|
32730
33476
|
"importStatement": "import com.santi020k.lumen.LumenIconButton",
|
|
32731
|
-
"install": "Include packages/compose from a
|
|
33477
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
32732
33478
|
"language": "kotlin",
|
|
32733
33479
|
"packageName": "com.santi020k:lumen-compose",
|
|
32734
33480
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -32826,7 +33572,7 @@
|
|
|
32826
33572
|
"example": "LumenIconButton(\n systemName: \"gearshape\",\n label: \"Settings\",\n action: openSettings\n)",
|
|
32827
33573
|
"exportName": "LumenIconButton",
|
|
32828
33574
|
"importStatement": "import LumenUI",
|
|
32829
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
33575
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
32830
33576
|
"language": "swift",
|
|
32831
33577
|
"packageName": "LumenUI",
|
|
32832
33578
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -32887,7 +33633,7 @@
|
|
|
32887
33633
|
"example": "LumenButton(\n onClick = ::continueFlow,\n intent = LumenButtonIntent.Primary\n) {\n Text(\"Continue\")\n}",
|
|
32888
33634
|
"exportName": "LumenButton",
|
|
32889
33635
|
"importStatement": "import com.santi020k.lumen.LumenButton",
|
|
32890
|
-
"install": "Include packages/compose from a
|
|
33636
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
32891
33637
|
"language": "kotlin",
|
|
32892
33638
|
"packageName": "com.santi020k:lumen-compose",
|
|
32893
33639
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -32985,7 +33731,7 @@
|
|
|
32985
33731
|
"example": "LumenButton(\n \"Continue\",\n intent: .primary,\n action: continueFlow\n)",
|
|
32986
33732
|
"exportName": "LumenButton",
|
|
32987
33733
|
"importStatement": "import LumenUI",
|
|
32988
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
33734
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
32989
33735
|
"language": "swift",
|
|
32990
33736
|
"packageName": "LumenUI",
|
|
32991
33737
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -33046,7 +33792,7 @@
|
|
|
33046
33792
|
"example": "LumenTextField(\n value = email,\n onValueChange = { email = it },\n label = \"Email address\"\n)",
|
|
33047
33793
|
"exportName": "LumenTextField",
|
|
33048
33794
|
"importStatement": "import com.santi020k.lumen.LumenTextField",
|
|
33049
|
-
"install": "Include packages/compose from a
|
|
33795
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
33050
33796
|
"language": "kotlin",
|
|
33051
33797
|
"packageName": "com.santi020k:lumen-compose",
|
|
33052
33798
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -33144,7 +33890,7 @@
|
|
|
33144
33890
|
"example": "LumenTextField(\n \"Email address\",\n text: $email\n)",
|
|
33145
33891
|
"exportName": "LumenTextField",
|
|
33146
33892
|
"importStatement": "import LumenUI",
|
|
33147
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
33893
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
33148
33894
|
"language": "swift",
|
|
33149
33895
|
"packageName": "LumenUI",
|
|
33150
33896
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -33187,7 +33933,7 @@
|
|
|
33187
33933
|
"example": "LumenBadge(\n text = \"Active\",\n tone = LumenBadgeTone.Success\n)",
|
|
33188
33934
|
"exportName": "LumenBadge",
|
|
33189
33935
|
"importStatement": "import com.santi020k.lumen.LumenBadge",
|
|
33190
|
-
"install": "Include packages/compose from a
|
|
33936
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
33191
33937
|
"language": "kotlin",
|
|
33192
33938
|
"packageName": "com.santi020k:lumen-compose",
|
|
33193
33939
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -33249,7 +33995,7 @@
|
|
|
33249
33995
|
"example": "LumenBadge(\"Active\", tone: .success)",
|
|
33250
33996
|
"exportName": "LumenBadge",
|
|
33251
33997
|
"importStatement": "import LumenUI",
|
|
33252
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
33998
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
33253
33999
|
"language": "swift",
|
|
33254
34000
|
"packageName": "LumenUI",
|
|
33255
34001
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -33286,7 +34032,7 @@
|
|
|
33286
34032
|
"example": "LumenDivider()",
|
|
33287
34033
|
"exportName": "LumenDivider",
|
|
33288
34034
|
"importStatement": "import com.santi020k.lumen.LumenDivider",
|
|
33289
|
-
"install": "Include packages/compose from a
|
|
34035
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
33290
34036
|
"language": "kotlin",
|
|
33291
34037
|
"packageName": "com.santi020k:lumen-compose",
|
|
33292
34038
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -33336,7 +34082,7 @@
|
|
|
33336
34082
|
"example": "LumenDivider()",
|
|
33337
34083
|
"exportName": "LumenDivider",
|
|
33338
34084
|
"importStatement": "import LumenUI",
|
|
33339
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
34085
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
33340
34086
|
"language": "swift",
|
|
33341
34087
|
"packageName": "LumenUI",
|
|
33342
34088
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -33379,7 +34125,7 @@
|
|
|
33379
34125
|
"example": "LumenSpinner(label = \"Loading projects\")",
|
|
33380
34126
|
"exportName": "LumenSpinner",
|
|
33381
34127
|
"importStatement": "import com.santi020k.lumen.LumenSpinner",
|
|
33382
|
-
"install": "Include packages/compose from a
|
|
34128
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
33383
34129
|
"language": "kotlin",
|
|
33384
34130
|
"packageName": "com.santi020k:lumen-compose",
|
|
33385
34131
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -33441,7 +34187,7 @@
|
|
|
33441
34187
|
"example": "LumenSpinner(label: \"Loading projects\")",
|
|
33442
34188
|
"exportName": "LumenSpinner",
|
|
33443
34189
|
"importStatement": "import LumenUI",
|
|
33444
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
34190
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
33445
34191
|
"language": "swift",
|
|
33446
34192
|
"packageName": "LumenUI",
|
|
33447
34193
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -33490,7 +34236,7 @@
|
|
|
33490
34236
|
"example": "LumenCard(variant = LumenCardVariant.Muted) {\n LumenText(\"Team workspace\")\n}",
|
|
33491
34237
|
"exportName": "LumenCard",
|
|
33492
34238
|
"importStatement": "import com.santi020k.lumen.LumenCard",
|
|
33493
|
-
"install": "Include packages/compose from a
|
|
34239
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
33494
34240
|
"language": "kotlin",
|
|
33495
34241
|
"packageName": "com.santi020k:lumen-compose",
|
|
33496
34242
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -33564,7 +34310,7 @@
|
|
|
33564
34310
|
"example": "LumenCard(variant: .muted) {\n LumenText(\"Team workspace\")\n}",
|
|
33565
34311
|
"exportName": "LumenCard",
|
|
33566
34312
|
"importStatement": "import LumenUI",
|
|
33567
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
34313
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
33568
34314
|
"language": "swift",
|
|
33569
34315
|
"packageName": "LumenUI",
|
|
33570
34316
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -33613,11 +34359,11 @@
|
|
|
33613
34359
|
"example": "LumenAlert(variant = LumenAlertVariant.Success) {\n Text(\"Profile synced\")\n}",
|
|
33614
34360
|
"exportName": "LumenAlert",
|
|
33615
34361
|
"importStatement": "import com.santi020k.lumen.LumenAlert",
|
|
33616
|
-
"install": "Include packages/compose from a
|
|
34362
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
33617
34363
|
"language": "kotlin",
|
|
33618
34364
|
"packageName": "com.santi020k:lumen-compose",
|
|
33619
34365
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
33620
|
-
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/
|
|
34366
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/OverlayComponents.kt",
|
|
33621
34367
|
"symbol": "LumenAlert"
|
|
33622
34368
|
},
|
|
33623
34369
|
"react-native": {
|
|
@@ -33687,11 +34433,11 @@
|
|
|
33687
34433
|
"example": "LumenAlert(variant: .success) {\n Text(\"Profile synced\")\n}",
|
|
33688
34434
|
"exportName": "LumenAlert",
|
|
33689
34435
|
"importStatement": "import LumenUI",
|
|
33690
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
34436
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
33691
34437
|
"language": "swift",
|
|
33692
34438
|
"packageName": "LumenUI",
|
|
33693
34439
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
33694
|
-
"sourceFile": "packages/swift/Sources/LumenUI/
|
|
34440
|
+
"sourceFile": "packages/swift/Sources/LumenUI/OverlayComponents.swift",
|
|
33695
34441
|
"symbol": "LumenAlert"
|
|
33696
34442
|
}
|
|
33697
34443
|
},
|
|
@@ -33736,7 +34482,7 @@
|
|
|
33736
34482
|
"example": "LumenProgress(\n value = 72f,\n label = \"Profile completion\"\n)",
|
|
33737
34483
|
"exportName": "LumenProgress",
|
|
33738
34484
|
"importStatement": "import com.santi020k.lumen.LumenProgress",
|
|
33739
|
-
"install": "Include packages/compose from a
|
|
34485
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
33740
34486
|
"language": "kotlin",
|
|
33741
34487
|
"packageName": "com.santi020k:lumen-compose",
|
|
33742
34488
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -33810,7 +34556,7 @@
|
|
|
33810
34556
|
"example": "LumenProgress(\n value: 72,\n label: \"Profile completion\"\n)",
|
|
33811
34557
|
"exportName": "LumenProgress",
|
|
33812
34558
|
"importStatement": "import LumenUI",
|
|
33813
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
34559
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
33814
34560
|
"language": "swift",
|
|
33815
34561
|
"packageName": "LumenUI",
|
|
33816
34562
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -33859,7 +34605,7 @@
|
|
|
33859
34605
|
"example": "LumenSkeleton(\n height = 16.dp,\n label = \"Loading profile\"\n)",
|
|
33860
34606
|
"exportName": "LumenSkeleton",
|
|
33861
34607
|
"importStatement": "import com.santi020k.lumen.LumenSkeleton",
|
|
33862
|
-
"install": "Include packages/compose from a
|
|
34608
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
33863
34609
|
"language": "kotlin",
|
|
33864
34610
|
"packageName": "com.santi020k:lumen-compose",
|
|
33865
34611
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -33933,7 +34679,7 @@
|
|
|
33933
34679
|
"example": "LumenSkeleton(\n height: 16,\n label: \"Loading profile\"\n)",
|
|
33934
34680
|
"exportName": "LumenSkeleton",
|
|
33935
34681
|
"importStatement": "import LumenUI",
|
|
33936
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
34682
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
33937
34683
|
"language": "swift",
|
|
33938
34684
|
"packageName": "LumenUI",
|
|
33939
34685
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -33994,7 +34740,7 @@
|
|
|
33994
34740
|
"example": "LumenDisclosure(\n title = \"Implementation notes\",\n expanded = expanded,\n onExpandedChange = ::setExpanded\n) {\n LumenText(\"Native details\")\n}",
|
|
33995
34741
|
"exportName": "LumenDisclosure",
|
|
33996
34742
|
"importStatement": "import com.santi020k.lumen.LumenDisclosure",
|
|
33997
|
-
"install": "Include packages/compose from a
|
|
34743
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
33998
34744
|
"language": "kotlin",
|
|
33999
34745
|
"packageName": "com.santi020k:lumen-compose",
|
|
34000
34746
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -34092,7 +34838,7 @@
|
|
|
34092
34838
|
"example": "LumenDisclosure(\n \"Implementation notes\",\n isExpanded: $isExpanded\n) {\n LumenText(\"Native details\")\n}",
|
|
34093
34839
|
"exportName": "LumenDisclosure",
|
|
34094
34840
|
"importStatement": "import LumenUI",
|
|
34095
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
34841
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
34096
34842
|
"language": "swift",
|
|
34097
34843
|
"packageName": "LumenUI",
|
|
34098
34844
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -34141,7 +34887,7 @@
|
|
|
34141
34887
|
"example": "LumenAvatar(\n fallback = \"SM\",\n size = LumenAvatarSize.Lg,\n label = \"Santiago Molina\"\n)",
|
|
34142
34888
|
"exportName": "LumenAvatar",
|
|
34143
34889
|
"importStatement": "import com.santi020k.lumen.LumenAvatar",
|
|
34144
|
-
"install": "Include packages/compose from a
|
|
34890
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
34145
34891
|
"language": "kotlin",
|
|
34146
34892
|
"packageName": "com.santi020k:lumen-compose",
|
|
34147
34893
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -34215,7 +34961,7 @@
|
|
|
34215
34961
|
"example": "LumenAvatar(\n fallback: \"SM\",\n size: .lg,\n label: \"Santiago Molina\"\n)",
|
|
34216
34962
|
"exportName": "LumenAvatar",
|
|
34217
34963
|
"importStatement": "import LumenUI",
|
|
34218
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
34964
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
34219
34965
|
"language": "swift",
|
|
34220
34966
|
"packageName": "LumenUI",
|
|
34221
34967
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -34270,7 +35016,7 @@
|
|
|
34270
35016
|
"example": "LumenTextarea(\n value = notes,\n onValueChange = ::setNotes,\n label = \"Release notes\",\n description = \"Summarize the visible changes.\"\n)",
|
|
34271
35017
|
"exportName": "LumenTextarea",
|
|
34272
35018
|
"importStatement": "import com.santi020k.lumen.LumenTextarea",
|
|
34273
|
-
"install": "Include packages/compose from a
|
|
35019
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
34274
35020
|
"language": "kotlin",
|
|
34275
35021
|
"packageName": "com.santi020k:lumen-compose",
|
|
34276
35022
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -34356,7 +35102,7 @@
|
|
|
34356
35102
|
"example": "LumenTextarea(\n \"Release notes\",\n text: $notes,\n description: \"Summarize the visible changes.\"\n)",
|
|
34357
35103
|
"exportName": "LumenTextarea",
|
|
34358
35104
|
"importStatement": "import LumenUI",
|
|
34359
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
35105
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
34360
35106
|
"language": "swift",
|
|
34361
35107
|
"packageName": "LumenUI",
|
|
34362
35108
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -34411,7 +35157,7 @@
|
|
|
34411
35157
|
"example": "LumenFieldGroup(\n label = \"Notification channels\",\n description = \"Choose every channel the team should use.\",\n required = true\n) {\n NotificationControls()\n}",
|
|
34412
35158
|
"exportName": "LumenFieldGroup",
|
|
34413
35159
|
"importStatement": "import com.santi020k.lumen.LumenFieldGroup",
|
|
34414
|
-
"install": "Include packages/compose from a
|
|
35160
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
34415
35161
|
"language": "kotlin",
|
|
34416
35162
|
"packageName": "com.santi020k:lumen-compose",
|
|
34417
35163
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -34497,7 +35243,7 @@
|
|
|
34497
35243
|
"example": "LumenFieldGroup(\n \"Notification channels\",\n description: \"Choose every channel the team should use.\",\n required: true\n) {\n NotificationControls()\n}",
|
|
34498
35244
|
"exportName": "LumenFieldGroup",
|
|
34499
35245
|
"importStatement": "import LumenUI",
|
|
34500
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
35246
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
34501
35247
|
"language": "swift",
|
|
34502
35248
|
"packageName": "LumenUI",
|
|
34503
35249
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -34552,7 +35298,7 @@
|
|
|
34552
35298
|
"example": "LumenChip(\n label = \"Design\",\n selected = selected,\n onClick = { selected = !selected },\n onRemove = ::removeDesign\n)",
|
|
34553
35299
|
"exportName": "LumenChip",
|
|
34554
35300
|
"importStatement": "import com.santi020k.lumen.LumenChip",
|
|
34555
|
-
"install": "Include packages/compose from a
|
|
35301
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
34556
35302
|
"language": "kotlin",
|
|
34557
35303
|
"packageName": "com.santi020k:lumen-compose",
|
|
34558
35304
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -34638,7 +35384,7 @@
|
|
|
34638
35384
|
"example": "LumenChip(\n \"Design\",\n selected: selected,\n removeLabel: \"Remove Design\",\n onPress: { selected.toggle() },\n onRemove: removeDesign\n)",
|
|
34639
35385
|
"exportName": "LumenChip",
|
|
34640
35386
|
"importStatement": "import LumenUI",
|
|
34641
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
35387
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
34642
35388
|
"language": "swift",
|
|
34643
35389
|
"packageName": "LumenUI",
|
|
34644
35390
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -34675,7 +35421,7 @@
|
|
|
34675
35421
|
"example": "LumenButtonGroup {\n LumenButton(onClick = ::save) { Text(\"Save\") }\n LumenButton(onClick = ::cancel, intent = LumenButtonIntent.Secondary) { Text(\"Cancel\") }\n}",
|
|
34676
35422
|
"exportName": "LumenButtonGroup",
|
|
34677
35423
|
"importStatement": "import com.santi020k.lumen.LumenButtonGroup",
|
|
34678
|
-
"install": "Include packages/compose from a
|
|
35424
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
34679
35425
|
"language": "kotlin",
|
|
34680
35426
|
"packageName": "com.santi020k:lumen-compose",
|
|
34681
35427
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -34725,7 +35471,7 @@
|
|
|
34725
35471
|
"example": "LumenButtonGroup {\n LumenButton(\"Save\", action: save)\n LumenButton(\"Cancel\", intent: .secondary, action: cancel)\n}",
|
|
34726
35472
|
"exportName": "LumenButtonGroup",
|
|
34727
35473
|
"importStatement": "import LumenUI",
|
|
34728
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
35474
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
34729
35475
|
"language": "swift",
|
|
34730
35476
|
"packageName": "LumenUI",
|
|
34731
35477
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -34780,7 +35526,7 @@
|
|
|
34780
35526
|
"example": "LumenToast(\n title = \"Changes saved\",\n description = \"The workspace is up to date.\",\n variant = LumenBannerVariant.Success,\n onDismiss = ::dismissToast\n)",
|
|
34781
35527
|
"exportName": "LumenToast",
|
|
34782
35528
|
"importStatement": "import com.santi020k.lumen.LumenToast",
|
|
34783
|
-
"install": "Include packages/compose from a
|
|
35529
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
34784
35530
|
"language": "kotlin",
|
|
34785
35531
|
"packageName": "com.santi020k:lumen-compose",
|
|
34786
35532
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -34866,7 +35612,7 @@
|
|
|
34866
35612
|
"example": "LumenToast(\n \"Changes saved\",\n description: \"The workspace is up to date.\",\n variant: .success,\n onDismiss: dismissToast\n)",
|
|
34867
35613
|
"exportName": "LumenToast",
|
|
34868
35614
|
"importStatement": "import LumenUI",
|
|
34869
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
35615
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
34870
35616
|
"language": "swift",
|
|
34871
35617
|
"packageName": "LumenUI",
|
|
34872
35618
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -34921,7 +35667,7 @@
|
|
|
34921
35667
|
"example": "LumenToggle(\n label = \"Automatic updates\",\n checked = automaticUpdates,\n onCheckedChange = ::setAutomaticUpdates\n)",
|
|
34922
35668
|
"exportName": "LumenToggle",
|
|
34923
35669
|
"importStatement": "import com.santi020k.lumen.LumenToggle",
|
|
34924
|
-
"install": "Include packages/compose from a
|
|
35670
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
34925
35671
|
"language": "kotlin",
|
|
34926
35672
|
"packageName": "com.santi020k:lumen-compose",
|
|
34927
35673
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -35007,7 +35753,7 @@
|
|
|
35007
35753
|
"example": "LumenToggle(\"Automatic updates\", isOn: $automaticUpdates)",
|
|
35008
35754
|
"exportName": "LumenToggle",
|
|
35009
35755
|
"importStatement": "import LumenUI",
|
|
35010
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
35756
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
35011
35757
|
"language": "swift",
|
|
35012
35758
|
"packageName": "LumenUI",
|
|
35013
35759
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -35056,7 +35802,7 @@
|
|
|
35056
35802
|
"example": "LumenSettingsRow(\n title = \"Automatic updates\",\n description = \"Download stable updates automatically.\",\n control = {\n LumenToggle(\n label = \"Automatic updates\",\n checked = automaticUpdates,\n showLabel = false,\n onCheckedChange = ::setAutomaticUpdates\n )\n }\n)",
|
|
35057
35803
|
"exportName": "LumenSettingsRow",
|
|
35058
35804
|
"importStatement": "import com.santi020k.lumen.LumenSettingsRow",
|
|
35059
|
-
"install": "Include packages/compose from a
|
|
35805
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
35060
35806
|
"language": "kotlin",
|
|
35061
35807
|
"packageName": "com.santi020k:lumen-compose",
|
|
35062
35808
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -35130,7 +35876,7 @@
|
|
|
35130
35876
|
"example": "LumenSettingsRow(\n \"Automatic updates\",\n description: \"Download stable updates automatically.\",\n systemName: \"arrow.triangle.2.circlepath\"\n) {\n LumenToggle(\"Automatic updates\", isOn: $automaticUpdates)\n .labelsHidden()\n}",
|
|
35131
35877
|
"exportName": "LumenSettingsRow",
|
|
35132
35878
|
"importStatement": "import LumenUI",
|
|
35133
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
35879
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
35134
35880
|
"language": "swift",
|
|
35135
35881
|
"packageName": "LumenUI",
|
|
35136
35882
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -35179,7 +35925,7 @@
|
|
|
35179
35925
|
"example": "LumenPicker(\n label = \"Profile\",\n value = profile,\n options = profileOptions,\n onValueChange = ::setProfile\n)",
|
|
35180
35926
|
"exportName": "LumenPicker",
|
|
35181
35927
|
"importStatement": "import com.santi020k.lumen.LumenPicker",
|
|
35182
|
-
"install": "Include packages/compose from a
|
|
35928
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
35183
35929
|
"language": "kotlin",
|
|
35184
35930
|
"packageName": "com.santi020k:lumen-compose",
|
|
35185
35931
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -35216,7 +35962,7 @@
|
|
|
35216
35962
|
"example": "LumenPicker(\"Profile\", selection: $profile, style: .segmented) {\n Text(\"Quiet\").tag(Profile.quiet)\n Text(\"Balanced\").tag(Profile.balanced)\n}",
|
|
35217
35963
|
"exportName": "LumenPicker",
|
|
35218
35964
|
"importStatement": "import LumenUI",
|
|
35219
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
35965
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
35220
35966
|
"language": "swift",
|
|
35221
35967
|
"packageName": "LumenUI",
|
|
35222
35968
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -35265,7 +36011,7 @@
|
|
|
35265
36011
|
"example": "LumenSlider(\n label = \"Minimum speed\",\n value = minimumSpeed,\n onValueChange = ::setMinimumSpeed,\n valueRange = 1_000f..5_000f,\n steps = 39,\n valueLabel = \"${minimumSpeed.toInt()} RPM\"\n)",
|
|
35266
36012
|
"exportName": "LumenSlider",
|
|
35267
36013
|
"importStatement": "import com.santi020k.lumen.LumenSlider",
|
|
35268
|
-
"install": "Include packages/compose from a
|
|
36014
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
35269
36015
|
"language": "kotlin",
|
|
35270
36016
|
"packageName": "com.santi020k:lumen-compose",
|
|
35271
36017
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -35302,7 +36048,7 @@
|
|
|
35302
36048
|
"example": "LumenSlider(\n \"Minimum speed\",\n value: $minimumSpeed,\n in: 1_000...5_000,\n step: 100,\n valueLabel: \"\\(Int(minimumSpeed)) RPM\"\n)",
|
|
35303
36049
|
"exportName": "LumenSlider",
|
|
35304
36050
|
"importStatement": "import LumenUI",
|
|
35305
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
36051
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
35306
36052
|
"language": "swift",
|
|
35307
36053
|
"packageName": "LumenUI",
|
|
35308
36054
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -35357,7 +36103,7 @@
|
|
|
35357
36103
|
"example": "LumenSearchField(\n value = query,\n onValueChange = ::setQuery,\n prompt = \"Search workspaces\"\n)",
|
|
35358
36104
|
"exportName": "LumenSearchField",
|
|
35359
36105
|
"importStatement": "import com.santi020k.lumen.LumenSearchField",
|
|
35360
|
-
"install": "Include packages/compose from a
|
|
36106
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
35361
36107
|
"language": "kotlin",
|
|
35362
36108
|
"packageName": "com.santi020k:lumen-compose",
|
|
35363
36109
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -35443,7 +36189,7 @@
|
|
|
35443
36189
|
"example": "LumenSearchField(\"Search workspaces\", text: $query)",
|
|
35444
36190
|
"exportName": "LumenSearchField",
|
|
35445
36191
|
"importStatement": "import LumenUI",
|
|
35446
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
36192
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
35447
36193
|
"language": "swift",
|
|
35448
36194
|
"packageName": "LumenUI",
|
|
35449
36195
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -35498,7 +36244,7 @@
|
|
|
35498
36244
|
"example": "LumenCheckbox(\n label = \"Share analytics\",\n checked = sharesAnalytics,\n onCheckedChange = ::setSharesAnalytics\n)",
|
|
35499
36245
|
"exportName": "LumenCheckbox",
|
|
35500
36246
|
"importStatement": "import com.santi020k.lumen.LumenCheckbox",
|
|
35501
|
-
"install": "Include packages/compose from a
|
|
36247
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
35502
36248
|
"language": "kotlin",
|
|
35503
36249
|
"packageName": "com.santi020k:lumen-compose",
|
|
35504
36250
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -35584,7 +36330,7 @@
|
|
|
35584
36330
|
"example": "LumenCheckbox(\n \"Share analytics\",\n isChecked: $sharesAnalytics\n)",
|
|
35585
36331
|
"exportName": "LumenCheckbox",
|
|
35586
36332
|
"importStatement": "import LumenUI",
|
|
35587
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
36333
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
35588
36334
|
"language": "swift",
|
|
35589
36335
|
"packageName": "LumenUI",
|
|
35590
36336
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -35633,7 +36379,7 @@
|
|
|
35633
36379
|
"example": "LumenRadioGroup(\n label = \"Density\",\n options = densityOptions,\n value = density,\n onValueChange = ::setDensity\n)",
|
|
35634
36380
|
"exportName": "LumenRadioGroup",
|
|
35635
36381
|
"importStatement": "import com.santi020k.lumen.LumenRadioGroup",
|
|
35636
|
-
"install": "Include packages/compose from a
|
|
36382
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
35637
36383
|
"language": "kotlin",
|
|
35638
36384
|
"packageName": "com.santi020k:lumen-compose",
|
|
35639
36385
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -35707,7 +36453,7 @@
|
|
|
35707
36453
|
"example": "LumenRadioGroup(\n \"Density\",\n selection: $density,\n options: densityOptions\n)",
|
|
35708
36454
|
"exportName": "LumenRadioGroup",
|
|
35709
36455
|
"importStatement": "import LumenUI",
|
|
35710
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
36456
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
35711
36457
|
"language": "swift",
|
|
35712
36458
|
"packageName": "LumenUI",
|
|
35713
36459
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -35762,7 +36508,7 @@
|
|
|
35762
36508
|
"example": "LumenSegmentedControl(\n label = \"View\",\n options = viewOptions,\n value = view,\n onValueChange = ::setView\n)",
|
|
35763
36509
|
"exportName": "LumenSegmentedControl",
|
|
35764
36510
|
"importStatement": "import com.santi020k.lumen.LumenSegmentedControl",
|
|
35765
|
-
"install": "Include packages/compose from a
|
|
36511
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
35766
36512
|
"language": "kotlin",
|
|
35767
36513
|
"packageName": "com.santi020k:lumen-compose",
|
|
35768
36514
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -35848,7 +36594,7 @@
|
|
|
35848
36594
|
"example": "LumenSegmentedControl(\n \"View\",\n selection: $view,\n options: viewOptions\n)",
|
|
35849
36595
|
"exportName": "LumenSegmentedControl",
|
|
35850
36596
|
"importStatement": "import LumenUI",
|
|
35851
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
36597
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
35852
36598
|
"language": "swift",
|
|
35853
36599
|
"packageName": "LumenUI",
|
|
35854
36600
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -35860,6 +36606,129 @@
|
|
|
35860
36606
|
"summary": "Choose one value from a compact row of short peer options.",
|
|
35861
36607
|
"tier": "shared"
|
|
35862
36608
|
},
|
|
36609
|
+
{
|
|
36610
|
+
"accessibility": "The group has a readable navigation label and every destination exposes selected and disabled state through native tab semantics.",
|
|
36611
|
+
"category": "Navigation",
|
|
36612
|
+
"contract": "Controlled selection from a small set of peer app destinations",
|
|
36613
|
+
"guidance": "Use for a small set of peer app destinations. Keep hierarchical navigation, history, deep links, restoration, and screen rendering in the application router or native navigation stack.",
|
|
36614
|
+
"id": "navigation-bar",
|
|
36615
|
+
"implementations": {
|
|
36616
|
+
"compose": {
|
|
36617
|
+
"api": [
|
|
36618
|
+
{
|
|
36619
|
+
"defaultValue": "Required",
|
|
36620
|
+
"description": "Provides destination values, short labels, native icons, and disabled state.",
|
|
36621
|
+
"name": "items",
|
|
36622
|
+
"values": "List<LumenNavigationItem<Value>>"
|
|
36623
|
+
},
|
|
36624
|
+
{
|
|
36625
|
+
"defaultValue": "Required",
|
|
36626
|
+
"description": "Stores the active destination.",
|
|
36627
|
+
"name": "selectedValue",
|
|
36628
|
+
"values": "Value"
|
|
36629
|
+
},
|
|
36630
|
+
{
|
|
36631
|
+
"defaultValue": "Required",
|
|
36632
|
+
"description": "Requests a destination change without owning navigation history.",
|
|
36633
|
+
"name": "onValueChange",
|
|
36634
|
+
"values": "(Value) -> Unit"
|
|
36635
|
+
},
|
|
36636
|
+
{
|
|
36637
|
+
"defaultValue": "Primary navigation",
|
|
36638
|
+
"description": "Names the destination group for assistive technology.",
|
|
36639
|
+
"name": "accessibilityLabel",
|
|
36640
|
+
"values": "String"
|
|
36641
|
+
}
|
|
36642
|
+
],
|
|
36643
|
+
"example": "LumenNavigationBar(\n items = destinations,\n selectedValue = destination,\n onValueChange = ::setDestination\n)",
|
|
36644
|
+
"exportName": "LumenNavigationBar",
|
|
36645
|
+
"importStatement": "import com.santi020k.lumen.LumenNavigationBar",
|
|
36646
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
36647
|
+
"language": "kotlin",
|
|
36648
|
+
"packageName": "com.santi020k:lumen-compose",
|
|
36649
|
+
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
36650
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/PlatformComponents.kt",
|
|
36651
|
+
"symbol": "LumenNavigationBar"
|
|
36652
|
+
},
|
|
36653
|
+
"react-native": {
|
|
36654
|
+
"api": [
|
|
36655
|
+
{
|
|
36656
|
+
"defaultValue": "Required",
|
|
36657
|
+
"description": "Provides destination values, short labels, native icons, and disabled state.",
|
|
36658
|
+
"name": "items",
|
|
36659
|
+
"values": "readonly LumenNavigationItem[]"
|
|
36660
|
+
},
|
|
36661
|
+
{
|
|
36662
|
+
"defaultValue": "Required",
|
|
36663
|
+
"description": "Stores the active destination.",
|
|
36664
|
+
"name": "value",
|
|
36665
|
+
"values": "string"
|
|
36666
|
+
},
|
|
36667
|
+
{
|
|
36668
|
+
"defaultValue": "Required",
|
|
36669
|
+
"description": "Requests a destination change without owning navigation history.",
|
|
36670
|
+
"name": "onValueChange",
|
|
36671
|
+
"values": "(string) => void"
|
|
36672
|
+
},
|
|
36673
|
+
{
|
|
36674
|
+
"defaultValue": "Primary navigation",
|
|
36675
|
+
"description": "Names the destination group for assistive technology.",
|
|
36676
|
+
"name": "accessibilityLabel",
|
|
36677
|
+
"values": "String"
|
|
36678
|
+
}
|
|
36679
|
+
],
|
|
36680
|
+
"example": "<LumenNavigationBar\n items={destinations}\n value={destination}\n onValueChange={setDestination}\n/>",
|
|
36681
|
+
"exportName": "LumenNavigationBar",
|
|
36682
|
+
"importStatement": "import { LumenNavigationBar } from '@santi020k/lumen-react-native'",
|
|
36683
|
+
"install": "pnpm add @santi020k/lumen-react-native",
|
|
36684
|
+
"language": "tsx",
|
|
36685
|
+
"packageName": "@santi020k/lumen-react-native",
|
|
36686
|
+
"setup": "Mount one LumenProvider near the application root. Native adapters do not load CSS or the Astro runtime.",
|
|
36687
|
+
"sourceFile": "packages/react-native/src/index.ts",
|
|
36688
|
+
"symbol": "LumenNavigationBar"
|
|
36689
|
+
},
|
|
36690
|
+
"swiftui": {
|
|
36691
|
+
"api": [
|
|
36692
|
+
{
|
|
36693
|
+
"defaultValue": "Required",
|
|
36694
|
+
"description": "Provides destination values, short labels, native icons, and disabled state.",
|
|
36695
|
+
"name": "items",
|
|
36696
|
+
"values": "[LumenNavigationItem<Selection>]"
|
|
36697
|
+
},
|
|
36698
|
+
{
|
|
36699
|
+
"defaultValue": "Required",
|
|
36700
|
+
"description": "Stores the active destination.",
|
|
36701
|
+
"name": "selection",
|
|
36702
|
+
"values": "Binding<Selection>"
|
|
36703
|
+
},
|
|
36704
|
+
{
|
|
36705
|
+
"defaultValue": "Required",
|
|
36706
|
+
"description": "Requests a destination change without owning navigation history.",
|
|
36707
|
+
"name": "onValueChange",
|
|
36708
|
+
"values": "Binding setter"
|
|
36709
|
+
},
|
|
36710
|
+
{
|
|
36711
|
+
"defaultValue": "Primary navigation",
|
|
36712
|
+
"description": "Names the destination group for assistive technology.",
|
|
36713
|
+
"name": "label",
|
|
36714
|
+
"values": "String"
|
|
36715
|
+
}
|
|
36716
|
+
],
|
|
36717
|
+
"example": "LumenNavigationBar(\n selection: $destination,\n items: destinations\n)",
|
|
36718
|
+
"exportName": "LumenNavigationBar",
|
|
36719
|
+
"importStatement": "import LumenUI",
|
|
36720
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
36721
|
+
"language": "swift",
|
|
36722
|
+
"packageName": "LumenUI",
|
|
36723
|
+
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
36724
|
+
"sourceFile": "packages/swift/Sources/LumenUI/NavigationComponents.swift",
|
|
36725
|
+
"symbol": "LumenNavigationBar"
|
|
36726
|
+
}
|
|
36727
|
+
},
|
|
36728
|
+
"name": "Navigation bar",
|
|
36729
|
+
"summary": "Select among peer application destinations with native visuals and controlled state.",
|
|
36730
|
+
"tier": "shared"
|
|
36731
|
+
},
|
|
35863
36732
|
{
|
|
35864
36733
|
"accessibility": "The title, description, optional graphic, and recovery actions remain in a readable native order.",
|
|
35865
36734
|
"category": "Feedback",
|
|
@@ -35897,7 +36766,7 @@
|
|
|
35897
36766
|
"example": "LumenEmptyState(\n title = \"No saved workspaces\",\n description = \"Create a workspace to get started.\",\n actions = { LumenButton(onClick = ::createWorkspace) { Text(\"Create\") } }\n)",
|
|
35898
36767
|
"exportName": "LumenEmptyState",
|
|
35899
36768
|
"importStatement": "import com.santi020k.lumen.LumenEmptyState",
|
|
35900
|
-
"install": "Include packages/compose from a
|
|
36769
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
35901
36770
|
"language": "kotlin",
|
|
35902
36771
|
"packageName": "com.santi020k:lumen-compose",
|
|
35903
36772
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -35971,7 +36840,7 @@
|
|
|
35971
36840
|
"example": "LumenEmptyState(\n \"No saved workspaces\",\n systemName: \"rectangle.stack\",\n description: \"Create a workspace to get started.\"\n) {\n LumenButton(\"Create Workspace\", action: createWorkspace)\n}",
|
|
35972
36841
|
"exportName": "LumenEmptyState",
|
|
35973
36842
|
"importStatement": "import LumenUI",
|
|
35974
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
36843
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
35975
36844
|
"language": "swift",
|
|
35976
36845
|
"packageName": "LumenUI",
|
|
35977
36846
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -36014,7 +36883,7 @@
|
|
|
36014
36883
|
"example": "LumenListRow(\n leading = { LumenAvatar(fallback = \"SM\") },\n trailing = { LumenBadge(\"Admin\") }\n) {\n LumenText(\"Santiago\", variant = LumenTextVariant.Label)\n}",
|
|
36015
36884
|
"exportName": "LumenListRow",
|
|
36016
36885
|
"importStatement": "import com.santi020k.lumen.LumenListRow",
|
|
36017
|
-
"install": "Include packages/compose from a
|
|
36886
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
36018
36887
|
"language": "kotlin",
|
|
36019
36888
|
"packageName": "com.santi020k:lumen-compose",
|
|
36020
36889
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -36076,7 +36945,7 @@
|
|
|
36076
36945
|
"example": "LumenListRow {\n LumenAvatar(fallback: \"SM\")\n} content: {\n LumenText(\"Santiago\", variant: .label)\n} trailing: {\n LumenBadge(\"Admin\")\n}",
|
|
36077
36946
|
"exportName": "LumenListRow",
|
|
36078
36947
|
"importStatement": "import LumenUI",
|
|
36079
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
36948
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
36080
36949
|
"language": "swift",
|
|
36081
36950
|
"packageName": "LumenUI",
|
|
36082
36951
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -36131,7 +37000,7 @@
|
|
|
36131
37000
|
"example": "LumenBanner(\n title = \"Workspace assigned automatically\",\n description = \"Moved the app to Documentation.\",\n variant = LumenBannerVariant.Accent,\n onDismiss = ::dismissAssignment\n)",
|
|
36132
37001
|
"exportName": "LumenBanner",
|
|
36133
37002
|
"importStatement": "import com.santi020k.lumen.LumenBanner",
|
|
36134
|
-
"install": "Include packages/compose from a
|
|
37003
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
36135
37004
|
"language": "kotlin",
|
|
36136
37005
|
"packageName": "com.santi020k:lumen-compose",
|
|
36137
37006
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -36217,7 +37086,7 @@
|
|
|
36217
37086
|
"example": "LumenBanner(\n \"Workspace assigned automatically\",\n description: \"Moved Safari to Documentation.\",\n variant: .accent,\n onDismiss: dismissAssignment\n) {\n LumenButton(\"Undo\", intent: .quiet, size: .sm, action: undo)\n}",
|
|
36218
37087
|
"exportName": "LumenBanner",
|
|
36219
37088
|
"importStatement": "import LumenUI",
|
|
36220
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
37089
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
36221
37090
|
"language": "swift",
|
|
36222
37091
|
"packageName": "LumenUI",
|
|
36223
37092
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -36272,7 +37141,7 @@
|
|
|
36272
37141
|
"example": "LumenStat(\n label = \"Open windows\",\n value = \"12\",\n detail = \"Across 3 workspaces\",\n tone = LumenMetricTone.Accent\n)",
|
|
36273
37142
|
"exportName": "LumenStat",
|
|
36274
37143
|
"importStatement": "import com.santi020k.lumen.LumenStat",
|
|
36275
|
-
"install": "Include packages/compose from a
|
|
37144
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
36276
37145
|
"language": "kotlin",
|
|
36277
37146
|
"packageName": "com.santi020k:lumen-compose",
|
|
36278
37147
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -36358,7 +37227,7 @@
|
|
|
36358
37227
|
"example": "LumenStat(\n \"Open windows\",\n value: \"12\",\n detail: \"Across 3 workspaces\",\n systemName: \"macwindow\",\n tone: .accent\n)",
|
|
36359
37228
|
"exportName": "LumenStat",
|
|
36360
37229
|
"importStatement": "import LumenUI",
|
|
36361
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
37230
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
36362
37231
|
"language": "swift",
|
|
36363
37232
|
"packageName": "LumenUI",
|
|
36364
37233
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -36413,7 +37282,7 @@
|
|
|
36413
37282
|
"example": "LumenGauge(\n label = \"Thermal pressure\",\n value = 48f,\n valueLabel = \"Fair\",\n tone = LumenMetricTone.Warning\n)",
|
|
36414
37283
|
"exportName": "LumenGauge",
|
|
36415
37284
|
"importStatement": "import com.santi020k.lumen.LumenGauge",
|
|
36416
|
-
"install": "Include packages/compose from a
|
|
37285
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
36417
37286
|
"language": "kotlin",
|
|
36418
37287
|
"packageName": "com.santi020k:lumen-compose",
|
|
36419
37288
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -36456,7 +37325,7 @@
|
|
|
36456
37325
|
"example": "LumenGauge(\n \"Thermal pressure\",\n value: 48,\n valueLabel: \"Fair\",\n systemName: \"thermometer.medium\",\n tone: .warning\n)",
|
|
36457
37326
|
"exportName": "LumenGauge",
|
|
36458
37327
|
"importStatement": "import LumenUI",
|
|
36459
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
37328
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
36460
37329
|
"language": "swift",
|
|
36461
37330
|
"packageName": "LumenUI",
|
|
36462
37331
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -36505,7 +37374,7 @@
|
|
|
36505
37374
|
"example": "LumenSectionHeader(\n title = \"Workspaces\",\n subtitle = \"Recently used\",\n count = \"4\"\n)",
|
|
36506
37375
|
"exportName": "LumenSectionHeader",
|
|
36507
37376
|
"importStatement": "import com.santi020k.lumen.LumenSectionHeader",
|
|
36508
|
-
"install": "Include packages/compose from a
|
|
37377
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
36509
37378
|
"language": "kotlin",
|
|
36510
37379
|
"packageName": "com.santi020k:lumen-compose",
|
|
36511
37380
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -36579,7 +37448,7 @@
|
|
|
36579
37448
|
"example": "LumenSectionHeader(\n \"Workspaces\",\n subtitle: \"Recently used\",\n count: \"4\"\n) {\n LumenButton(\"Add\", intent: .quiet, size: .sm, action: addWorkspace)\n}",
|
|
36580
37449
|
"exportName": "LumenSectionHeader",
|
|
36581
37450
|
"importStatement": "import LumenUI",
|
|
36582
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
37451
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
36583
37452
|
"language": "swift",
|
|
36584
37453
|
"packageName": "LumenUI",
|
|
36585
37454
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -36622,7 +37491,7 @@
|
|
|
36622
37491
|
"example": "LumenStatusBar(\n message = \"All changes saved\",\n tone = LumenMetricTone.Success\n)",
|
|
36623
37492
|
"exportName": "LumenStatusBar",
|
|
36624
37493
|
"importStatement": "import com.santi020k.lumen.LumenStatusBar",
|
|
36625
|
-
"install": "Include packages/compose from a
|
|
37494
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
36626
37495
|
"language": "kotlin",
|
|
36627
37496
|
"packageName": "com.santi020k:lumen-compose",
|
|
36628
37497
|
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
@@ -36684,7 +37553,7 @@
|
|
|
36684
37553
|
"example": "LumenStatusBar(\"All changes saved\", tone: .success) {\n Text(\"Just now\")\n}",
|
|
36685
37554
|
"exportName": "LumenStatusBar",
|
|
36686
37555
|
"importStatement": "import LumenUI",
|
|
36687
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
37556
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
36688
37557
|
"language": "swift",
|
|
36689
37558
|
"packageName": "LumenUI",
|
|
36690
37559
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -36696,6 +37565,176 @@
|
|
|
36696
37565
|
"summary": "Present compact textual application status with optional trailing content.",
|
|
36697
37566
|
"tier": "shared"
|
|
36698
37567
|
},
|
|
37568
|
+
{
|
|
37569
|
+
"accessibility": "The native refresh gesture remains owned by the scroll container. Supply an accessibility label that describes the refreshed content.",
|
|
37570
|
+
"category": "Feedback",
|
|
37571
|
+
"contract": "Native pull-to-refresh state with semantic indicator colors",
|
|
37572
|
+
"guidance": "Attach to a React Native ScrollView or compatible list. Keep refresh state and completion in application code.",
|
|
37573
|
+
"id": "refresh-control",
|
|
37574
|
+
"implementations": {
|
|
37575
|
+
"react-native": {
|
|
37576
|
+
"api": [
|
|
37577
|
+
{
|
|
37578
|
+
"defaultValue": "Required",
|
|
37579
|
+
"description": "Reports whether the native indicator is active.",
|
|
37580
|
+
"name": "refreshing",
|
|
37581
|
+
"values": "boolean"
|
|
37582
|
+
},
|
|
37583
|
+
{
|
|
37584
|
+
"defaultValue": "Required",
|
|
37585
|
+
"description": "Starts the application refresh operation.",
|
|
37586
|
+
"name": "onRefresh",
|
|
37587
|
+
"values": "() => void"
|
|
37588
|
+
},
|
|
37589
|
+
{
|
|
37590
|
+
"defaultValue": "brand",
|
|
37591
|
+
"description": "Selects the semantic native indicator color.",
|
|
37592
|
+
"name": "indicatorTone",
|
|
37593
|
+
"values": "brand · accent · neutral"
|
|
37594
|
+
},
|
|
37595
|
+
{
|
|
37596
|
+
"defaultValue": "Application-provided",
|
|
37597
|
+
"description": "Names the content refreshed by the gesture.",
|
|
37598
|
+
"name": "accessibilityLabel",
|
|
37599
|
+
"values": "string"
|
|
37600
|
+
}
|
|
37601
|
+
],
|
|
37602
|
+
"example": "<ScrollView\n refreshControl={(\n <LumenRefreshControl\n accessibilityLabel=\"Refresh projects\"\n refreshing={refreshing}\n onRefresh={refreshProjects}\n />\n )}\n>\n {content}\n</ScrollView>",
|
|
37603
|
+
"exportName": "LumenRefreshControl",
|
|
37604
|
+
"importStatement": "import { LumenRefreshControl } from '@santi020k/lumen-react-native'",
|
|
37605
|
+
"install": "pnpm add @santi020k/lumen-react-native",
|
|
37606
|
+
"language": "tsx",
|
|
37607
|
+
"packageName": "@santi020k/lumen-react-native",
|
|
37608
|
+
"setup": "Mount one LumenProvider near the application root. Native adapters do not load CSS or the Astro runtime.",
|
|
37609
|
+
"sourceFile": "packages/react-native/src/index.ts",
|
|
37610
|
+
"symbol": "LumenRefreshControl"
|
|
37611
|
+
}
|
|
37612
|
+
},
|
|
37613
|
+
"name": "Refresh control",
|
|
37614
|
+
"summary": "Apply Lumen semantic colors to React Native pull-to-refresh behavior.",
|
|
37615
|
+
"supportedPlatforms": [
|
|
37616
|
+
"iOS",
|
|
37617
|
+
"Android"
|
|
37618
|
+
],
|
|
37619
|
+
"tier": "platform-specific"
|
|
37620
|
+
},
|
|
37621
|
+
{
|
|
37622
|
+
"accessibility": "The native picker retains its label and adjustable behavior. Supporting or validation text remains visible in the same contained group.",
|
|
37623
|
+
"category": "Forms",
|
|
37624
|
+
"contract": "Native date and time selection with bounds, supporting text, and validation messaging",
|
|
37625
|
+
"guidance": "Use for Apple date or time input that needs Lumen supporting and validation context. Keep calendar and locale behavior native.",
|
|
37626
|
+
"id": "date-field",
|
|
37627
|
+
"implementations": {
|
|
37628
|
+
"swiftui": {
|
|
37629
|
+
"api": [
|
|
37630
|
+
{
|
|
37631
|
+
"defaultValue": "Required",
|
|
37632
|
+
"description": "Labels the native date picker.",
|
|
37633
|
+
"name": "title",
|
|
37634
|
+
"values": "LocalizedStringKey"
|
|
37635
|
+
},
|
|
37636
|
+
{
|
|
37637
|
+
"defaultValue": "Required",
|
|
37638
|
+
"description": "Stores the selected native date value.",
|
|
37639
|
+
"name": "selection",
|
|
37640
|
+
"values": "Binding<Date>"
|
|
37641
|
+
},
|
|
37642
|
+
{
|
|
37643
|
+
"defaultValue": "date",
|
|
37644
|
+
"description": "Chooses the visible date and time parts.",
|
|
37645
|
+
"name": "components",
|
|
37646
|
+
"values": "date · dateAndTime · time"
|
|
37647
|
+
},
|
|
37648
|
+
{
|
|
37649
|
+
"defaultValue": "unbounded",
|
|
37650
|
+
"description": "Constrains selection using native picker bounds.",
|
|
37651
|
+
"name": "bounds",
|
|
37652
|
+
"values": "unbounded · closed · from · through"
|
|
37653
|
+
},
|
|
37654
|
+
{
|
|
37655
|
+
"defaultValue": "nil",
|
|
37656
|
+
"description": "Shows supporting text or a semantic danger validation message.",
|
|
37657
|
+
"name": "description / errorMessage",
|
|
37658
|
+
"values": "LocalizedStringKey?"
|
|
37659
|
+
}
|
|
37660
|
+
],
|
|
37661
|
+
"example": "LumenDateField(\n \"Release date\",\n selection: $releaseDate,\n components: .dateAndTime,\n bounds: .from(.now),\n description: \"Choose when this version becomes available.\"\n)",
|
|
37662
|
+
"exportName": "LumenDateField",
|
|
37663
|
+
"importStatement": "import LumenUI",
|
|
37664
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
37665
|
+
"language": "swift",
|
|
37666
|
+
"packageName": "LumenUI",
|
|
37667
|
+
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
37668
|
+
"sourceFile": "packages/swift/Sources/LumenUI/AppleComponents.swift",
|
|
37669
|
+
"symbol": "LumenDateField"
|
|
37670
|
+
}
|
|
37671
|
+
},
|
|
37672
|
+
"name": "Date field",
|
|
37673
|
+
"summary": "Select an Apple-native date or time with bounds and validation context.",
|
|
37674
|
+
"supportedPlatforms": [
|
|
37675
|
+
"iOS",
|
|
37676
|
+
"macOS"
|
|
37677
|
+
],
|
|
37678
|
+
"tier": "platform-specific"
|
|
37679
|
+
},
|
|
37680
|
+
{
|
|
37681
|
+
"accessibility": "The required content description names the icon-only action while the control preserves native Material button semantics.",
|
|
37682
|
+
"category": "Actions",
|
|
37683
|
+
"contract": "Material-native floating action with semantic intent and small or regular sizing",
|
|
37684
|
+
"guidance": "Use for one prominent Material screen action. Keep navigation bars and scaffold placement in application code.",
|
|
37685
|
+
"id": "floating-action-button",
|
|
37686
|
+
"implementations": {
|
|
37687
|
+
"compose": {
|
|
37688
|
+
"api": [
|
|
37689
|
+
{
|
|
37690
|
+
"defaultValue": "Required",
|
|
37691
|
+
"description": "Renders the native action glyph.",
|
|
37692
|
+
"name": "imageVector",
|
|
37693
|
+
"values": "ImageVector"
|
|
37694
|
+
},
|
|
37695
|
+
{
|
|
37696
|
+
"defaultValue": "Required",
|
|
37697
|
+
"description": "Names the icon-only action.",
|
|
37698
|
+
"name": "contentDescription",
|
|
37699
|
+
"values": "String"
|
|
37700
|
+
},
|
|
37701
|
+
{
|
|
37702
|
+
"defaultValue": "Required",
|
|
37703
|
+
"description": "Runs the primary screen action.",
|
|
37704
|
+
"name": "onClick",
|
|
37705
|
+
"values": "() -> Unit"
|
|
37706
|
+
},
|
|
37707
|
+
{
|
|
37708
|
+
"defaultValue": "brand",
|
|
37709
|
+
"description": "Selects a semantic action palette.",
|
|
37710
|
+
"name": "intent",
|
|
37711
|
+
"values": "brand · accent · danger"
|
|
37712
|
+
},
|
|
37713
|
+
{
|
|
37714
|
+
"defaultValue": "regular",
|
|
37715
|
+
"description": "Selects Material-native FAB geometry.",
|
|
37716
|
+
"name": "size",
|
|
37717
|
+
"values": "regular · small"
|
|
37718
|
+
}
|
|
37719
|
+
],
|
|
37720
|
+
"example": "LumenFloatingActionButton(\n imageVector = Icons.Default.Add,\n contentDescription = \"Create project\",\n onClick = ::createProject\n)",
|
|
37721
|
+
"exportName": "LumenFloatingActionButton",
|
|
37722
|
+
"importStatement": "import com.santi020k.lumen.LumenFloatingActionButton",
|
|
37723
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
37724
|
+
"language": "kotlin",
|
|
37725
|
+
"packageName": "com.santi020k:lumen-compose",
|
|
37726
|
+
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
37727
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/PlatformComponents.kt",
|
|
37728
|
+
"symbol": "LumenFloatingActionButton"
|
|
37729
|
+
}
|
|
37730
|
+
},
|
|
37731
|
+
"name": "Floating action button",
|
|
37732
|
+
"summary": "Present a prominent Material-native action using Lumen semantic intent.",
|
|
37733
|
+
"supportedPlatforms": [
|
|
37734
|
+
"Android"
|
|
37735
|
+
],
|
|
37736
|
+
"tier": "platform-specific"
|
|
37737
|
+
},
|
|
36699
37738
|
{
|
|
36700
37739
|
"accessibility": "Recording, current shortcut, validation error, cancel, change, and clear states all have visible native labels.",
|
|
36701
37740
|
"category": "macOS utilities",
|
|
@@ -36733,7 +37772,7 @@
|
|
|
36733
37772
|
"example": "LumenShortcutRecorder(\n \"Quick switch\",\n shortcut: $shortcut\n) { candidate in\n reserved.contains(candidate) ? \"Already in use.\" : nil\n}",
|
|
36734
37773
|
"exportName": "LumenShortcutRecorder",
|
|
36735
37774
|
"importStatement": "import LumenUI",
|
|
36736
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
37775
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
36737
37776
|
"language": "swift",
|
|
36738
37777
|
"packageName": "LumenUI",
|
|
36739
37778
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -36746,7 +37785,7 @@
|
|
|
36746
37785
|
"supportedPlatforms": [
|
|
36747
37786
|
"macOS"
|
|
36748
37787
|
],
|
|
36749
|
-
"tier": "
|
|
37788
|
+
"tier": "platform-specific"
|
|
36750
37789
|
},
|
|
36751
37790
|
{
|
|
36752
37791
|
"accessibility": "Every symbol option has a readable label and selected state. Search and empty states use native controls and focus behavior.",
|
|
@@ -36785,7 +37824,7 @@
|
|
|
36785
37824
|
"example": "LumenSymbolPickerButton(\n \"Workspace symbol\",\n selectedName: $symbolName\n)",
|
|
36786
37825
|
"exportName": "LumenSymbolPicker / LumenSymbolPickerButton",
|
|
36787
37826
|
"importStatement": "import LumenUI",
|
|
36788
|
-
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager and link the LumenUI product to the application target.",
|
|
37827
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
36789
37828
|
"language": "swift",
|
|
36790
37829
|
"packageName": "LumenUI",
|
|
36791
37830
|
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
@@ -36798,31 +37837,936 @@
|
|
|
36798
37837
|
"supportedPlatforms": [
|
|
36799
37838
|
"macOS"
|
|
36800
37839
|
],
|
|
36801
|
-
"tier": "
|
|
37840
|
+
"tier": "platform-specific"
|
|
37841
|
+
},
|
|
37842
|
+
{
|
|
37843
|
+
"accessibility": "Decorative graphics stay hidden from assistive technology; an optional label exposes the complete composition as one image.",
|
|
37844
|
+
"category": "Data display",
|
|
37845
|
+
"contract": "Token-aware glow, grid, and orbit presets around application-provided artwork",
|
|
37846
|
+
"guidance": "Use to frame meaningful application artwork with a restrained token-aware preset. Leave the label absent when the surrounding content already communicates the same meaning.",
|
|
37847
|
+
"id": "graphic",
|
|
37848
|
+
"implementations": {
|
|
37849
|
+
"compose": {
|
|
37850
|
+
"api": [
|
|
37851
|
+
{
|
|
37852
|
+
"defaultValue": "orbit",
|
|
37853
|
+
"description": "Selects the decorative composition.",
|
|
37854
|
+
"name": "variant",
|
|
37855
|
+
"values": "glow · grid · orbit"
|
|
37856
|
+
},
|
|
37857
|
+
{
|
|
37858
|
+
"defaultValue": "md",
|
|
37859
|
+
"description": "Selects a shared native dimension.",
|
|
37860
|
+
"name": "size",
|
|
37861
|
+
"values": "sm · md · lg"
|
|
37862
|
+
},
|
|
37863
|
+
{
|
|
37864
|
+
"defaultValue": "brand",
|
|
37865
|
+
"description": "Selects the semantic decoration color.",
|
|
37866
|
+
"name": "tone",
|
|
37867
|
+
"values": "brand · accent · neutral"
|
|
37868
|
+
},
|
|
37869
|
+
{
|
|
37870
|
+
"defaultValue": "null",
|
|
37871
|
+
"description": "Optionally exposes the composition as one labeled image.",
|
|
37872
|
+
"name": "label",
|
|
37873
|
+
"values": "String?"
|
|
37874
|
+
}
|
|
37875
|
+
],
|
|
37876
|
+
"example": "LumenGraphic(\n variant = LumenGraphicVariant.Orbit,\n label = \"Project overview\"\n) {\n ProjectArtwork()\n}",
|
|
37877
|
+
"exportName": "LumenGraphic",
|
|
37878
|
+
"importStatement": "import com.santi020k.lumen.LumenGraphic",
|
|
37879
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
37880
|
+
"language": "kotlin",
|
|
37881
|
+
"packageName": "com.santi020k:lumen-compose",
|
|
37882
|
+
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
37883
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/ContentComponents.kt",
|
|
37884
|
+
"symbol": "LumenGraphic"
|
|
37885
|
+
},
|
|
37886
|
+
"react-native": {
|
|
37887
|
+
"api": [
|
|
37888
|
+
{
|
|
37889
|
+
"defaultValue": "orbit",
|
|
37890
|
+
"description": "Selects the decorative composition.",
|
|
37891
|
+
"name": "variant",
|
|
37892
|
+
"values": "glow · grid · orbit"
|
|
37893
|
+
},
|
|
37894
|
+
{
|
|
37895
|
+
"defaultValue": "md",
|
|
37896
|
+
"description": "Selects a shared native dimension.",
|
|
37897
|
+
"name": "size",
|
|
37898
|
+
"values": "sm · md · lg"
|
|
37899
|
+
},
|
|
37900
|
+
{
|
|
37901
|
+
"defaultValue": "brand",
|
|
37902
|
+
"description": "Selects the semantic decoration color.",
|
|
37903
|
+
"name": "tone",
|
|
37904
|
+
"values": "brand · accent · neutral"
|
|
37905
|
+
},
|
|
37906
|
+
{
|
|
37907
|
+
"defaultValue": "undefined",
|
|
37908
|
+
"description": "Optionally exposes the composition as one labeled image.",
|
|
37909
|
+
"name": "label",
|
|
37910
|
+
"values": "string"
|
|
37911
|
+
}
|
|
37912
|
+
],
|
|
37913
|
+
"example": "<LumenGraphic variant=\"orbit\" label=\"Project overview\">\n <ProjectArtwork />\n</LumenGraphic>",
|
|
37914
|
+
"exportName": "LumenGraphic",
|
|
37915
|
+
"importStatement": "import { LumenGraphic } from '@santi020k/lumen-react-native'",
|
|
37916
|
+
"install": "pnpm add @santi020k/lumen-react-native",
|
|
37917
|
+
"language": "tsx",
|
|
37918
|
+
"packageName": "@santi020k/lumen-react-native",
|
|
37919
|
+
"setup": "Mount one LumenProvider near the application root. Native adapters do not load CSS or the Astro runtime.",
|
|
37920
|
+
"sourceFile": "packages/react-native/src/content-components.tsx",
|
|
37921
|
+
"symbol": "LumenGraphic"
|
|
37922
|
+
},
|
|
37923
|
+
"swiftui": {
|
|
37924
|
+
"api": [
|
|
37925
|
+
{
|
|
37926
|
+
"defaultValue": "orbit",
|
|
37927
|
+
"description": "Selects the decorative composition.",
|
|
37928
|
+
"name": "variant",
|
|
37929
|
+
"values": "glow · grid · orbit"
|
|
37930
|
+
},
|
|
37931
|
+
{
|
|
37932
|
+
"defaultValue": "md",
|
|
37933
|
+
"description": "Selects a shared native dimension.",
|
|
37934
|
+
"name": "size",
|
|
37935
|
+
"values": "sm · md · lg"
|
|
37936
|
+
},
|
|
37937
|
+
{
|
|
37938
|
+
"defaultValue": "brand",
|
|
37939
|
+
"description": "Selects the semantic decoration color.",
|
|
37940
|
+
"name": "tone",
|
|
37941
|
+
"values": "brand · accent · neutral"
|
|
37942
|
+
},
|
|
37943
|
+
{
|
|
37944
|
+
"defaultValue": "nil",
|
|
37945
|
+
"description": "Optionally exposes the composition as one labeled image.",
|
|
37946
|
+
"name": "label",
|
|
37947
|
+
"values": "LocalizedStringKey?"
|
|
37948
|
+
}
|
|
37949
|
+
],
|
|
37950
|
+
"example": "LumenGraphic(\n variant: .orbit,\n label: \"Project overview\"\n) {\n ProjectArtwork()\n}",
|
|
37951
|
+
"exportName": "LumenGraphic",
|
|
37952
|
+
"importStatement": "import LumenUI",
|
|
37953
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
37954
|
+
"language": "swift",
|
|
37955
|
+
"packageName": "LumenUI",
|
|
37956
|
+
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
37957
|
+
"sourceFile": "packages/swift/Sources/LumenUI/ContentComponents.swift",
|
|
37958
|
+
"symbol": "LumenGraphic"
|
|
37959
|
+
}
|
|
37960
|
+
},
|
|
37961
|
+
"name": "Graphic",
|
|
37962
|
+
"summary": "Frame application-provided artwork with token-aware glow, grid, or orbit decoration.",
|
|
37963
|
+
"tier": "shared"
|
|
37964
|
+
},
|
|
37965
|
+
{
|
|
37966
|
+
"accessibility": "The ambient layer has no accessibility semantics; child content keeps its native reading order, names, and controls.",
|
|
37967
|
+
"category": "Data display",
|
|
37968
|
+
"contract": "Ambient aurora, dots, grid, and rays behind application content with semantic tones and intensity",
|
|
37969
|
+
"guidance": "Use behind meaningful hero, empty-state, or highlighted content. Keep child surfaces and text on semantic tokens so contrast does not depend on the decoration.",
|
|
37970
|
+
"id": "backdrop",
|
|
37971
|
+
"implementations": {
|
|
37972
|
+
"compose": {
|
|
37973
|
+
"api": [
|
|
37974
|
+
{
|
|
37975
|
+
"defaultValue": "aurora",
|
|
37976
|
+
"description": "Selects the ambient pattern.",
|
|
37977
|
+
"name": "variant",
|
|
37978
|
+
"values": "aurora · dots · grid · rays"
|
|
37979
|
+
},
|
|
37980
|
+
{
|
|
37981
|
+
"defaultValue": "brand",
|
|
37982
|
+
"description": "Selects the semantic decoration color.",
|
|
37983
|
+
"name": "tone",
|
|
37984
|
+
"values": "brand · accent · neutral"
|
|
37985
|
+
},
|
|
37986
|
+
{
|
|
37987
|
+
"defaultValue": "medium",
|
|
37988
|
+
"description": "Controls only the decoration opacity.",
|
|
37989
|
+
"name": "intensity",
|
|
37990
|
+
"values": "subtle · medium · strong"
|
|
37991
|
+
},
|
|
37992
|
+
{
|
|
37993
|
+
"defaultValue": "Required",
|
|
37994
|
+
"description": "Renders meaningful content above the decorative layer.",
|
|
37995
|
+
"name": "content",
|
|
37996
|
+
"values": "Native view content"
|
|
37997
|
+
}
|
|
37998
|
+
],
|
|
37999
|
+
"example": "LumenBackdrop(\n intensity = LumenBackdropIntensity.Medium,\n variant = LumenBackdropVariant.Aurora\n) {\n ProjectOverview()\n}",
|
|
38000
|
+
"exportName": "LumenBackdrop",
|
|
38001
|
+
"importStatement": "import com.santi020k.lumen.LumenBackdrop",
|
|
38002
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
38003
|
+
"language": "kotlin",
|
|
38004
|
+
"packageName": "com.santi020k:lumen-compose",
|
|
38005
|
+
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
38006
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/ContentComponents.kt",
|
|
38007
|
+
"symbol": "LumenBackdrop"
|
|
38008
|
+
},
|
|
38009
|
+
"react-native": {
|
|
38010
|
+
"api": [
|
|
38011
|
+
{
|
|
38012
|
+
"defaultValue": "aurora",
|
|
38013
|
+
"description": "Selects the ambient pattern.",
|
|
38014
|
+
"name": "variant",
|
|
38015
|
+
"values": "aurora · dots · grid · rays"
|
|
38016
|
+
},
|
|
38017
|
+
{
|
|
38018
|
+
"defaultValue": "brand",
|
|
38019
|
+
"description": "Selects the semantic decoration color.",
|
|
38020
|
+
"name": "tone",
|
|
38021
|
+
"values": "brand · accent · neutral"
|
|
38022
|
+
},
|
|
38023
|
+
{
|
|
38024
|
+
"defaultValue": "medium",
|
|
38025
|
+
"description": "Controls only the decoration opacity.",
|
|
38026
|
+
"name": "intensity",
|
|
38027
|
+
"values": "subtle · medium · strong"
|
|
38028
|
+
},
|
|
38029
|
+
{
|
|
38030
|
+
"defaultValue": "Required",
|
|
38031
|
+
"description": "Renders meaningful content above the decorative layer.",
|
|
38032
|
+
"name": "content",
|
|
38033
|
+
"values": "Native view content"
|
|
38034
|
+
}
|
|
38035
|
+
],
|
|
38036
|
+
"example": "<LumenBackdrop intensity=\"medium\" variant=\"aurora\">\n <ProjectOverview />\n</LumenBackdrop>",
|
|
38037
|
+
"exportName": "LumenBackdrop",
|
|
38038
|
+
"importStatement": "import { LumenBackdrop } from '@santi020k/lumen-react-native'",
|
|
38039
|
+
"install": "pnpm add @santi020k/lumen-react-native",
|
|
38040
|
+
"language": "tsx",
|
|
38041
|
+
"packageName": "@santi020k/lumen-react-native",
|
|
38042
|
+
"setup": "Mount one LumenProvider near the application root. Native adapters do not load CSS or the Astro runtime.",
|
|
38043
|
+
"sourceFile": "packages/react-native/src/content-components.tsx",
|
|
38044
|
+
"symbol": "LumenBackdrop"
|
|
38045
|
+
},
|
|
38046
|
+
"swiftui": {
|
|
38047
|
+
"api": [
|
|
38048
|
+
{
|
|
38049
|
+
"defaultValue": "aurora",
|
|
38050
|
+
"description": "Selects the ambient pattern.",
|
|
38051
|
+
"name": "variant",
|
|
38052
|
+
"values": "aurora · dots · grid · rays"
|
|
38053
|
+
},
|
|
38054
|
+
{
|
|
38055
|
+
"defaultValue": "brand",
|
|
38056
|
+
"description": "Selects the semantic decoration color.",
|
|
38057
|
+
"name": "tone",
|
|
38058
|
+
"values": "brand · accent · neutral"
|
|
38059
|
+
},
|
|
38060
|
+
{
|
|
38061
|
+
"defaultValue": "medium",
|
|
38062
|
+
"description": "Controls only the decoration opacity.",
|
|
38063
|
+
"name": "intensity",
|
|
38064
|
+
"values": "subtle · medium · strong"
|
|
38065
|
+
},
|
|
38066
|
+
{
|
|
38067
|
+
"defaultValue": "Required",
|
|
38068
|
+
"description": "Renders meaningful content above the decorative layer.",
|
|
38069
|
+
"name": "content",
|
|
38070
|
+
"values": "Native view content"
|
|
38071
|
+
}
|
|
38072
|
+
],
|
|
38073
|
+
"example": "LumenBackdrop(\n intensity: .medium,\n variant: .aurora\n) {\n ProjectOverview()\n}",
|
|
38074
|
+
"exportName": "LumenBackdrop",
|
|
38075
|
+
"importStatement": "import LumenUI",
|
|
38076
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
38077
|
+
"language": "swift",
|
|
38078
|
+
"packageName": "LumenUI",
|
|
38079
|
+
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
38080
|
+
"sourceFile": "packages/swift/Sources/LumenUI/ContentComponents.swift",
|
|
38081
|
+
"symbol": "LumenBackdrop"
|
|
38082
|
+
}
|
|
38083
|
+
},
|
|
38084
|
+
"name": "Backdrop",
|
|
38085
|
+
"summary": "Place a token-aware ambient pattern behind native application content.",
|
|
38086
|
+
"tier": "shared"
|
|
38087
|
+
},
|
|
38088
|
+
{
|
|
38089
|
+
"accessibility": "Illustrations are decorative by default; an optional label exposes the complete scene as one image.",
|
|
38090
|
+
"category": "Feedback",
|
|
38091
|
+
"contract": "Built-in empty, success, error, and offline scenes with shared semantic colors and sizes",
|
|
38092
|
+
"guidance": "Use the built-in scene when its state matches the product message. Keep it decorative when the adjacent heading and description already communicate the same status.",
|
|
38093
|
+
"id": "illustration",
|
|
38094
|
+
"implementations": {
|
|
38095
|
+
"compose": {
|
|
38096
|
+
"api": [
|
|
38097
|
+
{
|
|
38098
|
+
"defaultValue": "empty",
|
|
38099
|
+
"description": "Selects the semantic scene.",
|
|
38100
|
+
"name": "variant",
|
|
38101
|
+
"values": "empty · success · error · offline"
|
|
38102
|
+
},
|
|
38103
|
+
{
|
|
38104
|
+
"defaultValue": "auto",
|
|
38105
|
+
"description": "Uses the state color automatically or an explicit portable tone.",
|
|
38106
|
+
"name": "tone",
|
|
38107
|
+
"values": "auto · brand · accent · neutral"
|
|
38108
|
+
},
|
|
38109
|
+
{
|
|
38110
|
+
"defaultValue": "md",
|
|
38111
|
+
"description": "Selects the shared 96, 128, or 176 unit dimension.",
|
|
38112
|
+
"name": "size",
|
|
38113
|
+
"values": "sm · md · lg"
|
|
38114
|
+
},
|
|
38115
|
+
{
|
|
38116
|
+
"defaultValue": "null",
|
|
38117
|
+
"description": "Optionally exposes the illustration as one labeled image.",
|
|
38118
|
+
"name": "label",
|
|
38119
|
+
"values": "String?"
|
|
38120
|
+
}
|
|
38121
|
+
],
|
|
38122
|
+
"example": "LumenIllustration(\n variant = LumenIllustrationVariant.Success,\n label = \"Saved successfully\"\n)",
|
|
38123
|
+
"exportName": "LumenIllustration",
|
|
38124
|
+
"importStatement": "import com.santi020k.lumen.LumenIllustration",
|
|
38125
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
38126
|
+
"language": "kotlin",
|
|
38127
|
+
"packageName": "com.santi020k:lumen-compose",
|
|
38128
|
+
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
38129
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/ContentComponents.kt",
|
|
38130
|
+
"symbol": "LumenIllustration"
|
|
38131
|
+
},
|
|
38132
|
+
"react-native": {
|
|
38133
|
+
"api": [
|
|
38134
|
+
{
|
|
38135
|
+
"defaultValue": "empty",
|
|
38136
|
+
"description": "Selects the semantic scene.",
|
|
38137
|
+
"name": "variant",
|
|
38138
|
+
"values": "empty · success · error · offline"
|
|
38139
|
+
},
|
|
38140
|
+
{
|
|
38141
|
+
"defaultValue": "auto",
|
|
38142
|
+
"description": "Uses the state color automatically or an explicit portable tone.",
|
|
38143
|
+
"name": "tone",
|
|
38144
|
+
"values": "auto · brand · accent · neutral"
|
|
38145
|
+
},
|
|
38146
|
+
{
|
|
38147
|
+
"defaultValue": "md",
|
|
38148
|
+
"description": "Selects the shared 96, 128, or 176 unit dimension.",
|
|
38149
|
+
"name": "size",
|
|
38150
|
+
"values": "sm · md · lg"
|
|
38151
|
+
},
|
|
38152
|
+
{
|
|
38153
|
+
"defaultValue": "undefined",
|
|
38154
|
+
"description": "Optionally exposes the illustration as one labeled image.",
|
|
38155
|
+
"name": "label",
|
|
38156
|
+
"values": "string"
|
|
38157
|
+
}
|
|
38158
|
+
],
|
|
38159
|
+
"example": "<LumenIllustration\n label=\"Saved successfully\"\n variant=\"success\"\n/>",
|
|
38160
|
+
"exportName": "LumenIllustration",
|
|
38161
|
+
"importStatement": "import { LumenIllustration } from '@santi020k/lumen-react-native'",
|
|
38162
|
+
"install": "pnpm add @santi020k/lumen-react-native",
|
|
38163
|
+
"language": "tsx",
|
|
38164
|
+
"packageName": "@santi020k/lumen-react-native",
|
|
38165
|
+
"setup": "Mount one LumenProvider near the application root. Native adapters do not load CSS or the Astro runtime.",
|
|
38166
|
+
"sourceFile": "packages/react-native/src/content-components.tsx",
|
|
38167
|
+
"symbol": "LumenIllustration"
|
|
38168
|
+
},
|
|
38169
|
+
"swiftui": {
|
|
38170
|
+
"api": [
|
|
38171
|
+
{
|
|
38172
|
+
"defaultValue": "empty",
|
|
38173
|
+
"description": "Selects the semantic scene.",
|
|
38174
|
+
"name": "variant",
|
|
38175
|
+
"values": "empty · success · error · offline"
|
|
38176
|
+
},
|
|
38177
|
+
{
|
|
38178
|
+
"defaultValue": "auto",
|
|
38179
|
+
"description": "Uses the state color automatically or an explicit portable tone.",
|
|
38180
|
+
"name": "tone",
|
|
38181
|
+
"values": "auto · brand · accent · neutral"
|
|
38182
|
+
},
|
|
38183
|
+
{
|
|
38184
|
+
"defaultValue": "md",
|
|
38185
|
+
"description": "Selects the shared 96, 128, or 176 unit dimension.",
|
|
38186
|
+
"name": "size",
|
|
38187
|
+
"values": "sm · md · lg"
|
|
38188
|
+
},
|
|
38189
|
+
{
|
|
38190
|
+
"defaultValue": "nil",
|
|
38191
|
+
"description": "Optionally exposes the illustration as one labeled image.",
|
|
38192
|
+
"name": "label",
|
|
38193
|
+
"values": "String?"
|
|
38194
|
+
}
|
|
38195
|
+
],
|
|
38196
|
+
"example": "LumenIllustration(\n variant: .success,\n label: \"Saved successfully\"\n)",
|
|
38197
|
+
"exportName": "LumenIllustration",
|
|
38198
|
+
"importStatement": "import LumenUI",
|
|
38199
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
38200
|
+
"language": "swift",
|
|
38201
|
+
"packageName": "LumenUI",
|
|
38202
|
+
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
38203
|
+
"sourceFile": "packages/swift/Sources/LumenUI/ContentComponents.swift",
|
|
38204
|
+
"symbol": "LumenIllustration"
|
|
38205
|
+
}
|
|
38206
|
+
},
|
|
38207
|
+
"name": "Illustration",
|
|
38208
|
+
"summary": "Render a built-in empty, success, error, or offline semantic scene.",
|
|
38209
|
+
"tier": "shared"
|
|
38210
|
+
},
|
|
38211
|
+
{
|
|
38212
|
+
"accessibility": "Native modal focus stays inside the confirmation surface and cancel and confirm remain independently named actions.",
|
|
38213
|
+
"category": "Feedback",
|
|
38214
|
+
"contract": "Controlled confirmation with cancel, confirm, destructive, disabled, and loading states",
|
|
38215
|
+
"guidance": "Use for short consequential confirmations. Keep visibility and mutation state in the application and reserve destructive styling for irreversible or difficult-to-recover actions.",
|
|
38216
|
+
"id": "alert-dialog",
|
|
38217
|
+
"implementations": {
|
|
38218
|
+
"compose": {
|
|
38219
|
+
"api": [
|
|
38220
|
+
{
|
|
38221
|
+
"defaultValue": "Required",
|
|
38222
|
+
"description": "Controls native modal presentation.",
|
|
38223
|
+
"name": "visible",
|
|
38224
|
+
"values": "Boolean"
|
|
38225
|
+
},
|
|
38226
|
+
{
|
|
38227
|
+
"defaultValue": "Description optional",
|
|
38228
|
+
"description": "Explains the decision.",
|
|
38229
|
+
"name": "title / description",
|
|
38230
|
+
"values": "Native localized text"
|
|
38231
|
+
},
|
|
38232
|
+
{
|
|
38233
|
+
"defaultValue": "Cancel label: Cancel",
|
|
38234
|
+
"description": "Names both actions.",
|
|
38235
|
+
"name": "confirmLabel / cancelLabel",
|
|
38236
|
+
"values": "Native localized text"
|
|
38237
|
+
},
|
|
38238
|
+
{
|
|
38239
|
+
"defaultValue": "false",
|
|
38240
|
+
"description": "Communicates whether the confirm action is destructive.",
|
|
38241
|
+
"name": "destructive",
|
|
38242
|
+
"values": "Boolean"
|
|
38243
|
+
},
|
|
38244
|
+
{
|
|
38245
|
+
"defaultValue": "true",
|
|
38246
|
+
"description": "Controls whether the confirm action can be activated.",
|
|
38247
|
+
"name": "confirmEnabled",
|
|
38248
|
+
"values": "Boolean"
|
|
38249
|
+
},
|
|
38250
|
+
{
|
|
38251
|
+
"defaultValue": "false",
|
|
38252
|
+
"description": "Communicates asynchronous confirmation progress.",
|
|
38253
|
+
"name": "confirmLoading",
|
|
38254
|
+
"values": "Boolean"
|
|
38255
|
+
}
|
|
38256
|
+
],
|
|
38257
|
+
"example": "LumenAlertDialog(\n visible = showConfirmation,\n title = \"Delete report?\",\n confirmLabel = \"Delete\",\n destructive = true,\n onConfirm = ::deleteReport,\n onDismiss = { showConfirmation = false }\n)",
|
|
38258
|
+
"exportName": "LumenAlertDialog",
|
|
38259
|
+
"importStatement": "import com.santi020k.lumen.LumenAlertDialog",
|
|
38260
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
38261
|
+
"language": "kotlin",
|
|
38262
|
+
"packageName": "com.santi020k:lumen-compose",
|
|
38263
|
+
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
38264
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/OverlayComponents.kt",
|
|
38265
|
+
"symbol": "LumenAlertDialog"
|
|
38266
|
+
},
|
|
38267
|
+
"react-native": {
|
|
38268
|
+
"api": [
|
|
38269
|
+
{
|
|
38270
|
+
"defaultValue": "Required",
|
|
38271
|
+
"description": "Controls native modal presentation.",
|
|
38272
|
+
"name": "visible",
|
|
38273
|
+
"values": "boolean"
|
|
38274
|
+
},
|
|
38275
|
+
{
|
|
38276
|
+
"defaultValue": "Description optional",
|
|
38277
|
+
"description": "Explains the decision.",
|
|
38278
|
+
"name": "title / description",
|
|
38279
|
+
"values": "Native localized text"
|
|
38280
|
+
},
|
|
38281
|
+
{
|
|
38282
|
+
"defaultValue": "Cancel label: Cancel",
|
|
38283
|
+
"description": "Names both actions.",
|
|
38284
|
+
"name": "confirmLabel / cancelLabel",
|
|
38285
|
+
"values": "Native localized text"
|
|
38286
|
+
},
|
|
38287
|
+
{
|
|
38288
|
+
"defaultValue": "false",
|
|
38289
|
+
"description": "Communicates whether the confirm action is destructive.",
|
|
38290
|
+
"name": "destructive",
|
|
38291
|
+
"values": "boolean"
|
|
38292
|
+
},
|
|
38293
|
+
{
|
|
38294
|
+
"defaultValue": "false",
|
|
38295
|
+
"description": "Controls whether the confirm action can be activated.",
|
|
38296
|
+
"name": "confirmDisabled",
|
|
38297
|
+
"values": "boolean"
|
|
38298
|
+
},
|
|
38299
|
+
{
|
|
38300
|
+
"defaultValue": "false",
|
|
38301
|
+
"description": "Communicates asynchronous confirmation progress.",
|
|
38302
|
+
"name": "confirmLoading",
|
|
38303
|
+
"values": "boolean"
|
|
38304
|
+
}
|
|
38305
|
+
],
|
|
38306
|
+
"example": "<LumenAlertDialog\n visible={showConfirmation}\n title=\"Delete report?\"\n confirmLabel=\"Delete\"\n destructive\n onConfirm={deleteReport}\n onDismiss={() => setShowConfirmation(false)}\n/>",
|
|
38307
|
+
"exportName": "LumenAlertDialog",
|
|
38308
|
+
"importStatement": "import { LumenAlertDialog } from '@santi020k/lumen-react-native'",
|
|
38309
|
+
"install": "pnpm add @santi020k/lumen-react-native",
|
|
38310
|
+
"language": "tsx",
|
|
38311
|
+
"packageName": "@santi020k/lumen-react-native",
|
|
38312
|
+
"setup": "Mount one LumenProvider near the application root. Native adapters do not load CSS or the Astro runtime.",
|
|
38313
|
+
"sourceFile": "packages/react-native/src/index.ts",
|
|
38314
|
+
"symbol": "LumenAlertDialog"
|
|
38315
|
+
},
|
|
38316
|
+
"swiftui": {
|
|
38317
|
+
"api": [
|
|
38318
|
+
{
|
|
38319
|
+
"defaultValue": "Required",
|
|
38320
|
+
"description": "Controls native modal presentation.",
|
|
38321
|
+
"name": "isPresented",
|
|
38322
|
+
"values": "Binding<Bool>"
|
|
38323
|
+
},
|
|
38324
|
+
{
|
|
38325
|
+
"defaultValue": "Description optional",
|
|
38326
|
+
"description": "Explains the decision.",
|
|
38327
|
+
"name": "title / description",
|
|
38328
|
+
"values": "Native localized text"
|
|
38329
|
+
},
|
|
38330
|
+
{
|
|
38331
|
+
"defaultValue": "Cancel label: Cancel",
|
|
38332
|
+
"description": "Names both actions.",
|
|
38333
|
+
"name": "confirmLabel / cancelLabel",
|
|
38334
|
+
"values": "Native localized text"
|
|
38335
|
+
},
|
|
38336
|
+
{
|
|
38337
|
+
"defaultValue": "nil",
|
|
38338
|
+
"description": "Communicates whether the confirm action is destructive.",
|
|
38339
|
+
"name": "confirmRole",
|
|
38340
|
+
"values": "ButtonRole?"
|
|
38341
|
+
},
|
|
38342
|
+
{
|
|
38343
|
+
"defaultValue": "false",
|
|
38344
|
+
"description": "Controls whether the confirm action can be activated.",
|
|
38345
|
+
"name": "confirmDisabled",
|
|
38346
|
+
"values": "Bool"
|
|
38347
|
+
},
|
|
38348
|
+
{
|
|
38349
|
+
"defaultValue": "false",
|
|
38350
|
+
"description": "Communicates asynchronous confirmation progress.",
|
|
38351
|
+
"name": "confirmLoading",
|
|
38352
|
+
"values": "Bool"
|
|
38353
|
+
}
|
|
38354
|
+
],
|
|
38355
|
+
"example": "content.lumenAlertDialog(\n isPresented: $showConfirmation,\n title: \"Delete report?\",\n confirmLabel: \"Delete\",\n confirmRole: .destructive,\n onConfirm: deleteReport\n)",
|
|
38356
|
+
"exportName": "lumenAlertDialog",
|
|
38357
|
+
"importStatement": "import LumenUI",
|
|
38358
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
38359
|
+
"language": "swift",
|
|
38360
|
+
"packageName": "LumenUI",
|
|
38361
|
+
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
38362
|
+
"sourceFile": "packages/swift/Sources/LumenUI/OverlayComponents.swift",
|
|
38363
|
+
"symbol": "lumenAlertDialog"
|
|
38364
|
+
}
|
|
38365
|
+
},
|
|
38366
|
+
"name": "Alert dialog",
|
|
38367
|
+
"summary": "Request a controlled native confirmation with explicit cancel and confirm states.",
|
|
38368
|
+
"tier": "shared"
|
|
38369
|
+
},
|
|
38370
|
+
{
|
|
38371
|
+
"accessibility": "The native modal presentation contains focus while preserving the semantics and reading order of application-owned content.",
|
|
38372
|
+
"category": "Layout",
|
|
38373
|
+
"contract": "Controlled supplemental surface with optional heading, description, actions, and native dismissal",
|
|
38374
|
+
"guidance": "Use for supplemental editing or detail that should not replace the current screen. Keep form state and dismissal decisions application-owned.",
|
|
38375
|
+
"id": "sheet",
|
|
38376
|
+
"implementations": {
|
|
38377
|
+
"compose": {
|
|
38378
|
+
"api": [
|
|
38379
|
+
{
|
|
38380
|
+
"defaultValue": "Required",
|
|
38381
|
+
"description": "Controls native sheet presentation.",
|
|
38382
|
+
"name": "visible",
|
|
38383
|
+
"values": "Boolean"
|
|
38384
|
+
},
|
|
38385
|
+
{
|
|
38386
|
+
"defaultValue": "nil / undefined",
|
|
38387
|
+
"description": "Provides optional heading context.",
|
|
38388
|
+
"name": "title / description",
|
|
38389
|
+
"values": "Native localized text"
|
|
38390
|
+
},
|
|
38391
|
+
{
|
|
38392
|
+
"defaultValue": "Required",
|
|
38393
|
+
"description": "Returns presentation ownership to the application.",
|
|
38394
|
+
"name": "onDismiss",
|
|
38395
|
+
"values": "() -> Void"
|
|
38396
|
+
},
|
|
38397
|
+
{
|
|
38398
|
+
"defaultValue": "Actions optional",
|
|
38399
|
+
"description": "Composes application-owned content and actions.",
|
|
38400
|
+
"name": "content / actions",
|
|
38401
|
+
"values": "Native view slots"
|
|
38402
|
+
}
|
|
38403
|
+
],
|
|
38404
|
+
"example": "LumenSheet(\n visible = showEditor,\n onDismiss = { showEditor = false },\n title = \"Edit report\"\n) {\n ReportEditor()\n}",
|
|
38405
|
+
"exportName": "LumenSheet",
|
|
38406
|
+
"importStatement": "import com.santi020k.lumen.LumenSheet",
|
|
38407
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
38408
|
+
"language": "kotlin",
|
|
38409
|
+
"packageName": "com.santi020k:lumen-compose",
|
|
38410
|
+
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
38411
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/OverlayComponents.kt",
|
|
38412
|
+
"symbol": "LumenSheet"
|
|
38413
|
+
},
|
|
38414
|
+
"react-native": {
|
|
38415
|
+
"api": [
|
|
38416
|
+
{
|
|
38417
|
+
"defaultValue": "Required",
|
|
38418
|
+
"description": "Controls native sheet presentation.",
|
|
38419
|
+
"name": "visible",
|
|
38420
|
+
"values": "boolean"
|
|
38421
|
+
},
|
|
38422
|
+
{
|
|
38423
|
+
"defaultValue": "nil / undefined",
|
|
38424
|
+
"description": "Provides optional heading context.",
|
|
38425
|
+
"name": "title / description",
|
|
38426
|
+
"values": "Native localized text"
|
|
38427
|
+
},
|
|
38428
|
+
{
|
|
38429
|
+
"defaultValue": "Required",
|
|
38430
|
+
"description": "Returns presentation ownership to the application.",
|
|
38431
|
+
"name": "onDismiss",
|
|
38432
|
+
"values": "() -> Void"
|
|
38433
|
+
},
|
|
38434
|
+
{
|
|
38435
|
+
"defaultValue": "Actions optional",
|
|
38436
|
+
"description": "Composes application-owned content and actions.",
|
|
38437
|
+
"name": "content / actions",
|
|
38438
|
+
"values": "Native view slots"
|
|
38439
|
+
}
|
|
38440
|
+
],
|
|
38441
|
+
"example": "<LumenSheet\n visible={showEditor}\n title=\"Edit report\"\n onDismiss={() => setShowEditor(false)}\n>\n <ReportEditor />\n</LumenSheet>",
|
|
38442
|
+
"exportName": "LumenSheet",
|
|
38443
|
+
"importStatement": "import { LumenSheet } from '@santi020k/lumen-react-native'",
|
|
38444
|
+
"install": "pnpm add @santi020k/lumen-react-native",
|
|
38445
|
+
"language": "tsx",
|
|
38446
|
+
"packageName": "@santi020k/lumen-react-native",
|
|
38447
|
+
"setup": "Mount one LumenProvider near the application root. Native adapters do not load CSS or the Astro runtime.",
|
|
38448
|
+
"sourceFile": "packages/react-native/src/index.ts",
|
|
38449
|
+
"symbol": "LumenSheet"
|
|
38450
|
+
},
|
|
38451
|
+
"swiftui": {
|
|
38452
|
+
"api": [
|
|
38453
|
+
{
|
|
38454
|
+
"defaultValue": "Required",
|
|
38455
|
+
"description": "Controls native sheet presentation.",
|
|
38456
|
+
"name": "isPresented",
|
|
38457
|
+
"values": "Binding<Bool>"
|
|
38458
|
+
},
|
|
38459
|
+
{
|
|
38460
|
+
"defaultValue": "nil / undefined",
|
|
38461
|
+
"description": "Provides optional heading context.",
|
|
38462
|
+
"name": "title / description",
|
|
38463
|
+
"values": "Native localized text"
|
|
38464
|
+
},
|
|
38465
|
+
{
|
|
38466
|
+
"defaultValue": "Required",
|
|
38467
|
+
"description": "Returns presentation ownership to the application.",
|
|
38468
|
+
"name": "onDismiss",
|
|
38469
|
+
"values": "() -> Void"
|
|
38470
|
+
},
|
|
38471
|
+
{
|
|
38472
|
+
"defaultValue": "Actions optional",
|
|
38473
|
+
"description": "Composes application-owned content and actions.",
|
|
38474
|
+
"name": "content / actions",
|
|
38475
|
+
"values": "Native view slots"
|
|
38476
|
+
}
|
|
38477
|
+
],
|
|
38478
|
+
"example": "content.lumenSheet(\n isPresented: $showEditor,\n title: \"Edit report\"\n) {\n ReportEditor()\n}",
|
|
38479
|
+
"exportName": "lumenSheet",
|
|
38480
|
+
"importStatement": "import LumenUI",
|
|
38481
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
38482
|
+
"language": "swift",
|
|
38483
|
+
"packageName": "LumenUI",
|
|
38484
|
+
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
38485
|
+
"sourceFile": "packages/swift/Sources/LumenUI/OverlayComponents.swift",
|
|
38486
|
+
"symbol": "lumenSheet"
|
|
38487
|
+
}
|
|
38488
|
+
},
|
|
38489
|
+
"name": "Sheet",
|
|
38490
|
+
"summary": "Present supplemental application content in a controlled native sheet.",
|
|
38491
|
+
"tier": "shared"
|
|
38492
|
+
},
|
|
38493
|
+
{
|
|
38494
|
+
"accessibility": "The trigger exposes expanded state and each native menu action reports its label, disabled state, and destructive role.",
|
|
38495
|
+
"category": "Actions",
|
|
38496
|
+
"contract": "Anchored action menu with labeled disabled and destructive item states",
|
|
38497
|
+
"guidance": "Use for a short set of contextual actions. Keep labels unique and concise, and do not hide a screen's only primary action inside a menu.",
|
|
38498
|
+
"id": "menu",
|
|
38499
|
+
"implementations": {
|
|
38500
|
+
"compose": {
|
|
38501
|
+
"api": [
|
|
38502
|
+
{
|
|
38503
|
+
"defaultValue": "Required",
|
|
38504
|
+
"description": "Provides labeled actions and states.",
|
|
38505
|
+
"name": "items",
|
|
38506
|
+
"values": "Native Lumen menu item collection"
|
|
38507
|
+
},
|
|
38508
|
+
{
|
|
38509
|
+
"defaultValue": "Collapsed",
|
|
38510
|
+
"description": "Controls or reports native menu presentation.",
|
|
38511
|
+
"name": "expanded",
|
|
38512
|
+
"values": "Boolean"
|
|
38513
|
+
},
|
|
38514
|
+
{
|
|
38515
|
+
"defaultValue": "false",
|
|
38516
|
+
"description": "Keeps an unavailable item readable but inactive.",
|
|
38517
|
+
"name": "disabled",
|
|
38518
|
+
"values": "Boolean"
|
|
38519
|
+
},
|
|
38520
|
+
{
|
|
38521
|
+
"defaultValue": "false / nil",
|
|
38522
|
+
"description": "Marks a destructive action semantically and visually.",
|
|
38523
|
+
"name": "destructive / role",
|
|
38524
|
+
"values": "Destructive action role"
|
|
38525
|
+
}
|
|
38526
|
+
],
|
|
38527
|
+
"example": "LumenMenu(\n expanded = showMenu,\n onDismissRequest = { showMenu = false },\n items = actions\n)",
|
|
38528
|
+
"exportName": "LumenMenu",
|
|
38529
|
+
"importStatement": "import com.santi020k.lumen.LumenMenu",
|
|
38530
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
38531
|
+
"language": "kotlin",
|
|
38532
|
+
"packageName": "com.santi020k:lumen-compose",
|
|
38533
|
+
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
38534
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/OverlayComponents.kt",
|
|
38535
|
+
"symbol": "LumenMenu"
|
|
38536
|
+
},
|
|
38537
|
+
"react-native": {
|
|
38538
|
+
"api": [
|
|
38539
|
+
{
|
|
38540
|
+
"defaultValue": "Required",
|
|
38541
|
+
"description": "Provides labeled actions and states.",
|
|
38542
|
+
"name": "items",
|
|
38543
|
+
"values": "Native Lumen menu item collection"
|
|
38544
|
+
},
|
|
38545
|
+
{
|
|
38546
|
+
"defaultValue": "Collapsed",
|
|
38547
|
+
"description": "Controls or reports native menu presentation.",
|
|
38548
|
+
"name": "Internal trigger state",
|
|
38549
|
+
"values": "Native Modal"
|
|
38550
|
+
},
|
|
38551
|
+
{
|
|
38552
|
+
"defaultValue": "false",
|
|
38553
|
+
"description": "Keeps an unavailable item readable but inactive.",
|
|
38554
|
+
"name": "disabled",
|
|
38555
|
+
"values": "Boolean"
|
|
38556
|
+
},
|
|
38557
|
+
{
|
|
38558
|
+
"defaultValue": "false / nil",
|
|
38559
|
+
"description": "Marks a destructive action semantically and visually.",
|
|
38560
|
+
"name": "destructive / role",
|
|
38561
|
+
"values": "Destructive action role"
|
|
38562
|
+
}
|
|
38563
|
+
],
|
|
38564
|
+
"example": "<LumenMenu\n accessibilityLabel=\"More actions\"\n trigger={<MoreIcon />}\n items={actions}\n/>",
|
|
38565
|
+
"exportName": "LumenMenu",
|
|
38566
|
+
"importStatement": "import { LumenMenu } from '@santi020k/lumen-react-native'",
|
|
38567
|
+
"install": "pnpm add @santi020k/lumen-react-native",
|
|
38568
|
+
"language": "tsx",
|
|
38569
|
+
"packageName": "@santi020k/lumen-react-native",
|
|
38570
|
+
"setup": "Mount one LumenProvider near the application root. Native adapters do not load CSS or the Astro runtime.",
|
|
38571
|
+
"sourceFile": "packages/react-native/src/index.ts",
|
|
38572
|
+
"symbol": "LumenMenu"
|
|
38573
|
+
},
|
|
38574
|
+
"swiftui": {
|
|
38575
|
+
"api": [
|
|
38576
|
+
{
|
|
38577
|
+
"defaultValue": "Required",
|
|
38578
|
+
"description": "Provides labeled actions and states.",
|
|
38579
|
+
"name": "items",
|
|
38580
|
+
"values": "Native Lumen menu item collection"
|
|
38581
|
+
},
|
|
38582
|
+
{
|
|
38583
|
+
"defaultValue": "Collapsed",
|
|
38584
|
+
"description": "Controls or reports native menu presentation.",
|
|
38585
|
+
"name": "Native Menu state",
|
|
38586
|
+
"values": "Native"
|
|
38587
|
+
},
|
|
38588
|
+
{
|
|
38589
|
+
"defaultValue": "false",
|
|
38590
|
+
"description": "Keeps an unavailable item readable but inactive.",
|
|
38591
|
+
"name": "disabled",
|
|
38592
|
+
"values": "Boolean"
|
|
38593
|
+
},
|
|
38594
|
+
{
|
|
38595
|
+
"defaultValue": "false / nil",
|
|
38596
|
+
"description": "Marks a destructive action semantically and visually.",
|
|
38597
|
+
"name": "destructive / role",
|
|
38598
|
+
"values": "Destructive action role"
|
|
38599
|
+
}
|
|
38600
|
+
],
|
|
38601
|
+
"example": "LumenMenu(items: actions) {\n Label(\"More\", systemImage: \"ellipsis\")\n}",
|
|
38602
|
+
"exportName": "LumenMenu",
|
|
38603
|
+
"importStatement": "import LumenUI",
|
|
38604
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
38605
|
+
"language": "swift",
|
|
38606
|
+
"packageName": "LumenUI",
|
|
38607
|
+
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
38608
|
+
"sourceFile": "packages/swift/Sources/LumenUI/OverlayComponents.swift",
|
|
38609
|
+
"symbol": "LumenMenu"
|
|
38610
|
+
}
|
|
38611
|
+
},
|
|
38612
|
+
"name": "Menu",
|
|
38613
|
+
"summary": "Present a native anchored action menu with shared item-state semantics.",
|
|
38614
|
+
"tier": "shared"
|
|
38615
|
+
},
|
|
38616
|
+
{
|
|
38617
|
+
"accessibility": "The trigger remains a native labeled button and destination selection is delegated to the operating system share surface.",
|
|
38618
|
+
"category": "Actions",
|
|
38619
|
+
"contract": "Token-aware button that presents the operating system share surface for application-owned content",
|
|
38620
|
+
"guidance": "Use to share application-owned text, URLs, or files through the operating system. Keep content generation, file permissions, completion feedback, and error handling in the application.",
|
|
38621
|
+
"id": "share-button",
|
|
38622
|
+
"implementations": {
|
|
38623
|
+
"compose": {
|
|
38624
|
+
"api": [
|
|
38625
|
+
{
|
|
38626
|
+
"defaultValue": "Required",
|
|
38627
|
+
"description": "Supplies application-owned share content.",
|
|
38628
|
+
"name": "payload",
|
|
38629
|
+
"values": "LumenSharePayload"
|
|
38630
|
+
},
|
|
38631
|
+
{
|
|
38632
|
+
"defaultValue": "Required",
|
|
38633
|
+
"description": "Configures the native share presentation.",
|
|
38634
|
+
"name": "chooserTitle",
|
|
38635
|
+
"values": "String"
|
|
38636
|
+
},
|
|
38637
|
+
{
|
|
38638
|
+
"defaultValue": "Share",
|
|
38639
|
+
"description": "Names the trigger action.",
|
|
38640
|
+
"name": "label",
|
|
38641
|
+
"values": "Native localized text"
|
|
38642
|
+
},
|
|
38643
|
+
{
|
|
38644
|
+
"defaultValue": "No-op",
|
|
38645
|
+
"description": "Lets the application report sharing outcomes where the platform exposes them.",
|
|
38646
|
+
"name": "onFailure",
|
|
38647
|
+
"values": "Platform callback"
|
|
38648
|
+
}
|
|
38649
|
+
],
|
|
38650
|
+
"example": "LumenShareButton(\n payload = LumenSharePayload(text = reportText),\n chooserTitle = \"Share report\"\n)",
|
|
38651
|
+
"exportName": "LumenShareButton",
|
|
38652
|
+
"importStatement": "import com.santi020k.lumen.LumenShareButton",
|
|
38653
|
+
"install": "Include packages/compose from a shallow Git checkout pinned to the Lumen release tag, then add implementation(project(\":lumen-compose\")).",
|
|
38654
|
+
"language": "kotlin",
|
|
38655
|
+
"packageName": "com.santi020k:lumen-compose",
|
|
38656
|
+
"setup": "Wrap application content in LumenTheme and preserve Compose state and navigation.",
|
|
38657
|
+
"sourceFile": "packages/compose/src/main/kotlin/com/santi020k/lumen/OverlayComponents.kt",
|
|
38658
|
+
"symbol": "LumenShareButton"
|
|
38659
|
+
},
|
|
38660
|
+
"react-native": {
|
|
38661
|
+
"api": [
|
|
38662
|
+
{
|
|
38663
|
+
"defaultValue": "Required",
|
|
38664
|
+
"description": "Supplies application-owned share content.",
|
|
38665
|
+
"name": "content",
|
|
38666
|
+
"values": "ShareContent"
|
|
38667
|
+
},
|
|
38668
|
+
{
|
|
38669
|
+
"defaultValue": "undefined",
|
|
38670
|
+
"description": "Configures the native share presentation.",
|
|
38671
|
+
"name": "options",
|
|
38672
|
+
"values": "ShareOptions"
|
|
38673
|
+
},
|
|
38674
|
+
{
|
|
38675
|
+
"defaultValue": "Share",
|
|
38676
|
+
"description": "Names the trigger action.",
|
|
38677
|
+
"name": "label",
|
|
38678
|
+
"values": "Native localized text"
|
|
38679
|
+
},
|
|
38680
|
+
{
|
|
38681
|
+
"defaultValue": "No-op",
|
|
38682
|
+
"description": "Lets the application report sharing outcomes where the platform exposes them.",
|
|
38683
|
+
"name": "onError / onShared",
|
|
38684
|
+
"values": "Platform callback"
|
|
38685
|
+
}
|
|
38686
|
+
],
|
|
38687
|
+
"example": "<LumenShareButton\n label=\"Share report\"\n content={{ message: reportText }}\n/>",
|
|
38688
|
+
"exportName": "LumenShareButton",
|
|
38689
|
+
"importStatement": "import { LumenShareButton } from '@santi020k/lumen-react-native'",
|
|
38690
|
+
"install": "pnpm add @santi020k/lumen-react-native",
|
|
38691
|
+
"language": "tsx",
|
|
38692
|
+
"packageName": "@santi020k/lumen-react-native",
|
|
38693
|
+
"setup": "Mount one LumenProvider near the application root. Native adapters do not load CSS or the Astro runtime.",
|
|
38694
|
+
"sourceFile": "packages/react-native/src/index.ts",
|
|
38695
|
+
"symbol": "LumenShareButton"
|
|
38696
|
+
},
|
|
38697
|
+
"swiftui": {
|
|
38698
|
+
"api": [
|
|
38699
|
+
{
|
|
38700
|
+
"defaultValue": "Required",
|
|
38701
|
+
"description": "Supplies application-owned share content.",
|
|
38702
|
+
"name": "item",
|
|
38703
|
+
"values": "Transferable"
|
|
38704
|
+
},
|
|
38705
|
+
{
|
|
38706
|
+
"defaultValue": "System-provided",
|
|
38707
|
+
"description": "Configures the native share presentation.",
|
|
38708
|
+
"name": "Native share title",
|
|
38709
|
+
"values": "System-provided"
|
|
38710
|
+
},
|
|
38711
|
+
{
|
|
38712
|
+
"defaultValue": "Share",
|
|
38713
|
+
"description": "Names the trigger action.",
|
|
38714
|
+
"name": "label",
|
|
38715
|
+
"values": "Native localized text"
|
|
38716
|
+
},
|
|
38717
|
+
{
|
|
38718
|
+
"defaultValue": "No-op",
|
|
38719
|
+
"description": "Lets the application report sharing outcomes where the platform exposes them.",
|
|
38720
|
+
"name": "Native ShareLink result",
|
|
38721
|
+
"values": "Platform callback"
|
|
38722
|
+
}
|
|
38723
|
+
],
|
|
38724
|
+
"example": "LumenShareButton(\"Share report\", item: reportText)",
|
|
38725
|
+
"exportName": "LumenShareButton",
|
|
38726
|
+
"importStatement": "import LumenUI",
|
|
38727
|
+
"install": "Add https://github.com/santi020k/lumen with Swift Package Manager, pin an exact or compatible release version, and link the LumenUI product to the application target.",
|
|
38728
|
+
"language": "swift",
|
|
38729
|
+
"packageName": "LumenUI",
|
|
38730
|
+
"setup": "Apply .lumenTheme(...) near the SwiftUI application root and preserve native environment behavior.",
|
|
38731
|
+
"sourceFile": "packages/swift/Sources/LumenUI/OverlayComponents.swift",
|
|
38732
|
+
"symbol": "LumenShareButton"
|
|
38733
|
+
}
|
|
38734
|
+
},
|
|
38735
|
+
"name": "Share button",
|
|
38736
|
+
"summary": "Open the operating system share surface from a token-aware native action.",
|
|
38737
|
+
"tier": "shared"
|
|
36802
38738
|
}
|
|
36803
38739
|
],
|
|
36804
38740
|
"nativeSources": {
|
|
36805
38741
|
"compose": {
|
|
36806
38742
|
"packages/compose/src/main/kotlin/com/santi020k/lumen/AdditionalComponents.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.BorderStroke\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.RowScope\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.heightIn\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.DropdownMenu\nimport androidx.compose.material3.DropdownMenuItem\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedTextField\nimport androidx.compose.material3.OutlinedTextFieldDefaults\nimport androidx.compose.material3.Slider\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.semantics.LiveRegionMode\nimport androidx.compose.ui.semantics.Role\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.liveRegion\nimport androidx.compose.ui.semantics.progressBarRangeInfo\nimport androidx.compose.ui.semantics.ProgressBarRangeInfo\nimport androidx.compose.ui.semantics.selected\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.semantics.stateDescription\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.dp\n\nenum class LumenButtonGroupOrientation {\n Horizontal,\n Vertical\n}\n\n@Composable\nfun LumenButtonGroup(\n modifier: Modifier = Modifier,\n orientation: LumenButtonGroupOrientation = LumenButtonGroupOrientation.Horizontal,\n content: @Composable () -> Unit\n) {\n if (orientation == LumenButtonGroupOrientation.Horizontal) {\n Row(\n modifier = modifier,\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Sm),\n verticalAlignment = Alignment.CenterVertically\n ) { content() }\n } else {\n Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(LumenSpacing.Sm)) {\n content()\n }\n }\n}\n\n@Composable\nfun LumenFieldGroup(\n label: String,\n modifier: Modifier = Modifier,\n description: String? = null,\n errorMessage: String? = null,\n required: Boolean = false,\n content: @Composable () -> Unit\n) {\n val colors = LocalLumenTheme.current.colors\n\n Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(LumenSpacing.Sm)) {\n Text(\n text = if (required) \"$label *\" else label,\n color = colors.ink,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n if (description != null) {\n Text(description, color = colors.inkMuted, style = MaterialTheme.typography.bodySmall)\n }\n content()\n if (errorMessage != null) {\n Text(\n errorMessage,\n modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite },\n color = colors.danger,\n style = MaterialTheme.typography.bodySmall\n )\n }\n }\n}\n\n@Composable\nfun LumenTextarea(\n value: String,\n onValueChange: (String) -> Unit,\n label: String,\n modifier: Modifier = Modifier,\n description: String? = null,\n errorMessage: String? = null,\n enabled: Boolean = true,\n minLines: Int = 4\n) {\n val colors = LocalLumenTheme.current.colors\n\n OutlinedTextField(\n value = value,\n onValueChange = onValueChange,\n modifier = modifier.fillMaxWidth(),\n enabled = enabled,\n isError = errorMessage != null,\n minLines = minLines.coerceAtLeast(2),\n label = { Text(label) },\n supportingText = {\n val message = errorMessage ?: description\n if (message != null) Text(message)\n },\n shape = RoundedCornerShape(LumenRadius.Sm),\n colors = OutlinedTextFieldDefaults.colors(\n focusedContainerColor = colors.surface,\n unfocusedContainerColor = colors.surface,\n disabledContainerColor = colors.surface,\n focusedTextColor = colors.ink,\n unfocusedTextColor = colors.ink,\n focusedBorderColor = colors.brand,\n unfocusedBorderColor = colors.line,\n errorBorderColor = colors.danger,\n cursorColor = colors.brand\n )\n )\n}\n\n@Composable\nfun LumenChip(\n label: String,\n modifier: Modifier = Modifier,\n selected: Boolean = false,\n enabled: Boolean = true,\n removeLabel: String = \"Remove $label\",\n onClick: (() -> Unit)? = null,\n onRemove: (() -> Unit)? = null\n) {\n val colors = LocalLumenTheme.current.colors\n val interactionModifier = if (onClick != null) {\n Modifier.clickable(enabled = enabled, role = Role.Button, onClick = onClick)\n } else {\n Modifier\n }\n\n Surface(\n modifier = modifier\n .then(interactionModifier)\n .semantics { this.selected = selected },\n color = if (selected) colors.brandSoft else colors.surfaceMuted,\n contentColor = if (selected) colors.brand else colors.inkSoft,\n shape = RoundedCornerShape(LumenRadius.Full),\n border = BorderStroke(1.dp, if (selected) colors.brand else colors.line)\n ) {\n Row(\n modifier = Modifier\n .heightIn(min = 32.dp)\n .padding(horizontal = LumenSpacing.Md),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Xs),\n verticalAlignment = Alignment.CenterVertically\n ) {\n Text(label, style = MaterialTheme.typography.labelSmall.copy(fontWeight = FontWeight.SemiBold))\n if (onRemove != null) {\n TextButton(enabled = enabled, onClick = onRemove) {\n Text(\"×\", modifier = Modifier.semantics { contentDescription = removeLabel })\n }\n }\n }\n }\n}\n\n@Composable\nfun LumenToast(\n title: String,\n modifier: Modifier = Modifier,\n description: String? = null,\n variant: LumenBannerVariant = LumenBannerVariant.Default,\n dismissLabel: String = \"Dismiss\",\n onDismiss: (() -> Unit)? = null,\n action: (@Composable RowScope.() -> Unit)? = null\n) {\n val colors = LocalLumenTheme.current.colors\n val palette = lumenBannerPalette(colors, variant)\n\n Surface(\n modifier = modifier.semantics { liveRegion = LiveRegionMode.Polite },\n color = palette.background,\n contentColor = colors.ink,\n shape = RoundedCornerShape(LumenRadius.Md),\n border = BorderStroke(1.dp, palette.border)\n ) {\n Row(\n modifier = Modifier.padding(horizontal = LumenSpacing.Lg, vertical = LumenSpacing.Md),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Md),\n verticalAlignment = Alignment.CenterVertically\n ) {\n Column(\n modifier = Modifier.weight(1f),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)\n ) {\n Text(\n title,\n color = palette.accent,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n if (description != null) {\n Text(description, color = colors.inkSoft, style = MaterialTheme.typography.bodySmall)\n }\n }\n action?.invoke(this)\n if (onDismiss != null) {\n TextButton(onClick = onDismiss) { Text(dismissLabel, color = palette.accent) }\n }\n }\n }\n}\n\ndata class LumenPickerOption<T>(\n val value: T,\n val label: String,\n val enabled: Boolean = true\n)\n\n@Composable\nfun <T> LumenPicker(\n label: String,\n value: T,\n options: List<LumenPickerOption<T>>,\n onValueChange: (T) -> Unit,\n modifier: Modifier = Modifier,\n enabled: Boolean = true\n) {\n var expanded by remember { mutableStateOf(false) }\n val selectedLabel = options.firstOrNull { it.value == value }?.label ?: value.toString()\n\n Box(modifier = modifier) {\n Button(\n onClick = { expanded = true },\n enabled = enabled,\n modifier = Modifier.semantics {\n contentDescription = label\n stateDescription = selectedLabel\n }\n ) {\n Text(selectedLabel)\n }\n DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {\n options.forEach { option ->\n DropdownMenuItem(\n text = { Text(option.label) },\n enabled = option.enabled,\n onClick = {\n onValueChange(option.value)\n expanded = false\n }\n )\n }\n }\n }\n}\n\n@Composable\nfun LumenSlider(\n label: String,\n value: Float,\n onValueChange: (Float) -> Unit,\n modifier: Modifier = Modifier,\n valueRange: ClosedFloatingPointRange<Float> = 0f..1f,\n steps: Int = 0,\n valueLabel: String = value.toString(),\n enabled: Boolean = true\n) {\n Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)) {\n Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {\n Text(label)\n Text(valueLabel, color = LocalLumenTheme.current.colors.inkMuted)\n }\n Slider(\n value = value.coerceIn(valueRange.start, valueRange.endInclusive),\n onValueChange = onValueChange,\n enabled = enabled,\n valueRange = valueRange,\n steps = steps.coerceAtLeast(0),\n modifier = Modifier.semantics { stateDescription = valueLabel }\n )\n }\n}\n\n@Composable\nfun LumenGauge(\n label: String,\n value: Float,\n modifier: Modifier = Modifier,\n max: Float = 100f,\n valueLabel: String = value.toString(),\n tone: LumenMetricTone = LumenMetricTone.Brand,\n center: (@Composable () -> Unit)? = null\n) {\n val progress = LumenProgressValue.resolve(value, max)\n val colors = LocalLumenTheme.current.colors\n\n Box(\n modifier = modifier\n .size(72.dp)\n .semantics {\n contentDescription = label\n progressBarRangeInfo = ProgressBarRangeInfo(\n current = progress.value,\n range = 0f..progress.max,\n steps = 0\n )\n stateDescription = valueLabel\n },\n contentAlignment = Alignment.Center\n ) {\n CircularProgressIndicator(\n progress = { progress.value / progress.max },\n modifier = Modifier.size(72.dp),\n color = lumenMetricColor(colors, tone),\n trackColor = colors.surfaceStrong\n )\n if (center != null) center() else Text(valueLabel, style = MaterialTheme.typography.labelSmall)\n }\n}\n",
|
|
36807
|
-
"packages/compose/src/main/kotlin/com/santi020k/lumen/ContentComponents.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.heightIn\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.semantics.ProgressBarRangeInfo\nimport androidx.compose.ui.semantics.Role\nimport androidx.compose.ui.semantics.clearAndSetSemantics\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.progressBarRangeInfo\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.semantics.stateDescription\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\n\nenum class LumenSkeletonShape {\n Circle,\n Rectangle,\n Text\n}\n\nfun resolveLumenSkeletonHeight(height: Dp): Dp = if (height.value.isFinite() && height > 0.dp) {\n height\n} else {\n 16.dp\n}\n\n@Composable\nfun LumenSkeleton(\n modifier: Modifier = Modifier,\n width: Dp? = null,\n height: Dp = 16.dp,\n shape: LumenSkeletonShape = LumenSkeletonShape.Text,\n label: String? = null\n) {\n val colors = LocalLumenTheme.current.colors\n val safeHeight = resolveLumenSkeletonHeight(height)\n val shapeModifier = when {\n shape == LumenSkeletonShape.Circle -> Modifier.size(safeHeight).clip(CircleShape)\n width != null -> Modifier.width(width).height(safeHeight)\n else -> Modifier.fillMaxWidth().height(safeHeight)\n }\n val cornerShape = when (shape) {\n LumenSkeletonShape.Circle -> CircleShape\n LumenSkeletonShape.Rectangle -> RoundedCornerShape(LumenRadius.Md)\n LumenSkeletonShape.Text -> RoundedCornerShape(LumenRadius.Sm)\n }\n val semanticsModifier = if (label == null) {\n Modifier.clearAndSetSemantics {}\n } else {\n Modifier.semantics {\n contentDescription = label\n progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate\n }\n }\n\n Box(\n modifier = modifier\n .then(shapeModifier)\n .then(semanticsModifier)\n .clip(cornerShape)\n .background(colors.surfaceStrong.copy(alpha = 0.72f))\n )\n}\n\n@Composable\nfun LumenDisclosure(\n title: String,\n expanded: Boolean,\n onExpandedChange: (Boolean) -> Unit,\n modifier: Modifier = Modifier,\n description: String? = null,\n enabled: Boolean = true,\n graphic: (@Composable () -> Unit)? = null,\n content: @Composable () -> Unit\n) {\n val colors = LocalLumenTheme.current.colors\n\n Surface(\n modifier = modifier,\n color = colors.surface,\n contentColor = colors.ink,\n shape = RoundedCornerShape(LumenRadius.Md),\n border = androidx.compose.foundation.BorderStroke(1.dp, colors.line)\n ) {\n Column {\n Row(\n modifier = Modifier\n .fillMaxWidth()\n .heightIn(min = 48.dp)\n .clickable(\n enabled = enabled,\n role = Role.Button,\n onClick = { onExpandedChange(!expanded) }\n )\n .semantics(mergeDescendants = true) {\n contentDescription = title\n stateDescription = if (expanded) \"Expanded\" else \"Collapsed\"\n }\n .padding(horizontal = LumenSpacing.Lg, vertical = LumenSpacing.Md),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Md),\n verticalAlignment = Alignment.CenterVertically\n ) {\n graphic?.invoke()\n Column(\n modifier = Modifier.weight(1f),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)\n ) {\n Text(\n text = title,\n color = colors.ink,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n if (description != null) {\n Text(text = description, color = colors.inkMuted, style = MaterialTheme.typography.bodySmall)\n }\n }\n Text(\n text = if (expanded) \"−\" else \"+\",\n modifier = Modifier.clearAndSetSemantics {},\n color = colors.inkSoft,\n style = MaterialTheme.typography.titleMedium\n )\n }\n if (expanded) {\n LumenDivider()\n Box(\n modifier = Modifier\n .fillMaxWidth()\n .padding(LumenSpacing.Lg)\n ) {\n content()\n }\n }\n }\n }\n}\n",
|
|
38743
|
+
"packages/compose/src/main/kotlin/com/santi020k/lumen/ContentComponents.kt": "// cspell:ignore drawscope\npackage com.santi020k.lumen\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.Canvas\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.heightIn\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.CornerRadius\nimport androidx.compose.ui.geometry.Size\nimport androidx.compose.ui.graphics.Brush\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.RectangleShape\nimport androidx.compose.ui.graphics.StrokeCap\nimport androidx.compose.ui.graphics.StrokeJoin\nimport androidx.compose.ui.graphics.drawscope.Stroke\nimport androidx.compose.ui.semantics.ProgressBarRangeInfo\nimport androidx.compose.ui.semantics.Role\nimport androidx.compose.ui.semantics.clearAndSetSemantics\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.progressBarRangeInfo\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.semantics.stateDescription\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport kotlin.math.cos\nimport kotlin.math.hypot\nimport kotlin.math.sin\n\nenum class LumenSkeletonShape {\n Circle,\n Rectangle,\n Text\n}\n\nfun resolveLumenSkeletonHeight(height: Dp): Dp = if (height.value.isFinite() && height > 0.dp) {\n height\n} else {\n 16.dp\n}\n\n@Composable\nfun LumenSkeleton(\n modifier: Modifier = Modifier,\n width: Dp? = null,\n height: Dp = 16.dp,\n shape: LumenSkeletonShape = LumenSkeletonShape.Text,\n label: String? = null\n) {\n val colors = LocalLumenTheme.current.colors\n val safeHeight = resolveLumenSkeletonHeight(height)\n val shapeModifier = when {\n shape == LumenSkeletonShape.Circle -> Modifier.size(safeHeight).clip(CircleShape)\n width != null -> Modifier.width(width).height(safeHeight)\n else -> Modifier.fillMaxWidth().height(safeHeight)\n }\n val cornerShape = when (shape) {\n LumenSkeletonShape.Circle -> CircleShape\n LumenSkeletonShape.Rectangle -> RoundedCornerShape(LumenRadius.Md)\n LumenSkeletonShape.Text -> RoundedCornerShape(LumenRadius.Sm)\n }\n val semanticsModifier = if (label == null) {\n Modifier.clearAndSetSemantics {}\n } else {\n Modifier.semantics {\n contentDescription = label\n progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate\n }\n }\n\n Box(\n modifier = modifier\n .then(shapeModifier)\n .then(semanticsModifier)\n .clip(cornerShape)\n .background(colors.surfaceStrong.copy(alpha = 0.72f))\n )\n}\n\nenum class LumenGraphicSize(val dimension: Dp) {\n Sm(160.dp),\n Md(240.dp),\n Lg(320.dp)\n}\n\nenum class LumenGraphicTone {\n Accent,\n Brand,\n Neutral\n}\n\nenum class LumenGraphicVariant {\n Glow,\n Grid,\n Orbit\n}\n\n@Composable\nfun LumenGraphic(\n modifier: Modifier = Modifier,\n label: String? = null,\n size: LumenGraphicSize = LumenGraphicSize.Md,\n tone: LumenGraphicTone = LumenGraphicTone.Brand,\n variant: LumenGraphicVariant = LumenGraphicVariant.Orbit,\n content: @Composable BoxScope.() -> Unit\n) {\n val colors = LocalLumenTheme.current.colors\n val color = when (tone) {\n LumenGraphicTone.Accent -> colors.accent\n LumenGraphicTone.Brand -> colors.brand\n LumenGraphicTone.Neutral -> colors.inkMuted\n }\n val semanticsModifier = Modifier.clearAndSetSemantics {\n if (label != null) {\n contentDescription = label\n }\n }\n\n Box(\n modifier = modifier\n .size(size.dimension)\n .then(semanticsModifier),\n contentAlignment = Alignment.Center\n ) {\n Canvas(modifier = Modifier.matchParentSize()) {\n when (variant) {\n LumenGraphicVariant.Glow -> drawCircle(\n brush = Brush.radialGradient(\n colors = listOf(color.copy(alpha = 0.3f), color.copy(alpha = 0f)),\n center = center,\n radius = this.size.minDimension * 0.42f\n ),\n radius = this.size.minDimension * 0.42f\n )\n LumenGraphicVariant.Grid -> {\n for (index in 0..6) {\n val position = this.size.minDimension * (0.12f + (index * 0.76f / 6))\n drawLine(\n color = color.copy(alpha = 0.18f),\n start = Offset(position, this.size.height * 0.12f),\n end = Offset(position, this.size.height * 0.88f),\n strokeWidth = 1.dp.toPx()\n )\n drawLine(\n color = color.copy(alpha = 0.18f),\n start = Offset(this.size.width * 0.12f, position),\n end = Offset(this.size.width * 0.88f, position),\n strokeWidth = 1.dp.toPx()\n )\n }\n }\n LumenGraphicVariant.Orbit -> {\n for (ratio in listOf(0.9f, 0.62f, 0.34f)) {\n drawCircle(\n color = color.copy(alpha = 0.24f),\n radius = this.size.minDimension * ratio / 2,\n style = Stroke(width = 1.dp.toPx())\n )\n }\n drawLine(\n color = color.copy(alpha = 0.18f),\n start = Offset(this.size.width * 0.14f, center.y),\n end = Offset(this.size.width * 0.86f, center.y),\n strokeWidth = 1.dp.toPx()\n )\n drawLine(\n color = color.copy(alpha = 0.18f),\n start = Offset(center.x, this.size.height * 0.14f),\n end = Offset(center.x, this.size.height * 0.86f),\n strokeWidth = 1.dp.toPx()\n )\n }\n }\n }\n content()\n }\n}\n\nenum class LumenBackdropIntensity(val opacity: Float) {\n Subtle(0.4f),\n Medium(0.68f),\n Strong(1f)\n}\n\nenum class LumenBackdropTone {\n Accent,\n Brand,\n Neutral\n}\n\nenum class LumenBackdropVariant {\n Aurora,\n Dots,\n Grid,\n Rays\n}\n\n@Composable\nfun LumenBackdrop(\n modifier: Modifier = Modifier,\n intensity: LumenBackdropIntensity = LumenBackdropIntensity.Medium,\n tone: LumenBackdropTone = LumenBackdropTone.Brand,\n variant: LumenBackdropVariant = LumenBackdropVariant.Aurora,\n content: @Composable BoxScope.() -> Unit\n) {\n val colors = LocalLumenTheme.current.colors\n val color = when (tone) {\n LumenBackdropTone.Accent -> colors.accent\n LumenBackdropTone.Brand -> colors.brand\n LumenBackdropTone.Neutral -> colors.inkMuted\n }\n val secondaryColor = if (tone == LumenBackdropTone.Neutral) colors.surfaceStrong else colors.accent\n\n Box(modifier = modifier.heightIn(min = 160.dp).clip(RectangleShape)) {\n Canvas(modifier = Modifier.matchParentSize()) {\n when (variant) {\n LumenBackdropVariant.Aurora -> {\n drawCircle(\n color = color.copy(alpha = 0.22f * intensity.opacity),\n radius = size.width * 0.36f,\n center = Offset(size.width * 0.12f, size.height * 0.04f)\n )\n drawCircle(\n color = secondaryColor.copy(alpha = 0.18f * intensity.opacity),\n radius = size.width * 0.31f,\n center = Offset(size.width * 0.9f, size.height * 0.92f)\n )\n }\n LumenBackdropVariant.Dots -> {\n for (row in 0 until 6) {\n for (column in 0 until 8) {\n drawCircle(\n color = color.copy(alpha = 0.28f * intensity.opacity),\n radius = 1.5.dp.toPx(),\n center = Offset(\n (column + 0.5f) * size.width / 8,\n (row + 0.5f) * size.height / 6\n )\n )\n }\n }\n }\n LumenBackdropVariant.Grid -> {\n for (index in 0..6) {\n val x = index * size.width / 6\n val y = index * size.height / 6\n val lineColor = color.copy(alpha = 0.18f * intensity.opacity)\n drawLine(lineColor, Offset(x, 0f), Offset(x, size.height), 1.dp.toPx())\n drawLine(lineColor, Offset(0f, y), Offset(size.width, y), 1.dp.toPx())\n }\n }\n LumenBackdropVariant.Rays -> {\n val radius = hypot(size.width, size.height)\n for (index in 0 until 12) {\n val angle = index * Math.PI.toFloat() / 6\n drawLine(\n color = color.copy(alpha = 0.2f * intensity.opacity),\n start = center,\n end = Offset(\n center.x + cos(angle) * radius,\n center.y + sin(angle) * radius\n ),\n strokeWidth = 1.dp.toPx()\n )\n }\n }\n }\n }\n content()\n }\n}\n\nenum class LumenIllustrationSize(val dimension: Dp) {\n Sm(96.dp),\n Md(128.dp),\n Lg(176.dp)\n}\n\nenum class LumenIllustrationTone {\n Accent,\n Auto,\n Brand,\n Neutral\n}\n\nenum class LumenIllustrationVariant {\n Empty,\n Error,\n Offline,\n Success\n}\n\ninternal fun lumenIllustrationColor(\n colors: LumenColorPalette,\n tone: LumenIllustrationTone,\n variant: LumenIllustrationVariant\n): Color = when (tone) {\n LumenIllustrationTone.Accent -> colors.accent\n LumenIllustrationTone.Brand -> colors.brand\n LumenIllustrationTone.Neutral -> colors.inkMuted\n LumenIllustrationTone.Auto -> when (variant) {\n LumenIllustrationVariant.Empty -> colors.brand\n LumenIllustrationVariant.Error -> colors.danger\n LumenIllustrationVariant.Offline -> colors.inkMuted\n LumenIllustrationVariant.Success -> colors.success\n }\n}\n\n@Composable\nfun LumenIllustration(\n modifier: Modifier = Modifier,\n variant: LumenIllustrationVariant = LumenIllustrationVariant.Empty,\n tone: LumenIllustrationTone = LumenIllustrationTone.Auto,\n size: LumenIllustrationSize = LumenIllustrationSize.Md,\n label: String? = null\n) {\n val colors = LocalLumenTheme.current.colors\n val color = lumenIllustrationColor(colors, tone, variant)\n val semanticsModifier = if (label == null) {\n Modifier.clearAndSetSemantics {}\n } else {\n Modifier.semantics { contentDescription = label }\n }\n\n Canvas(modifier = modifier.size(size.dimension).then(semanticsModifier)) {\n val scale = this.size.minDimension / 120f\n val strokeWidth = maxOf(2.dp.toPx(), 2.5f * scale)\n val stroke = Stroke(width = strokeWidth, cap = StrokeCap.Round, join = StrokeJoin.Round)\n drawCircle(color.copy(alpha = 0.1f), radius = this.size.minDimension * 0.41f)\n\n when (variant) {\n LumenIllustrationVariant.Empty -> {\n drawRoundRect(\n color = color,\n topLeft = Offset(29f * scale, 43f * scale),\n size = Size(62f * scale, 40f * scale),\n cornerRadius = CornerRadius(8f * scale),\n style = stroke\n )\n drawLine(color, Offset(40f * scale, 60f * scale), Offset(80f * scale, 60f * scale), strokeWidth)\n }\n LumenIllustrationVariant.Error, LumenIllustrationVariant.Success -> {\n drawCircle(color, radius = 30f * scale, style = stroke)\n if (variant == LumenIllustrationVariant.Success) {\n drawLine(color, Offset(44f * scale, 61f * scale), Offset(55f * scale, 72f * scale), strokeWidth, StrokeCap.Round)\n drawLine(color, Offset(55f * scale, 72f * scale), Offset(77f * scale, 48f * scale), strokeWidth, StrokeCap.Round)\n } else {\n drawLine(color, Offset(47f * scale, 47f * scale), Offset(73f * scale, 73f * scale), strokeWidth, StrokeCap.Round)\n drawLine(color, Offset(73f * scale, 47f * scale), Offset(47f * scale, 73f * scale), strokeWidth, StrokeCap.Round)\n }\n }\n LumenIllustrationVariant.Offline -> {\n drawRoundRect(\n color = color,\n topLeft = Offset(24f * scale, 45f * scale),\n size = Size(72f * scale, 35f * scale),\n cornerRadius = CornerRadius(18f * scale),\n style = stroke\n )\n drawLine(color, Offset(30f * scale, 30f * scale), Offset(90f * scale, 90f * scale), strokeWidth, StrokeCap.Round)\n }\n }\n }\n}\n\n@Composable\nfun LumenDisclosure(\n title: String,\n expanded: Boolean,\n onExpandedChange: (Boolean) -> Unit,\n modifier: Modifier = Modifier,\n description: String? = null,\n enabled: Boolean = true,\n graphic: (@Composable () -> Unit)? = null,\n content: @Composable () -> Unit\n) {\n val colors = LocalLumenTheme.current.colors\n\n Surface(\n modifier = modifier,\n color = colors.surface,\n contentColor = colors.ink,\n shape = RoundedCornerShape(LumenRadius.Md),\n border = androidx.compose.foundation.BorderStroke(1.dp, colors.line)\n ) {\n Column {\n Row(\n modifier = Modifier\n .fillMaxWidth()\n .heightIn(min = 48.dp)\n .clickable(\n enabled = enabled,\n role = Role.Button,\n onClick = { onExpandedChange(!expanded) }\n )\n .semantics(mergeDescendants = true) {\n contentDescription = title\n stateDescription = if (expanded) \"Expanded\" else \"Collapsed\"\n }\n .padding(horizontal = LumenSpacing.Lg, vertical = LumenSpacing.Md),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Md),\n verticalAlignment = Alignment.CenterVertically\n ) {\n graphic?.invoke()\n Column(\n modifier = Modifier.weight(1f),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)\n ) {\n Text(\n text = title,\n color = colors.ink,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n if (description != null) {\n Text(text = description, color = colors.inkMuted, style = MaterialTheme.typography.bodySmall)\n }\n }\n Text(\n text = if (expanded) \"−\" else \"+\",\n modifier = Modifier.clearAndSetSemantics {},\n color = colors.inkSoft,\n style = MaterialTheme.typography.titleMedium\n )\n }\n if (expanded) {\n LumenDivider()\n Box(\n modifier = Modifier\n .fillMaxWidth()\n .padding(LumenSpacing.Lg)\n ) {\n content()\n }\n }\n }\n }\n}\n",
|
|
36808
38744
|
"packages/compose/src/main/kotlin/com/santi020k/lumen/FormComponents.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.heightIn\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.selection.toggleable\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedTextField\nimport androidx.compose.material3.OutlinedTextFieldDefaults\nimport androidx.compose.material3.Switch\nimport androidx.compose.material3.SwitchDefaults\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.semantics.Role\nimport androidx.compose.ui.semantics.clearAndSetSemantics\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.dp\n\ndata class LumenSearchFieldState(val showClearAction: Boolean) {\n companion object {\n fun resolve(value: String, enabled: Boolean): LumenSearchFieldState = LumenSearchFieldState(\n showClearAction = enabled && value.isNotEmpty()\n )\n }\n}\n\n@Composable\nfun LumenToggle(\n label: String,\n checked: Boolean,\n onCheckedChange: (Boolean) -> Unit,\n modifier: Modifier = Modifier,\n description: String? = null,\n showLabel: Boolean = true,\n enabled: Boolean = true\n) {\n val colors = LocalLumenTheme.current.colors\n val widthModifier = if (showLabel) Modifier.fillMaxWidth() else Modifier\n\n Row(\n modifier = modifier\n .then(widthModifier)\n .heightIn(min = LumenButtonMetrics.resolve(LumenControlSize.Md).minHeight)\n .toggleable(\n value = checked,\n enabled = enabled,\n role = Role.Switch,\n onValueChange = onCheckedChange\n )\n .semantics(mergeDescendants = true) { contentDescription = label },\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Lg),\n verticalAlignment = Alignment.CenterVertically\n ) {\n if (showLabel) {\n Column(\n modifier = Modifier.weight(1f),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)\n ) {\n Text(\n text = label,\n color = colors.ink,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n if (description != null) {\n Text(text = description, color = colors.inkMuted, style = MaterialTheme.typography.bodySmall)\n }\n }\n }\n Switch(\n checked = checked,\n onCheckedChange = null,\n modifier = Modifier.clearAndSetSemantics {},\n enabled = enabled,\n colors = SwitchDefaults.colors(\n checkedThumbColor = colors.onBrand,\n checkedTrackColor = colors.brandSolid,\n uncheckedThumbColor = colors.surface,\n uncheckedTrackColor = colors.surfaceStrong,\n uncheckedBorderColor = colors.line\n )\n )\n }\n}\n\n@Composable\nfun LumenSettingsRow(\n title: String,\n modifier: Modifier = Modifier,\n description: String? = null,\n graphic: (@Composable () -> Unit)? = null,\n control: @Composable () -> Unit\n) {\n val colors = LocalLumenTheme.current.colors\n\n Row(\n modifier = modifier\n .fillMaxWidth()\n .heightIn(min = 56.dp)\n .padding(vertical = LumenSpacing.Sm),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Lg),\n verticalAlignment = Alignment.CenterVertically\n ) {\n graphic?.invoke()\n Column(\n modifier = Modifier.weight(1f),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)\n ) {\n Text(\n text = title,\n color = colors.ink,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n if (description != null) {\n Text(text = description, color = colors.inkMuted, style = MaterialTheme.typography.bodySmall)\n }\n }\n control()\n }\n}\n\n@Composable\nfun LumenSearchField(\n value: String,\n onValueChange: (String) -> Unit,\n modifier: Modifier = Modifier,\n prompt: String = \"Search\",\n clearLabel: String = \"Clear search\",\n enabled: Boolean = true,\n graphic: (@Composable () -> Unit)? = null\n) {\n val colors = LocalLumenTheme.current.colors\n val searchState = LumenSearchFieldState.resolve(value, enabled)\n val trailingIcon: (@Composable () -> Unit)? = if (searchState.showClearAction) {\n {\n TextButton(onClick = { onValueChange(\"\") }) {\n Text(clearLabel, color = colors.inkSoft, style = MaterialTheme.typography.labelSmall)\n }\n }\n } else {\n null\n }\n\n OutlinedTextField(\n value = value,\n onValueChange = onValueChange,\n modifier = modifier\n .fillMaxWidth()\n .heightIn(min = LumenButtonMetrics.resolve(LumenControlSize.Md).minHeight),\n enabled = enabled,\n singleLine = true,\n label = { Text(prompt) },\n leadingIcon = graphic,\n trailingIcon = trailingIcon,\n shape = RoundedCornerShape(LumenRadius.Sm),\n colors = OutlinedTextFieldDefaults.colors(\n focusedContainerColor = colors.surface,\n unfocusedContainerColor = colors.surface,\n disabledContainerColor = colors.surface,\n focusedTextColor = colors.ink,\n unfocusedTextColor = colors.ink,\n focusedBorderColor = colors.brand,\n unfocusedBorderColor = colors.line,\n cursorColor = colors.brand\n )\n )\n}\n",
|
|
36809
|
-
"packages/compose/src/main/kotlin/com/santi020k/lumen/LumenTheme.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.isSystemInDarkTheme\nimport androidx.compose.
|
|
36810
|
-
"packages/compose/src/main/kotlin/com/santi020k/lumen/Primitives.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.BorderStroke\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.RowScope\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.defaultMinSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.ButtonDefaults\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.HorizontalDivider\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.IconButtonDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedTextField\nimport androidx.compose.material3.OutlinedTextFieldDefaults\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.vector.ImageVector\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.semantics.stateDescription\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.input.VisualTransformation\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\n\nenum class LumenTextTone {\n Danger,\n Default,\n Muted,\n Soft,\n Success,\n Warning\n}\n\nenum class LumenTextVariant {\n Body,\n Caption,\n Label,\n Title\n}\n\n@Composable\nfun LumenText(\n text: String,\n modifier: Modifier = Modifier,\n variant: LumenTextVariant = LumenTextVariant.Body,\n tone: LumenTextTone = LumenTextTone.Default\n) {\n val theme = LocalLumenTheme.current\n val color = when (tone) {\n LumenTextTone.Danger -> theme.colors.danger\n LumenTextTone.Default -> theme.colors.ink\n LumenTextTone.Muted -> theme.colors.inkMuted\n LumenTextTone.Soft -> theme.colors.inkSoft\n LumenTextTone.Success -> theme.colors.success\n LumenTextTone.Warning -> theme.colors.warning\n }\n val style = when (variant) {\n LumenTextVariant.Body -> MaterialTheme.typography.bodyLarge\n LumenTextVariant.Caption -> MaterialTheme.typography.bodySmall\n LumenTextVariant.Label -> MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n LumenTextVariant.Title -> MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Bold)\n }\n\n Text(text = text, modifier = modifier, color = color, style = style)\n}\n\nenum class LumenSurfacePadding(val value: Dp) {\n None(0.dp),\n Sm(LumenSpacing.Sm),\n Md(LumenSpacing.Md),\n Lg(LumenSpacing.Lg)\n}\n\nenum class LumenSurfaceRadius(val value: Dp) {\n None(0.dp),\n Sm(LumenRadius.Sm),\n Md(LumenRadius.Md),\n Lg(LumenRadius.Lg)\n}\n\nenum class LumenSurfaceTone {\n Canvas,\n Muted,\n Strong,\n Surface\n}\n\n@Composable\nfun LumenSurface(\n modifier: Modifier = Modifier,\n tone: LumenSurfaceTone = LumenSurfaceTone.Surface,\n padding: LumenSurfacePadding = LumenSurfacePadding.Md,\n radius: LumenSurfaceRadius = LumenSurfaceRadius.Md,\n content: @Composable () -> Unit\n) {\n val colors = LocalLumenTheme.current.colors\n val color = when (tone) {\n LumenSurfaceTone.Canvas -> colors.canvas\n LumenSurfaceTone.Muted -> colors.surfaceMuted\n LumenSurfaceTone.Strong -> colors.surfaceStrong\n LumenSurfaceTone.Surface -> colors.surface\n }\n\n Surface(\n modifier = modifier,\n color = color,\n shape = RoundedCornerShape(radius.value)\n ) {\n androidx.compose.foundation.layout.Box(\n modifier = Modifier.padding(padding.value),\n contentAlignment = Alignment.TopStart\n ) {\n content()\n }\n }\n}\n\nenum class LumenButtonIntent {\n Danger,\n Primary,\n Quiet,\n Secondary\n}\n\nenum class LumenControlSize {\n Lg,\n Md,\n Sm\n}\n\nenum class LumenIconSize(val dimension: Dp) {\n Sm(16.dp),\n Md(20.dp),\n Lg(24.dp)\n}\n\n@Composable\nfun LumenIcon(\n imageVector: ImageVector,\n contentDescription: String? = null,\n modifier: Modifier = Modifier,\n size: LumenIconSize = LumenIconSize.Md,\n tint: Color = LocalLumenTheme.current.colors.ink\n) {\n Icon(\n imageVector = imageVector,\n contentDescription = contentDescription,\n modifier = modifier.size(size.dimension),\n tint = tint\n )\n}\n\n@Immutable\ndata class LumenIconButtonMetrics(\n val iconSize: LumenIconSize,\n val touchTarget: Dp\n) {\n companion object {\n fun resolve(size: LumenControlSize): LumenIconButtonMetrics = when (size) {\n LumenControlSize.Lg -> LumenIconButtonMetrics(LumenIconSize.Lg, 52.dp)\n LumenControlSize.Md -> LumenIconButtonMetrics(LumenIconSize.Md, 44.dp)\n LumenControlSize.Sm -> LumenIconButtonMetrics(LumenIconSize.Sm, 44.dp)\n }\n }\n}\n\n@Composable\nfun LumenIconButton(\n imageVector: ImageVector,\n contentDescription: String,\n onClick: () -> Unit,\n modifier: Modifier = Modifier,\n intent: LumenButtonIntent = LumenButtonIntent.Quiet,\n size: LumenControlSize = LumenControlSize.Md,\n enabled: Boolean = true\n) {\n val palette = buttonPalette(LocalLumenTheme.current.colors, intent)\n val metrics = LumenIconButtonMetrics.resolve(size)\n val shape = RoundedCornerShape(LumenRadius.Sm)\n\n IconButton(\n onClick = onClick,\n modifier = modifier\n .size(metrics.touchTarget)\n .clip(shape)\n .border(1.dp, palette.border, shape),\n enabled = enabled,\n colors = IconButtonDefaults.iconButtonColors(\n containerColor = palette.background,\n contentColor = palette.foreground,\n disabledContainerColor = palette.background.copy(alpha = 0.52f),\n disabledContentColor = palette.foreground.copy(alpha = 0.52f)\n )\n ) {\n LumenIcon(\n imageVector = imageVector,\n contentDescription = contentDescription,\n size = metrics.iconSize,\n tint = palette.foreground.copy(alpha = if (enabled) 1f else 0.52f)\n )\n }\n}\n\n@Immutable\ndata class LumenButtonMetrics(\n val horizontalPadding: Dp,\n val minHeight: Dp\n) {\n companion object {\n fun resolve(size: LumenControlSize): LumenButtonMetrics = when (size) {\n LumenControlSize.Lg -> LumenButtonMetrics(LumenSpacing.Xl, 52.dp)\n LumenControlSize.Md -> LumenButtonMetrics(LumenSpacing.Lg, 44.dp)\n LumenControlSize.Sm -> LumenButtonMetrics(LumenSpacing.Md, 36.dp)\n }\n }\n}\n\nprivate data class LumenButtonPalette(\n val background: Color,\n val border: Color,\n val foreground: Color\n)\n\nprivate fun buttonPalette(\n colors: LumenColorPalette,\n intent: LumenButtonIntent\n): LumenButtonPalette = when (intent) {\n LumenButtonIntent.Danger -> LumenButtonPalette(colors.danger, colors.danger, colors.onDanger)\n LumenButtonIntent.Primary -> LumenButtonPalette(colors.brandSolid, colors.brandSolid, colors.onBrand)\n LumenButtonIntent.Quiet -> LumenButtonPalette(Color.Transparent, Color.Transparent, colors.inkSoft)\n LumenButtonIntent.Secondary -> LumenButtonPalette(colors.surfaceMuted, colors.line, colors.ink)\n}\n\n@Composable\nfun LumenButton(\n onClick: () -> Unit,\n modifier: Modifier = Modifier,\n intent: LumenButtonIntent = LumenButtonIntent.Primary,\n size: LumenControlSize = LumenControlSize.Md,\n loading: Boolean = false,\n enabled: Boolean = true,\n content: @Composable RowScope.() -> Unit\n) {\n val palette = buttonPalette(LocalLumenTheme.current.colors, intent)\n val metrics = LumenButtonMetrics.resolve(size)\n val semanticsModifier = if (loading) {\n Modifier.semantics { stateDescription = \"Loading\" }\n } else {\n Modifier\n }\n\n Button(\n onClick = onClick,\n modifier = modifier\n .then(semanticsModifier)\n .defaultMinSize(minHeight = metrics.minHeight),\n enabled = enabled && !loading,\n shape = RoundedCornerShape(LumenRadius.Sm),\n border = BorderStroke(1.dp, palette.border),\n colors = ButtonDefaults.buttonColors(\n containerColor = palette.background,\n contentColor = palette.foreground,\n disabledContainerColor = palette.background.copy(alpha = 0.52f),\n disabledContentColor = palette.foreground.copy(alpha = 0.52f)\n ),\n contentPadding = PaddingValues(\n horizontal = metrics.horizontalPadding,\n vertical = LumenSpacing.Sm\n )\n ) {\n Row(\n horizontalArrangement = Arrangement.Center,\n verticalAlignment = Alignment.CenterVertically\n ) {\n if (loading) {\n CircularProgressIndicator(\n modifier = Modifier.size(16.dp),\n color = palette.foreground,\n strokeWidth = 2.dp\n )\n Spacer(modifier = Modifier.width(LumenSpacing.Sm))\n }\n content()\n }\n }\n}\n\n@Composable\nfun LumenTextField(\n value: String,\n onValueChange: (String) -> Unit,\n label: String,\n modifier: Modifier = Modifier,\n size: LumenControlSize = LumenControlSize.Md,\n error: Boolean = false,\n errorMessage: String? = null,\n enabled: Boolean = true,\n visualTransformation: VisualTransformation = VisualTransformation.None\n) {\n val colors = LocalLumenTheme.current.colors\n val metrics = LumenButtonMetrics.resolve(size)\n\n OutlinedTextField(\n value = value,\n onValueChange = onValueChange,\n modifier = modifier.defaultMinSize(minHeight = metrics.minHeight),\n enabled = enabled,\n isError = error,\n singleLine = true,\n label = { Text(label) },\n supportingText = errorMessage?.let { message -> ({ Text(message) }) },\n visualTransformation = visualTransformation,\n shape = RoundedCornerShape(LumenRadius.Sm),\n colors = OutlinedTextFieldDefaults.colors(\n focusedContainerColor = colors.surface,\n unfocusedContainerColor = colors.surface,\n disabledContainerColor = colors.surface,\n focusedTextColor = colors.ink,\n unfocusedTextColor = colors.ink,\n focusedBorderColor = colors.brand,\n unfocusedBorderColor = colors.line,\n errorBorderColor = colors.danger,\n cursorColor = colors.brand\n )\n )\n}\n\nenum class LumenBadgeTone {\n Accent,\n Danger,\n Neutral,\n Success,\n Warning\n}\n\n@Composable\nfun LumenBadge(\n text: String,\n modifier: Modifier = Modifier,\n tone: LumenBadgeTone = LumenBadgeTone.Neutral\n) {\n val colors = LocalLumenTheme.current.colors\n val foreground = when (tone) {\n LumenBadgeTone.Accent -> colors.accent\n LumenBadgeTone.Danger -> colors.danger\n LumenBadgeTone.Neutral -> colors.inkSoft\n LumenBadgeTone.Success -> colors.success\n LumenBadgeTone.Warning -> colors.warning\n }\n val background = if (tone == LumenBadgeTone.Neutral) {\n colors.surfaceMuted\n } else {\n foreground.copy(alpha = 0.12f)\n }\n\n Surface(\n modifier = modifier,\n color = background,\n contentColor = foreground,\n shape = RoundedCornerShape(LumenRadius.Full)\n ) {\n Text(\n text = text,\n modifier = Modifier.padding(horizontal = LumenSpacing.Sm, vertical = LumenSpacing.Xs),\n style = MaterialTheme.typography.labelSmall.copy(fontWeight = FontWeight.SemiBold)\n )\n }\n}\n\n@Composable\nfun LumenDivider(modifier: Modifier = Modifier) {\n val colors = LocalLumenTheme.current.colors\n\n HorizontalDivider(modifier = modifier, color = colors.line)\n}\n\n@Composable\nfun LumenSpinner(\n modifier: Modifier = Modifier,\n label: String = \"Loading\"\n) {\n val colors = LocalLumenTheme.current.colors\n\n CircularProgressIndicator(\n modifier = modifier.semantics { contentDescription = label },\n color = colors.brand\n )\n}\n",
|
|
38745
|
+
"packages/compose/src/main/kotlin/com/santi020k/lumen/LumenTheme.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.isSystemInDarkTheme\nimport androidx.compose.material3.ColorScheme\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Shapes\nimport androidx.compose.material3.Typography\nimport androidx.compose.material3.darkColorScheme\nimport androidx.compose.material3.lightColorScheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtime.staticCompositionLocalOf\nimport androidx.compose.ui.graphics.Color\n\n@Immutable\ndata class LumenThemeValues(\n val colors: LumenColorPalette,\n val isDark: Boolean\n)\n\n@Immutable\ndata class LumenMaterialColorOverrides(\n val brand: Color? = null,\n val accent: Color? = null,\n val success: Color? = null,\n val warning: Color? = null\n)\n\nval LocalLumenTheme = staticCompositionLocalOf {\n LumenThemeValues(colors = LumenColors.Light, isDark = false)\n}\n\nfun ColorScheme.toLumenColorPalette(\n fallback: LumenColorPalette,\n overrides: LumenMaterialColorOverrides = LumenMaterialColorOverrides()\n): LumenColorPalette = LumenColorPalette(\n canvas = background,\n surface = surface,\n surfaceMuted = surfaceVariant,\n surfaceStrong = surfaceContainerHigh,\n line = outline,\n ink = onBackground,\n inkSoft = onSurface,\n inkMuted = onSurfaceVariant,\n brand = overrides.brand ?: primary,\n brandSolid = overrides.brand ?: primary,\n brandSoft = primaryContainer,\n onBrand = onPrimary,\n accent = overrides.accent ?: secondary,\n success = overrides.success ?: fallback.success,\n warning = overrides.warning ?: fallback.warning,\n danger = error,\n onDanger = onError\n)\n\nfun LumenColorPalette.toMaterialColorScheme(isDark: Boolean): ColorScheme {\n return if (isDark) {\n darkColorScheme(\n primary = brand,\n onPrimary = onBrand,\n primaryContainer = brandSoft,\n onPrimaryContainer = ink,\n secondary = accent,\n background = canvas,\n onBackground = ink,\n surface = surface,\n onSurface = ink,\n surfaceVariant = surfaceMuted,\n onSurfaceVariant = inkMuted,\n surfaceContainerHigh = surfaceStrong,\n error = danger,\n onError = onDanger,\n outline = line\n )\n } else {\n lightColorScheme(\n primary = brand,\n onPrimary = onBrand,\n primaryContainer = brandSoft,\n onPrimaryContainer = ink,\n secondary = accent,\n background = canvas,\n onBackground = ink,\n surface = surface,\n onSurface = ink,\n surfaceVariant = surfaceMuted,\n onSurfaceVariant = inkMuted,\n surfaceContainerHigh = surfaceStrong,\n error = danger,\n onError = onDanger,\n outline = line\n )\n }\n}\n\n@Composable\nfun LumenTheme(\n darkTheme: Boolean = isSystemInDarkTheme(),\n values: LumenThemeValues? = null,\n materialColorScheme: ColorScheme? = null,\n materialColorOverrides: LumenMaterialColorOverrides = LumenMaterialColorOverrides(),\n typography: Typography = Typography(),\n shapes: Shapes = Shapes(),\n content: @Composable () -> Unit\n) {\n val defaultValues = if (darkTheme) {\n LumenThemeValues(colors = LumenColors.Dark, isDark = true)\n } else {\n LumenThemeValues(colors = LumenColors.Light, isDark = false)\n }\n val resolvedValues = values ?: materialColorScheme?.let { colorScheme ->\n LumenThemeValues(\n colors = colorScheme.toLumenColorPalette(defaultValues.colors, materialColorOverrides),\n isDark = darkTheme\n )\n } ?: defaultValues\n val resolvedColorScheme = materialColorScheme\n ?: resolvedValues.colors.toMaterialColorScheme(resolvedValues.isDark)\n\n CompositionLocalProvider(LocalLumenTheme provides resolvedValues) {\n MaterialTheme(\n colorScheme = resolvedColorScheme,\n typography = typography,\n shapes = shapes,\n content = content\n )\n }\n}\n",
|
|
38746
|
+
"packages/compose/src/main/kotlin/com/santi020k/lumen/OverlayComponents.kt": "package com.santi020k.lumen\n\nimport android.app.Activity\nimport android.content.ActivityNotFoundException\nimport android.content.Intent\nimport android.net.Uri\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.DropdownMenu\nimport androidx.compose.material3.DropdownMenuItem\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.HorizontalDivider\nimport androidx.compose.material3.ModalBottomSheet\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.vector.ImageVector\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.text.font.FontWeight\n\n/** A controlled native confirmation dialog with explicit cancel and confirm actions. */\n@Composable\nfun LumenAlertDialog(\n visible: Boolean,\n title: String,\n confirmLabel: String,\n onConfirm: () -> Unit,\n onDismiss: () -> Unit,\n modifier: Modifier = Modifier,\n description: String? = null,\n cancelLabel: String = \"Cancel\",\n destructive: Boolean = false,\n confirmEnabled: Boolean = true,\n confirmLoading: Boolean = false\n) {\n if (!visible) return\n\n AlertDialog(\n onDismissRequest = onDismiss,\n modifier = modifier,\n title = { Text(title, fontWeight = FontWeight.SemiBold) },\n text = description?.let { value -> { Text(value) } },\n confirmButton = {\n LumenButton(\n onClick = onConfirm,\n intent = if (destructive) LumenButtonIntent.Danger else LumenButtonIntent.Primary,\n loading = confirmLoading,\n enabled = confirmEnabled\n ) {\n Text(confirmLabel)\n }\n },\n dismissButton = {\n LumenButton(onClick = onDismiss, intent = LumenButtonIntent.Quiet) {\n Text(cancelLabel)\n }\n }\n )\n}\n\n/** A controlled Material bottom sheet with a shared heading, description, and action layout. */\n@OptIn(ExperimentalMaterial3Api::class)\n@Composable\nfun LumenSheet(\n visible: Boolean,\n onDismiss: () -> Unit,\n modifier: Modifier = Modifier,\n title: String? = null,\n description: String? = null,\n actions: @Composable () -> Unit = {},\n content: @Composable () -> Unit\n) {\n if (!visible) return\n\n ModalBottomSheet(onDismissRequest = onDismiss, modifier = modifier) {\n Column(\n modifier = Modifier\n .fillMaxWidth()\n .padding(horizontal = LumenSpacing.Xl)\n .padding(bottom = LumenSpacing.Xl),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Lg)\n ) {\n if (title != null || description != null) {\n Column(verticalArrangement = Arrangement.spacedBy(LumenSpacing.Sm)) {\n title?.let { Text(it, fontWeight = FontWeight.SemiBold) }\n description?.let { Text(it, color = LocalLumenTheme.current.colors.inkSoft) }\n }\n }\n\n content()\n\n Row(\n modifier = Modifier.fillMaxWidth(),\n horizontalArrangement = Arrangement.End\n ) {\n actions()\n }\n }\n }\n}\n\nclass LumenMenuItem(\n val label: String,\n val enabled: Boolean = true,\n val destructive: Boolean = false,\n val leadingIcon: ImageVector? = null,\n val onClick: () -> Unit\n)\n\n/** A controlled native anchored menu with labeled disabled and destructive action states. */\n@Composable\nfun LumenMenu(\n expanded: Boolean,\n onDismissRequest: () -> Unit,\n items: List<LumenMenuItem>,\n modifier: Modifier = Modifier\n) {\n val colors = LocalLumenTheme.current.colors\n\n DropdownMenu(\n expanded = expanded,\n onDismissRequest = onDismissRequest,\n modifier = modifier\n ) {\n items.forEachIndexed { index, item ->\n DropdownMenuItem(\n text = {\n Text(\n item.label,\n color = if (item.destructive) colors.danger else colors.ink\n )\n },\n onClick = {\n onDismissRequest()\n item.onClick()\n },\n enabled = item.enabled,\n leadingIcon = item.leadingIcon?.let { icon ->\n {\n LumenIcon(\n imageVector = icon,\n tint = if (item.destructive) colors.danger else colors.inkSoft\n )\n }\n }\n )\n\n if (index < items.lastIndex) {\n HorizontalDivider(color = colors.line)\n }\n }\n }\n}\n\n@Immutable\ndata class LumenSharePayload(\n val text: String? = null,\n val uri: Uri? = null,\n val subject: String? = null,\n val mimeType: String = \"text/plain\"\n) {\n val hasContent: Boolean\n get() = !text.isNullOrBlank() || uri != null\n}\n\n/** A Lumen button that opens Android's native share sheet for text or a content URI. */\n@Composable\nfun LumenShareButton(\n payload: LumenSharePayload,\n chooserTitle: String,\n modifier: Modifier = Modifier,\n label: String = \"Share\",\n enabled: Boolean = true,\n onFailure: (ActivityNotFoundException) -> Unit = {}\n) {\n val context = LocalContext.current\n\n LumenButton(\n onClick = {\n val sendIntent = Intent(Intent.ACTION_SEND).apply {\n type = payload.mimeType\n payload.subject?.let { putExtra(Intent.EXTRA_SUBJECT, it) }\n payload.text?.let { putExtra(Intent.EXTRA_TEXT, it) }\n payload.uri?.let {\n putExtra(Intent.EXTRA_STREAM, it)\n addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n }\n }\n val chooser = Intent.createChooser(sendIntent, chooserTitle)\n\n if (context !is Activity) chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n\n try {\n context.startActivity(chooser)\n } catch (error: ActivityNotFoundException) {\n onFailure(error)\n }\n },\n modifier = modifier,\n intent = LumenButtonIntent.Secondary,\n enabled = enabled && payload.hasContent\n ) {\n Text(label)\n }\n}\n",
|
|
38747
|
+
"packages/compose/src/main/kotlin/com/santi020k/lumen/PlatformComponents.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.FloatingActionButton\nimport androidx.compose.material3.NavigationBar as MaterialNavigationBar\nimport androidx.compose.material3.NavigationBarItem\nimport androidx.compose.material3.NavigationBarItemDefaults\nimport androidx.compose.material3.SmallFloatingActionButton\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.vector.ImageVector\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\n\n@Immutable\ndata class LumenNavigationItem<Value>(\n val value: Value,\n val label: String,\n val icon: ImageVector,\n val selectedIcon: ImageVector = icon,\n val enabled: Boolean = true\n)\n\ninternal fun <Value> isLumenNavigationItemSelected(itemValue: Value, selectedValue: Value): Boolean =\n itemValue == selectedValue\n\n/**\n * A Material-native controlled destination bar. Navigation stacks and screen content remain app-owned.\n */\n@Composable\nfun <Value> LumenNavigationBar(\n items: List<LumenNavigationItem<Value>>,\n selectedValue: Value,\n onValueChange: (Value) -> Unit,\n modifier: Modifier = Modifier,\n accessibilityLabel: String = \"Primary navigation\"\n) {\n val colors = LocalLumenTheme.current.colors\n\n MaterialNavigationBar(\n modifier = modifier.semantics { contentDescription = accessibilityLabel },\n containerColor = colors.surface,\n contentColor = colors.inkMuted\n ) {\n items.forEach { item ->\n val selected = isLumenNavigationItemSelected(item.value, selectedValue)\n\n NavigationBarItem(\n selected = selected,\n onClick = { onValueChange(item.value) },\n icon = {\n LumenIcon(\n imageVector = if (selected) item.selectedIcon else item.icon,\n contentDescription = null,\n size = LumenIconSize.Md,\n tint = (if (selected) colors.brand else colors.inkMuted).copy(\n alpha = if (item.enabled) 1f else 0.52f\n )\n )\n },\n enabled = item.enabled,\n label = { Text(item.label, maxLines = 1) },\n colors = NavigationBarItemDefaults.colors(\n selectedIconColor = colors.brand,\n selectedTextColor = colors.brand,\n indicatorColor = colors.brandSoft,\n unselectedIconColor = colors.inkMuted,\n unselectedTextColor = colors.inkMuted,\n disabledIconColor = colors.inkMuted.copy(alpha = 0.52f),\n disabledTextColor = colors.inkMuted.copy(alpha = 0.52f)\n )\n )\n }\n }\n}\n\nenum class LumenFloatingActionButtonIntent {\n Accent,\n Brand,\n Danger\n}\n\nenum class LumenFloatingActionButtonSize {\n Regular,\n Small\n}\n\n@Immutable\ndata class LumenFloatingActionButtonMetrics(\n val dimension: Dp,\n val iconSize: LumenIconSize\n) {\n companion object {\n fun resolve(size: LumenFloatingActionButtonSize): LumenFloatingActionButtonMetrics = when (size) {\n LumenFloatingActionButtonSize.Regular -> LumenFloatingActionButtonMetrics(56.dp, LumenIconSize.Lg)\n LumenFloatingActionButtonSize.Small -> LumenFloatingActionButtonMetrics(40.dp, LumenIconSize.Md)\n }\n }\n}\n\n@Immutable\ninternal data class LumenFloatingActionButtonPalette(\n val background: Color,\n val foreground: Color\n)\n\ninternal fun lumenFloatingActionButtonPalette(\n colors: LumenColorPalette,\n intent: LumenFloatingActionButtonIntent\n): LumenFloatingActionButtonPalette = when (intent) {\n LumenFloatingActionButtonIntent.Accent -> LumenFloatingActionButtonPalette(\n background = colors.accent,\n foreground = colors.canvas\n )\n LumenFloatingActionButtonIntent.Brand -> LumenFloatingActionButtonPalette(\n background = colors.brandSolid,\n foreground = colors.onBrand\n )\n LumenFloatingActionButtonIntent.Danger -> LumenFloatingActionButtonPalette(\n background = colors.danger,\n foreground = colors.onDanger\n )\n}\n\n/**\n * A Material-native floating action button using Lumen semantic colors and icon sizing.\n * The content description is required because floating actions are icon-only.\n */\n@Composable\nfun LumenFloatingActionButton(\n imageVector: ImageVector,\n contentDescription: String,\n onClick: () -> Unit,\n modifier: Modifier = Modifier,\n intent: LumenFloatingActionButtonIntent = LumenFloatingActionButtonIntent.Brand,\n size: LumenFloatingActionButtonSize = LumenFloatingActionButtonSize.Regular\n) {\n val palette = lumenFloatingActionButtonPalette(LocalLumenTheme.current.colors, intent)\n val metrics = LumenFloatingActionButtonMetrics.resolve(size)\n val content: @Composable () -> Unit = {\n LumenIcon(\n imageVector = imageVector,\n contentDescription = contentDescription,\n size = metrics.iconSize,\n tint = palette.foreground\n )\n }\n\n when (size) {\n LumenFloatingActionButtonSize.Regular -> FloatingActionButton(\n onClick = onClick,\n modifier = modifier,\n shape = RoundedCornerShape(LumenRadius.Lg),\n containerColor = palette.background,\n contentColor = palette.foreground,\n content = content\n )\n LumenFloatingActionButtonSize.Small -> SmallFloatingActionButton(\n onClick = onClick,\n modifier = modifier,\n shape = RoundedCornerShape(LumenRadius.Md),\n containerColor = palette.background,\n contentColor = palette.foreground,\n content = content\n )\n }\n}\n",
|
|
38748
|
+
"packages/compose/src/main/kotlin/com/santi020k/lumen/Primitives.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.BorderStroke\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.RowScope\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.defaultMinSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.ButtonDefaults\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.HorizontalDivider\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.IconButtonDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedTextField\nimport androidx.compose.material3.OutlinedTextFieldDefaults\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.vector.ImageVector\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.semantics.stateDescription\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.input.VisualTransformation\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\n\nenum class LumenTextTone {\n Danger,\n Default,\n Muted,\n Soft,\n Success,\n Warning\n}\n\nenum class LumenTextVariant {\n Body,\n Caption,\n Label,\n Title\n}\n\n@Composable\nfun LumenText(\n text: String,\n modifier: Modifier = Modifier,\n variant: LumenTextVariant = LumenTextVariant.Body,\n tone: LumenTextTone = LumenTextTone.Default\n) {\n val theme = LocalLumenTheme.current\n val color = when (tone) {\n LumenTextTone.Danger -> theme.colors.danger\n LumenTextTone.Default -> theme.colors.ink\n LumenTextTone.Muted -> theme.colors.inkMuted\n LumenTextTone.Soft -> theme.colors.inkSoft\n LumenTextTone.Success -> theme.colors.success\n LumenTextTone.Warning -> theme.colors.warning\n }\n val style = when (variant) {\n LumenTextVariant.Body -> MaterialTheme.typography.bodyLarge\n LumenTextVariant.Caption -> MaterialTheme.typography.bodySmall\n LumenTextVariant.Label -> MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n LumenTextVariant.Title -> MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Bold)\n }\n\n Text(text = text, modifier = modifier, color = color, style = style)\n}\n\nenum class LumenSurfacePadding(val value: Dp) {\n None(0.dp),\n Sm(LumenSpacing.Sm),\n Md(LumenSpacing.Md),\n Lg(LumenSpacing.Lg)\n}\n\nenum class LumenSurfaceRadius(val value: Dp) {\n None(0.dp),\n Sm(LumenRadius.Sm),\n Md(LumenRadius.Md),\n Lg(LumenRadius.Lg)\n}\n\nenum class LumenSurfaceTone {\n Canvas,\n Muted,\n Strong,\n Surface\n}\n\n@Composable\nfun LumenSurface(\n modifier: Modifier = Modifier,\n tone: LumenSurfaceTone = LumenSurfaceTone.Surface,\n padding: LumenSurfacePadding = LumenSurfacePadding.Md,\n radius: LumenSurfaceRadius = LumenSurfaceRadius.Md,\n content: @Composable () -> Unit\n) {\n val colors = LocalLumenTheme.current.colors\n val color = when (tone) {\n LumenSurfaceTone.Canvas -> colors.canvas\n LumenSurfaceTone.Muted -> colors.surfaceMuted\n LumenSurfaceTone.Strong -> colors.surfaceStrong\n LumenSurfaceTone.Surface -> colors.surface\n }\n\n Surface(\n modifier = modifier,\n color = color,\n shape = RoundedCornerShape(radius.value)\n ) {\n androidx.compose.foundation.layout.Box(\n modifier = Modifier.padding(padding.value),\n contentAlignment = Alignment.TopStart\n ) {\n content()\n }\n }\n}\n\nenum class LumenButtonIntent {\n Danger,\n Primary,\n Quiet,\n Secondary\n}\n\nenum class LumenControlSize {\n Lg,\n Md,\n Sm\n}\n\nenum class LumenIconSize(val dimension: Dp) {\n Sm(16.dp),\n Md(20.dp),\n Lg(24.dp)\n}\n\n@Composable\nfun LumenIcon(\n imageVector: ImageVector,\n modifier: Modifier = Modifier,\n contentDescription: String? = null,\n size: LumenIconSize = LumenIconSize.Md,\n tint: Color = LocalLumenTheme.current.colors.ink\n) {\n Icon(\n imageVector = imageVector,\n contentDescription = contentDescription,\n modifier = modifier.size(size.dimension),\n tint = tint\n )\n}\n\n@Immutable\ndata class LumenIconButtonMetrics(\n val iconSize: LumenIconSize,\n val touchTarget: Dp\n) {\n companion object {\n fun resolve(size: LumenControlSize): LumenIconButtonMetrics = when (size) {\n LumenControlSize.Lg -> LumenIconButtonMetrics(LumenIconSize.Lg, 52.dp)\n LumenControlSize.Md -> LumenIconButtonMetrics(LumenIconSize.Md, 44.dp)\n LumenControlSize.Sm -> LumenIconButtonMetrics(LumenIconSize.Sm, 44.dp)\n }\n }\n}\n\n@Composable\nfun LumenIconButton(\n imageVector: ImageVector,\n contentDescription: String,\n onClick: () -> Unit,\n modifier: Modifier = Modifier,\n intent: LumenButtonIntent = LumenButtonIntent.Quiet,\n size: LumenControlSize = LumenControlSize.Md,\n enabled: Boolean = true\n) {\n val palette = buttonPalette(LocalLumenTheme.current.colors, intent)\n val metrics = LumenIconButtonMetrics.resolve(size)\n val shape = RoundedCornerShape(LumenRadius.Sm)\n\n IconButton(\n onClick = onClick,\n modifier = modifier\n .size(metrics.touchTarget)\n .clip(shape)\n .border(1.dp, palette.border, shape),\n enabled = enabled,\n colors = IconButtonDefaults.iconButtonColors(\n containerColor = palette.background,\n contentColor = palette.foreground,\n disabledContainerColor = palette.background.copy(alpha = 0.52f),\n disabledContentColor = palette.foreground.copy(alpha = 0.52f)\n )\n ) {\n LumenIcon(\n imageVector = imageVector,\n contentDescription = contentDescription,\n size = metrics.iconSize,\n tint = palette.foreground.copy(alpha = if (enabled) 1f else 0.52f)\n )\n }\n}\n\n@Immutable\ndata class LumenButtonMetrics(\n val horizontalPadding: Dp,\n val minHeight: Dp\n) {\n companion object {\n fun resolve(size: LumenControlSize): LumenButtonMetrics = when (size) {\n LumenControlSize.Lg -> LumenButtonMetrics(LumenSpacing.Xl, 52.dp)\n LumenControlSize.Md -> LumenButtonMetrics(LumenSpacing.Lg, 44.dp)\n LumenControlSize.Sm -> LumenButtonMetrics(LumenSpacing.Md, 36.dp)\n }\n }\n}\n\nprivate data class LumenButtonPalette(\n val background: Color,\n val border: Color,\n val foreground: Color\n)\n\nprivate fun buttonPalette(\n colors: LumenColorPalette,\n intent: LumenButtonIntent\n): LumenButtonPalette = when (intent) {\n LumenButtonIntent.Danger -> LumenButtonPalette(colors.danger, colors.danger, colors.onDanger)\n LumenButtonIntent.Primary -> LumenButtonPalette(colors.brandSolid, colors.brandSolid, colors.onBrand)\n LumenButtonIntent.Quiet -> LumenButtonPalette(Color.Transparent, Color.Transparent, colors.inkSoft)\n LumenButtonIntent.Secondary -> LumenButtonPalette(colors.surfaceMuted, colors.line, colors.ink)\n}\n\n@Composable\nfun LumenButton(\n onClick: () -> Unit,\n modifier: Modifier = Modifier,\n intent: LumenButtonIntent = LumenButtonIntent.Primary,\n size: LumenControlSize = LumenControlSize.Md,\n loading: Boolean = false,\n enabled: Boolean = true,\n content: @Composable RowScope.() -> Unit\n) {\n val palette = buttonPalette(LocalLumenTheme.current.colors, intent)\n val metrics = LumenButtonMetrics.resolve(size)\n val semanticsModifier = if (loading) {\n Modifier.semantics { stateDescription = \"Loading\" }\n } else {\n Modifier\n }\n\n Button(\n onClick = onClick,\n modifier = modifier\n .then(semanticsModifier)\n .defaultMinSize(minHeight = metrics.minHeight),\n enabled = enabled && !loading,\n shape = RoundedCornerShape(LumenRadius.Sm),\n border = BorderStroke(1.dp, palette.border),\n colors = ButtonDefaults.buttonColors(\n containerColor = palette.background,\n contentColor = palette.foreground,\n disabledContainerColor = palette.background.copy(alpha = 0.52f),\n disabledContentColor = palette.foreground.copy(alpha = 0.52f)\n ),\n contentPadding = PaddingValues(\n horizontal = metrics.horizontalPadding,\n vertical = LumenSpacing.Sm\n )\n ) {\n Row(\n horizontalArrangement = Arrangement.Center,\n verticalAlignment = Alignment.CenterVertically\n ) {\n if (loading) {\n CircularProgressIndicator(\n modifier = Modifier.size(16.dp),\n color = palette.foreground,\n strokeWidth = 2.dp\n )\n Spacer(modifier = Modifier.width(LumenSpacing.Sm))\n }\n content()\n }\n }\n}\n\n@Composable\nfun LumenTextField(\n value: String,\n onValueChange: (String) -> Unit,\n label: String,\n modifier: Modifier = Modifier,\n size: LumenControlSize = LumenControlSize.Md,\n error: Boolean = false,\n errorMessage: String? = null,\n enabled: Boolean = true,\n visualTransformation: VisualTransformation = VisualTransformation.None\n) {\n val colors = LocalLumenTheme.current.colors\n val metrics = LumenButtonMetrics.resolve(size)\n\n OutlinedTextField(\n value = value,\n onValueChange = onValueChange,\n modifier = modifier.defaultMinSize(minHeight = metrics.minHeight),\n enabled = enabled,\n isError = error,\n singleLine = true,\n label = { Text(label) },\n supportingText = errorMessage?.let { message -> ({ Text(message) }) },\n visualTransformation = visualTransformation,\n shape = RoundedCornerShape(LumenRadius.Sm),\n colors = OutlinedTextFieldDefaults.colors(\n focusedContainerColor = colors.surface,\n unfocusedContainerColor = colors.surface,\n disabledContainerColor = colors.surface,\n focusedTextColor = colors.ink,\n unfocusedTextColor = colors.ink,\n focusedBorderColor = colors.brand,\n unfocusedBorderColor = colors.line,\n errorBorderColor = colors.danger,\n cursorColor = colors.brand\n )\n )\n}\n\nenum class LumenBadgeTone {\n Accent,\n Danger,\n Neutral,\n Success,\n Warning\n}\n\n@Composable\nfun LumenBadge(\n text: String,\n modifier: Modifier = Modifier,\n tone: LumenBadgeTone = LumenBadgeTone.Neutral\n) {\n val colors = LocalLumenTheme.current.colors\n val foreground = when (tone) {\n LumenBadgeTone.Accent -> colors.accent\n LumenBadgeTone.Danger -> colors.danger\n LumenBadgeTone.Neutral -> colors.inkSoft\n LumenBadgeTone.Success -> colors.success\n LumenBadgeTone.Warning -> colors.warning\n }\n val background = if (tone == LumenBadgeTone.Neutral) {\n colors.surfaceMuted\n } else {\n foreground.copy(alpha = 0.12f)\n }\n\n Surface(\n modifier = modifier,\n color = background,\n contentColor = foreground,\n shape = RoundedCornerShape(LumenRadius.Full)\n ) {\n Text(\n text = text,\n modifier = Modifier.padding(horizontal = LumenSpacing.Sm, vertical = LumenSpacing.Xs),\n style = MaterialTheme.typography.labelSmall.copy(fontWeight = FontWeight.SemiBold)\n )\n }\n}\n\n@Composable\nfun LumenDivider(modifier: Modifier = Modifier) {\n val colors = LocalLumenTheme.current.colors\n\n HorizontalDivider(modifier = modifier, color = colors.line)\n}\n\n@Composable\nfun LumenSpinner(\n modifier: Modifier = Modifier,\n label: String = \"Loading\"\n) {\n val colors = LocalLumenTheme.current.colors\n\n CircularProgressIndicator(\n modifier = modifier.semantics { contentDescription = label },\n color = colors.brand\n )\n}\n",
|
|
36811
38749
|
"packages/compose/src/main/kotlin/com/santi020k/lumen/SelectionComponents.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.heightIn\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.selection.selectable\nimport androidx.compose.foundation.selection.selectableGroup\nimport androidx.compose.foundation.selection.toggleable\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.Checkbox\nimport androidx.compose.material3.CheckboxDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.RadioButton\nimport androidx.compose.material3.RadioButtonDefaults\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.semantics.Role\nimport androidx.compose.ui.semantics.clearAndSetSemantics\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.style.TextOverflow\nimport androidx.compose.ui.unit.dp\n\n@Immutable\ndata class LumenSelectionOption(\n val value: String,\n val label: String,\n val description: String? = null,\n val enabled: Boolean = true\n)\n\n@Composable\nfun LumenCheckbox(\n label: String,\n checked: Boolean,\n onCheckedChange: (Boolean) -> Unit,\n modifier: Modifier = Modifier,\n description: String? = null,\n enabled: Boolean = true\n) {\n val colors = LocalLumenTheme.current.colors\n\n Row(\n modifier = modifier\n .fillMaxWidth()\n .heightIn(min = LumenButtonMetrics.resolve(LumenControlSize.Md).minHeight)\n .toggleable(\n value = checked,\n enabled = enabled,\n role = Role.Checkbox,\n onValueChange = onCheckedChange\n )\n .semantics(mergeDescendants = true) { contentDescription = label }\n .padding(vertical = LumenSpacing.Sm),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Md),\n verticalAlignment = Alignment.Top\n ) {\n Checkbox(\n checked = checked,\n onCheckedChange = null,\n modifier = Modifier.clearAndSetSemantics {},\n enabled = enabled,\n colors = CheckboxDefaults.colors(\n checkedColor = colors.brandSolid,\n uncheckedColor = colors.line,\n checkmarkColor = colors.onBrand,\n disabledCheckedColor = colors.brandSolid.copy(alpha = 0.52f),\n disabledUncheckedColor = colors.line.copy(alpha = 0.52f)\n )\n )\n Column(\n modifier = Modifier.weight(1f).padding(top = 2.dp),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)\n ) {\n Text(\n text = label,\n color = colors.ink,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n if (description != null) {\n Text(text = description, color = colors.inkMuted, style = MaterialTheme.typography.bodySmall)\n }\n }\n }\n}\n\n@Composable\nfun LumenRadioGroup(\n label: String,\n options: List<LumenSelectionOption>,\n value: String,\n onValueChange: (String) -> Unit,\n modifier: Modifier = Modifier\n) {\n val colors = LocalLumenTheme.current.colors\n\n Column(\n modifier = modifier\n .fillMaxWidth()\n .selectableGroup()\n .semantics { contentDescription = label },\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Sm)\n ) {\n Text(\n text = label,\n color = colors.ink,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n options.forEach { option ->\n val selected = value == option.value\n\n Row(\n modifier = Modifier\n .fillMaxWidth()\n .heightIn(min = LumenButtonMetrics.resolve(LumenControlSize.Md).minHeight)\n .selectable(\n selected = selected,\n enabled = option.enabled,\n role = Role.RadioButton,\n onClick = { onValueChange(option.value) }\n )\n .padding(vertical = LumenSpacing.Sm),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Md),\n verticalAlignment = Alignment.Top\n ) {\n RadioButton(\n selected = selected,\n onClick = null,\n modifier = Modifier.clearAndSetSemantics {},\n enabled = option.enabled,\n colors = RadioButtonDefaults.colors(\n selectedColor = colors.brandSolid,\n unselectedColor = colors.line,\n disabledSelectedColor = colors.brandSolid.copy(alpha = 0.52f),\n disabledUnselectedColor = colors.line.copy(alpha = 0.52f)\n )\n )\n Column(\n modifier = Modifier.weight(1f).padding(top = 2.dp),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)\n ) {\n Text(text = option.label, color = colors.ink, style = MaterialTheme.typography.bodyMedium)\n if (option.description != null) {\n Text(\n text = option.description,\n color = colors.inkMuted,\n style = MaterialTheme.typography.bodySmall\n )\n }\n }\n }\n }\n }\n}\n\n@Composable\nfun LumenSegmentedControl(\n label: String,\n options: List<LumenSelectionOption>,\n value: String,\n onValueChange: (String) -> Unit,\n modifier: Modifier = Modifier,\n showLabel: Boolean = true\n) {\n val colors = LocalLumenTheme.current.colors\n\n Column(\n modifier = modifier.fillMaxWidth(),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Sm)\n ) {\n if (showLabel) {\n Text(\n text = label,\n color = colors.ink,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n }\n Row(\n modifier = Modifier\n .fillMaxWidth()\n .clip(RoundedCornerShape(LumenRadius.Sm))\n .background(colors.surfaceMuted)\n .border(1.dp, colors.line, RoundedCornerShape(LumenRadius.Sm))\n .padding(2.dp)\n .selectableGroup()\n .semantics { contentDescription = label },\n verticalAlignment = Alignment.CenterVertically\n ) {\n options.forEach { option ->\n val selected = value == option.value\n\n Box(\n modifier = Modifier\n .weight(1f)\n .heightIn(min = 40.dp)\n .clip(RoundedCornerShape(LumenRadius.Sm))\n .background(if (selected) colors.surface else androidx.compose.ui.graphics.Color.Transparent)\n .selectable(\n selected = selected,\n enabled = option.enabled,\n role = Role.RadioButton,\n onClick = { onValueChange(option.value) }\n )\n .padding(horizontal = LumenSpacing.Sm, vertical = LumenSpacing.Sm),\n contentAlignment = Alignment.Center\n ) {\n Text(\n text = option.label,\n color = if (selected) colors.brand else colors.inkSoft,\n fontWeight = if (selected) FontWeight.Bold else FontWeight.SemiBold,\n maxLines = 1,\n overflow = TextOverflow.Ellipsis,\n style = MaterialTheme.typography.labelSmall\n )\n }\n }\n }\n }\n}\n",
|
|
36812
38750
|
"packages/compose/src/main/kotlin/com/santi020k/lumen/SharedComponents.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxHeight\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.painter.Painter\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.semantics.ProgressBarRangeInfo\nimport androidx.compose.ui.semantics.Role\nimport androidx.compose.ui.semantics.clearAndSetSemantics\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.progressBarRangeInfo\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\n\nenum class LumenCardVariant {\n Accent,\n Default,\n Destructive,\n Muted,\n Success,\n Warning\n}\n\n@Immutable\ndata class LumenCardPalette(\n val background: Color,\n val border: Color\n)\n\nfun lumenCardPalette(colors: LumenColorPalette, variant: LumenCardVariant): LumenCardPalette {\n if (variant == LumenCardVariant.Default) return LumenCardPalette(colors.surface, colors.line)\n\n if (variant == LumenCardVariant.Muted) return LumenCardPalette(colors.surfaceMuted, colors.line)\n\n val accent = when (variant) {\n LumenCardVariant.Accent -> colors.accent\n LumenCardVariant.Destructive -> colors.danger\n LumenCardVariant.Success -> colors.success\n LumenCardVariant.Warning -> colors.warning\n LumenCardVariant.Default, LumenCardVariant.Muted -> colors.line\n }\n\n return LumenCardPalette(\n background = accent.copy(alpha = 0.06f),\n border = accent.copy(alpha = 0.24f)\n )\n}\n\n@Composable\nfun LumenCard(\n modifier: Modifier = Modifier,\n variant: LumenCardVariant = LumenCardVariant.Default,\n onClick: (() -> Unit)? = null,\n enabled: Boolean = true,\n content: @Composable () -> Unit\n) {\n val colors = LocalLumenTheme.current.colors\n val palette = lumenCardPalette(colors, variant)\n val interactionModifier = if (onClick == null) {\n Modifier\n } else {\n Modifier.clickable(enabled = enabled, role = Role.Button, onClick = onClick)\n }\n\n Surface(\n modifier = modifier.then(interactionModifier),\n color = palette.background,\n contentColor = colors.ink,\n shape = RoundedCornerShape(LumenRadius.Lg),\n border = androidx.compose.foundation.BorderStroke(1.dp, palette.border)\n ) {\n Box(modifier = Modifier.padding(LumenSpacing.Xl)) {\n content()\n }\n }\n}\n\nenum class LumenAlertVariant {\n Default,\n Destructive,\n Success,\n Warning\n}\n\nprivate data class LumenAlertPalette(\n val background: Color,\n val border: Color,\n val foreground: Color\n)\n\nprivate fun alertPalette(\n colors: LumenColorPalette,\n variant: LumenAlertVariant\n): LumenAlertPalette = when (variant) {\n LumenAlertVariant.Default -> LumenAlertPalette(colors.surface, colors.line, colors.ink)\n LumenAlertVariant.Destructive -> LumenAlertPalette(\n colors.danger.copy(alpha = 0.08f),\n colors.danger.copy(alpha = 0.36f),\n colors.danger\n )\n LumenAlertVariant.Success -> LumenAlertPalette(\n colors.success.copy(alpha = 0.08f),\n colors.success.copy(alpha = 0.36f),\n colors.success\n )\n LumenAlertVariant.Warning -> LumenAlertPalette(\n colors.warning.copy(alpha = 0.10f),\n colors.warning.copy(alpha = 0.42f),\n colors.warning\n )\n}\n\n@Composable\nfun LumenAlert(\n modifier: Modifier = Modifier,\n variant: LumenAlertVariant = LumenAlertVariant.Default,\n content: @Composable () -> Unit\n) {\n val palette = alertPalette(LocalLumenTheme.current.colors, variant)\n\n Surface(\n modifier = modifier,\n color = palette.background,\n contentColor = palette.foreground,\n shape = RoundedCornerShape(LumenRadius.Md),\n border = androidx.compose.foundation.BorderStroke(1.dp, palette.border)\n ) {\n Box(\n modifier = Modifier.padding(horizontal = LumenSpacing.Lg, vertical = LumenSpacing.Md)\n ) {\n content()\n }\n }\n}\n\n@Immutable\ndata class LumenProgressValue(\n val max: Float,\n val value: Float\n) {\n val fraction: Float get() = value / max\n\n companion object {\n fun resolve(value: Float, max: Float): LumenProgressValue {\n val safeMax = if (max.isFinite() && max > 0f) max else 100f\n val finiteValue = if (value.isFinite()) value else 0f\n\n return LumenProgressValue(\n max = safeMax,\n value = finiteValue.coerceIn(0f, safeMax)\n )\n }\n }\n}\n\n@Composable\nfun LumenProgress(\n value: Float,\n modifier: Modifier = Modifier,\n max: Float = 100f,\n label: String? = null\n) {\n val colors = LocalLumenTheme.current.colors\n val progress = LumenProgressValue.resolve(value, max)\n val semanticsModifier = Modifier.semantics {\n progressBarRangeInfo = ProgressBarRangeInfo(progress.value, 0f..progress.max)\n if (label != null) contentDescription = label\n }\n\n Box(\n modifier = modifier\n .then(semanticsModifier)\n .fillMaxWidth()\n .height(LumenSpacing.Sm)\n .clip(RoundedCornerShape(LumenRadius.Full))\n .background(colors.surfaceStrong)\n ) {\n Box(\n modifier = Modifier\n .fillMaxWidth(progress.fraction)\n .fillMaxHeight()\n .background(colors.brandSolid)\n )\n }\n}\n\nenum class LumenAvatarSize(val dimension: Dp) {\n Sm(32.dp),\n Md(40.dp),\n Lg(56.dp)\n}\n\n@Composable\nfun LumenAvatar(\n modifier: Modifier = Modifier,\n painter: Painter? = null,\n fallback: String = \"?\",\n size: LumenAvatarSize = LumenAvatarSize.Md,\n label: String? = null\n) {\n val colors = LocalLumenTheme.current.colors\n\n Box(\n modifier = modifier\n .size(size.dimension)\n .clip(CircleShape)\n .background(colors.surfaceMuted)\n .border(1.dp, colors.line, CircleShape)\n .clearAndSetSemantics {\n if (label != null) contentDescription = label\n },\n contentAlignment = Alignment.Center\n ) {\n if (painter == null) {\n Text(\n text = fallback,\n color = colors.ink,\n fontWeight = FontWeight.Bold\n )\n } else {\n Image(\n painter = painter,\n contentDescription = null,\n modifier = Modifier.fillMaxWidth().fillMaxHeight(),\n contentScale = ContentScale.Crop\n )\n }\n }\n}\n",
|
|
36813
38751
|
"packages/compose/src/main/kotlin/com/santi020k/lumen/StructuredComponents.kt": "package com.santi020k.lumen\n\nimport androidx.compose.foundation.BorderStroke\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.IntrinsicSize\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxHeight\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.heightIn\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.foundation.layout.widthIn\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.semantics.contentDescription\nimport androidx.compose.ui.semantics.heading\nimport androidx.compose.ui.semantics.semantics\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.text.style.TextOverflow\nimport androidx.compose.ui.unit.dp\n\nenum class LumenBannerVariant {\n Accent,\n Default,\n Destructive,\n Success,\n Warning\n}\n\nenum class LumenMetricTone {\n Accent,\n Brand,\n Danger,\n Neutral,\n Success,\n Warning\n}\n\n@Immutable\ndata class LumenBannerPalette(\n val accent: Color,\n val background: Color,\n val border: Color\n)\n\nfun lumenMetricColor(colors: LumenColorPalette, tone: LumenMetricTone): Color = when (tone) {\n LumenMetricTone.Accent -> colors.accent\n LumenMetricTone.Brand -> colors.brand\n LumenMetricTone.Danger -> colors.danger\n LumenMetricTone.Neutral -> colors.inkMuted\n LumenMetricTone.Success -> colors.success\n LumenMetricTone.Warning -> colors.warning\n}\n\nfun lumenBannerPalette(\n colors: LumenColorPalette,\n variant: LumenBannerVariant\n): LumenBannerPalette {\n val tone = when (variant) {\n LumenBannerVariant.Accent -> LumenMetricTone.Accent\n LumenBannerVariant.Default -> LumenMetricTone.Brand\n LumenBannerVariant.Destructive -> LumenMetricTone.Danger\n LumenBannerVariant.Success -> LumenMetricTone.Success\n LumenBannerVariant.Warning -> LumenMetricTone.Warning\n }\n val accent = lumenMetricColor(colors, tone)\n\n return LumenBannerPalette(\n accent = accent,\n background = if (variant == LumenBannerVariant.Default) colors.surface else accent.copy(alpha = 0.08f),\n border = if (variant == LumenBannerVariant.Default) colors.line else accent.copy(alpha = 0.32f)\n )\n}\n\n@Composable\nfun LumenEmptyState(\n title: String,\n modifier: Modifier = Modifier,\n description: String? = null,\n graphic: (@Composable () -> Unit)? = null,\n actions: (@Composable () -> Unit)? = null\n) {\n val colors = LocalLumenTheme.current.colors\n\n Column(\n modifier = modifier\n .fillMaxWidth()\n .widthIn(max = 440.dp)\n .padding(LumenSpacing.Xl),\n horizontalAlignment = Alignment.CenterHorizontally,\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Lg)\n ) {\n if (graphic != null) {\n Box(\n modifier = Modifier\n .size(56.dp)\n .clip(CircleShape)\n .background(colors.surfaceMuted),\n contentAlignment = Alignment.Center\n ) {\n graphic()\n }\n }\n\n Column(\n horizontalAlignment = Alignment.CenterHorizontally,\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Sm)\n ) {\n Text(\n text = title,\n color = colors.ink,\n style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),\n textAlign = TextAlign.Center\n )\n\n if (description != null) {\n Text(\n text = description,\n color = colors.inkMuted,\n style = MaterialTheme.typography.bodyMedium,\n textAlign = TextAlign.Center\n )\n }\n }\n\n actions?.invoke()\n }\n}\n\n@Composable\nfun LumenListRow(\n modifier: Modifier = Modifier,\n leading: (@Composable () -> Unit)? = null,\n trailing: (@Composable () -> Unit)? = null,\n content: @Composable () -> Unit\n) {\n Row(\n modifier = modifier\n .fillMaxWidth()\n .heightIn(min = 56.dp)\n .padding(horizontal = LumenSpacing.Lg, vertical = LumenSpacing.Md),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Md),\n verticalAlignment = Alignment.CenterVertically\n ) {\n leading?.invoke()\n\n Box(modifier = Modifier.weight(1f)) {\n content()\n }\n\n trailing?.invoke()\n }\n}\n\n@Composable\nfun LumenBanner(\n title: String,\n modifier: Modifier = Modifier,\n description: String? = null,\n variant: LumenBannerVariant = LumenBannerVariant.Default,\n dismissLabel: String = \"Dismiss\",\n onDismiss: (() -> Unit)? = null,\n graphic: (@Composable () -> Unit)? = null,\n actions: (@Composable () -> Unit)? = null\n) {\n val colors = LocalLumenTheme.current.colors\n val palette = lumenBannerPalette(colors, variant)\n val shape = RoundedCornerShape(LumenRadius.Md)\n\n Surface(\n modifier = modifier,\n color = palette.background,\n contentColor = colors.ink,\n shape = shape,\n border = BorderStroke(1.dp, palette.border)\n ) {\n Row(\n modifier = Modifier.height(IntrinsicSize.Min)\n ) {\n Box(\n modifier = Modifier\n .width(3.dp)\n .fillMaxHeight()\n .background(palette.accent)\n )\n Row(\n modifier = Modifier\n .weight(1f)\n .padding(horizontal = LumenSpacing.Lg, vertical = LumenSpacing.Md),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Md),\n verticalAlignment = Alignment.CenterVertically\n ) {\n graphic?.invoke()\n\n Column(\n modifier = Modifier.weight(1f),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)\n ) {\n Text(\n text = title,\n color = colors.ink,\n style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold)\n )\n\n if (description != null) {\n Text(\n text = description,\n color = colors.inkSoft,\n style = MaterialTheme.typography.bodySmall\n )\n }\n }\n\n actions?.invoke()\n\n if (onDismiss != null) {\n TextButton(onClick = onDismiss) {\n Text(dismissLabel, color = palette.accent)\n }\n }\n }\n }\n }\n}\n\n@Composable\nfun LumenStat(\n label: String,\n value: String,\n modifier: Modifier = Modifier,\n detail: String? = null,\n tone: LumenMetricTone = LumenMetricTone.Brand,\n graphic: (@Composable () -> Unit)? = null\n) {\n val colors = LocalLumenTheme.current.colors\n val accent = lumenMetricColor(colors, tone)\n val accessibleText = listOfNotNull(label, value, detail).joinToString(\", \")\n\n Column(\n modifier = modifier\n .semantics(mergeDescendants = true) { contentDescription = accessibleText }\n .background(accent.copy(alpha = 0.06f), RoundedCornerShape(LumenRadius.Md))\n .border(1.dp, accent.copy(alpha = 0.18f), RoundedCornerShape(LumenRadius.Md))\n .padding(LumenSpacing.Lg),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Sm)\n ) {\n graphic?.invoke()\n\n Text(\n text = value,\n color = colors.ink,\n style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Bold)\n )\n Text(\n text = label,\n color = colors.inkSoft,\n style = MaterialTheme.typography.labelSmall.copy(fontWeight = FontWeight.SemiBold)\n )\n\n if (detail != null) {\n Text(text = detail, color = colors.inkMuted, style = MaterialTheme.typography.bodySmall)\n }\n }\n}\n\n@Composable\nfun LumenSectionHeader(\n title: String,\n modifier: Modifier = Modifier,\n subtitle: String? = null,\n count: String? = null,\n actions: (@Composable () -> Unit)? = null\n) {\n val colors = LocalLumenTheme.current.colors\n\n Row(\n modifier = modifier.fillMaxWidth(),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Md),\n verticalAlignment = Alignment.Top\n ) {\n Column(\n modifier = Modifier.weight(1f),\n verticalArrangement = Arrangement.spacedBy(LumenSpacing.Xs)\n ) {\n Row(\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Sm),\n verticalAlignment = Alignment.CenterVertically\n ) {\n Text(\n text = title,\n modifier = Modifier.semantics { heading() },\n color = colors.ink,\n style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold)\n )\n if (count != null) LumenBadge(count)\n }\n\n if (subtitle != null) {\n Text(text = subtitle, color = colors.inkMuted, style = MaterialTheme.typography.bodySmall)\n }\n }\n\n actions?.invoke()\n }\n}\n\n@Composable\nfun LumenStatusBar(\n message: String,\n modifier: Modifier = Modifier,\n tone: LumenMetricTone = LumenMetricTone.Neutral,\n trailing: (@Composable () -> Unit)? = null\n) {\n val colors = LocalLumenTheme.current.colors\n\n Row(\n modifier = modifier\n .fillMaxWidth()\n .heightIn(min = 36.dp)\n .background(colors.surfaceMuted)\n .padding(horizontal = LumenSpacing.Lg),\n horizontalArrangement = Arrangement.spacedBy(LumenSpacing.Sm),\n verticalAlignment = Alignment.CenterVertically\n ) {\n Spacer(\n modifier = Modifier\n .size(7.dp)\n .clip(CircleShape)\n .background(lumenMetricColor(colors, tone))\n )\n Text(\n text = message,\n modifier = Modifier.weight(1f),\n color = colors.inkSoft,\n maxLines = 1,\n overflow = TextOverflow.Ellipsis,\n style = MaterialTheme.typography.bodySmall\n )\n trailing?.invoke()\n }\n}\n",
|
|
36814
|
-
"packages/compose/src/main/kotlin/com/santi020k/lumen/Tokens.generated.kt": "// This file is generated by scripts/generate-platform-tokens.mjs.\npackage com.santi020k.lumen\n\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\n\ndata class LumenColorPalette(\n val canvas: Color,\n val surface: Color,\n val surfaceMuted: Color,\n val surfaceStrong: Color,\n val line: Color,\n val ink: Color,\n val inkSoft: Color,\n val inkMuted: Color,\n val brand: Color,\n val brandSolid: Color,\n val brandSoft: Color,\n val onBrand: Color,\n val accent: Color,\n val success: Color,\n val warning: Color,\n val danger: Color,\n val onDanger: Color\n)\n\nobject LumenColors {\n val Light = LumenColorPalette(\n canvas = Color(0xFFFFFFFF),\n surface = Color(0xFFFFFFFF),\n surfaceMuted = Color(0xFFF3F4F6),\n surfaceStrong = Color(0xFFE5E7EB),\n line = Color(0xFFD7DAE0),\n ink = Color(0xFF0F1729),\n inkSoft = Color(0xFF454F5F),\n inkMuted = Color(0xFF6C7889),\n brand = Color(0xFF2463EB),\n brandSolid = Color(0xFF1452D7),\n brandSoft = Color(0xFFF0F5FF),\n onBrand = Color(0xFFFFFFFF),\n accent = Color(0xFF16A286),\n success = Color(0xFF1B9D4A),\n warning = Color(0xFFF59F0A),\n danger = Color(0xFFEF4343),\n onDanger = Color(0xFF000000)\n )\n\n val Dark = LumenColorPalette(\n canvas = Color(0xFF101219),\n surface = Color(0xFF171A21),\n surfaceMuted = Color(0xFF20242C),\n surfaceStrong = Color(0xFF303540),\n line = Color(0xFF393F4C),\n ink = Color(0xFFF3F5F7),\n inkSoft = Color(0xFFBCC3CD),\n inkMuted = Color(0xFF949CA8),\n brand = Color(0xFF29B4F5),\n brandSolid = Color(0xFF087AAF),\n brandSoft = Color(0xFF07354B),\n onBrand = Color(0xFFFFFFFF),\n accent = Color(0xFF1AC19A),\n success = Color(0xFF24C25E),\n warning = Color(0xFFF6A823),\n danger = Color(0xFFF15B5B),\n onDanger = Color(0xFF101219)\n )\n}\n\nobject LumenSpacing {\n val Zero = 0f.dp\n val Xs = 4f.dp\n val Sm = 8f.dp\n val Md = 12f.dp\n val Lg = 16f.dp\n val Xl = 24f.dp\n val Size2xl = 32f.dp\n val Size3xl = 48f.dp\n}\n\nobject LumenRadius {\n val Sm = 6f.dp\n val Md = 10f.dp\n val Lg = 12f.dp\n val Full = 9999f.dp\n}\n\nobject LumenTypography {\n val XsSize = 12f.sp\n val SmSize = 14f.sp\n val MdSize = 16f.sp\n val LgSize = 18f.sp\n val XlSize = 20f.sp\n val Size2xlSize = 24f.sp\n const val RegularWeight = 400\n const val MediumWeight = 500\n const val SemiboldWeight = 600\n const val BoldWeight = 700\n}\n\ndata class LumenCubicBezier(\n val x1: Float,\n val y1: Float,\n val x2: Float,\n val y2: Float\n)\n\nobject LumenMotion {\n const val FastDurationMillis = 120\n const val StandardDurationMillis = 160\n const val SlowDurationMillis = 300\n val StandardEasing = LumenCubicBezier(0.32f, 0.72f, 0f, 1f)\n val EmphasizedEasing = LumenCubicBezier(0.22f, 1f, 0.36f, 1f)\n}\n\nobject LumenElevation {\n val Resting = 1f.dp\n val Raised = 3f.dp\n val Overlay = 6f.dp\n}\n"
|
|
38752
|
+
"packages/compose/src/main/kotlin/com/santi020k/lumen/Tokens.generated.kt": "// This file is generated by scripts/generate-platform-tokens.mjs.\npackage com.santi020k.lumen\n\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\n\n@Immutable\ndata class LumenColorPalette(\n val canvas: Color,\n val surface: Color,\n val surfaceMuted: Color,\n val surfaceStrong: Color,\n val line: Color,\n val ink: Color,\n val inkSoft: Color,\n val inkMuted: Color,\n val brand: Color,\n val brandSolid: Color,\n val brandSoft: Color,\n val onBrand: Color,\n val accent: Color,\n val success: Color,\n val warning: Color,\n val danger: Color,\n val onDanger: Color\n)\n\nobject LumenColors {\n val Light = LumenColorPalette(\n canvas = Color(0xFFFFFFFF),\n surface = Color(0xFFFFFFFF),\n surfaceMuted = Color(0xFFF3F4F6),\n surfaceStrong = Color(0xFFE5E7EB),\n line = Color(0xFFD7DAE0),\n ink = Color(0xFF0F1729),\n inkSoft = Color(0xFF454F5F),\n inkMuted = Color(0xFF6C7889),\n brand = Color(0xFF2463EB),\n brandSolid = Color(0xFF1452D7),\n brandSoft = Color(0xFFF0F5FF),\n onBrand = Color(0xFFFFFFFF),\n accent = Color(0xFF16A286),\n success = Color(0xFF1B9D4A),\n warning = Color(0xFFF59F0A),\n danger = Color(0xFFEF4343),\n onDanger = Color(0xFF000000)\n )\n\n val Dark = LumenColorPalette(\n canvas = Color(0xFF101219),\n surface = Color(0xFF171A21),\n surfaceMuted = Color(0xFF20242C),\n surfaceStrong = Color(0xFF303540),\n line = Color(0xFF393F4C),\n ink = Color(0xFFF3F5F7),\n inkSoft = Color(0xFFBCC3CD),\n inkMuted = Color(0xFF949CA8),\n brand = Color(0xFF29B4F5),\n brandSolid = Color(0xFF087AAF),\n brandSoft = Color(0xFF07354B),\n onBrand = Color(0xFFFFFFFF),\n accent = Color(0xFF1AC19A),\n success = Color(0xFF24C25E),\n warning = Color(0xFFF6A823),\n danger = Color(0xFFF15B5B),\n onDanger = Color(0xFF101219)\n )\n}\n\nobject LumenSpacing {\n val Zero = 0f.dp\n val Xs = 4f.dp\n val Sm = 8f.dp\n val Md = 12f.dp\n val Lg = 16f.dp\n val Xl = 24f.dp\n val Size2xl = 32f.dp\n val Size3xl = 48f.dp\n}\n\nobject LumenRadius {\n val Sm = 6f.dp\n val Md = 10f.dp\n val Lg = 12f.dp\n val Full = 9999f.dp\n}\n\nobject LumenTypography {\n val XsSize = 12f.sp\n val SmSize = 14f.sp\n val MdSize = 16f.sp\n val LgSize = 18f.sp\n val XlSize = 20f.sp\n val Size2xlSize = 24f.sp\n const val RegularWeight = 400\n const val MediumWeight = 500\n const val SemiboldWeight = 600\n const val BoldWeight = 700\n}\n\n@Immutable\ndata class LumenCubicBezier(\n val x1: Float,\n val y1: Float,\n val x2: Float,\n val y2: Float\n)\n\nobject LumenMotion {\n const val FastDurationMillis = 120\n const val StandardDurationMillis = 160\n const val SlowDurationMillis = 300\n val StandardEasing = LumenCubicBezier(0.32f, 0.72f, 0f, 1f)\n val EmphasizedEasing = LumenCubicBezier(0.22f, 1f, 0.36f, 1f)\n}\n\nobject LumenElevation {\n val Resting = 1f.dp\n val Raised = 3f.dp\n val Overlay = 6f.dp\n}\n"
|
|
36815
38753
|
},
|
|
36816
38754
|
"react-native": {
|
|
36817
38755
|
"packages/react-native/src/additional-components.tsx": "import {\n type ReactElement,\n type ReactNode,\n type Ref\n} from 'react'\nimport {\n type HostInstance,\n Pressable,\n type PressableProps,\n Text,\n TextInput,\n type TextInputInstance,\n type TextInputProps,\n type TextStyle,\n View,\n type ViewProps\n} from 'react-native'\n\nimport { resolveLumenButtonOpacity } from './recipes.js'\nimport { type LumenAlertVariant, resolveLumenAlertColors } from './shared-recipes.js'\nimport { useLumenTheme } from './theme-context.js'\n\nconst resolveOptional = <Value,>(value: Value | undefined, fallback: Value): Value => value ?? fallback\n\nconst resolveTextareaBorderColor = (\n errorMessage: string | undefined,\n dangerColor: string,\n lineColor: string\n): string => errorMessage ? dangerColor : lineColor\n\nconst resolveTextareaOpacity = (editable: boolean): number => editable ? 1 : 0.52\n\nconst LumenTextareaMessage = ({\n color,\n fontSize,\n message\n}: {\n color: string\n fontSize: number\n message: string | undefined\n}): ReactElement | null => message ? <Text style={{ color, fontSize }}>{message}</Text> : null\n\nexport interface LumenToastProps extends ViewProps {\n action?: ReactNode\n description?: string\n dismissLabel?: string\n onDismiss?: () => void\n ref?: Ref<HostInstance>\n title: string\n variant?: LumenAlertVariant\n}\n\nexport const LumenToast = ({\n action,\n description,\n dismissLabel = 'Dismiss',\n onDismiss,\n ref,\n style,\n title,\n variant = 'default',\n ...props\n}: LumenToastProps): ReactElement => {\n const theme = useLumenTheme()\n const colors = resolveLumenAlertColors(theme.colors, variant)\n\n return (\n <View\n ref={ref}\n {...props}\n accessibilityRole=\"alert\"\n style={[\n {\n alignItems: 'center',\n backgroundColor: colors.backgroundColor,\n borderColor: colors.borderColor,\n borderRadius: theme.radii.md,\n borderWidth: 1,\n flexDirection: 'row',\n gap: theme.spacing.md,\n paddingHorizontal: theme.spacing.lg,\n paddingVertical: theme.spacing.md\n },\n style\n ]}\n >\n <View style={{ flex: 1, gap: theme.spacing.xs }}>\n <Text\n style={{\n color: colors.color,\n fontSize: theme.fontSizes.sm,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {title}\n </Text>\n {description ?\n <Text style={{ color: theme.colors.inkSoft, fontSize: theme.fontSizes.xs }}>{description}</Text> :\n null}\n </View>\n {action}\n {onDismiss ?\n (\n <Pressable\n accessibilityLabel={dismissLabel}\n accessibilityRole=\"button\"\n onPress={onDismiss}\n style={({ pressed }) => ({\n opacity: resolveLumenButtonOpacity(false, pressed),\n padding: theme.spacing.sm\n })}\n >\n <Text style={{ color: colors.color, fontSize: theme.fontSizes.xs }}>×</Text>\n </Pressable>\n ) :\n null}\n </View>\n )\n}\n\nexport interface LumenTextareaProps extends Omit<\n TextInputProps,\n 'multiline' | 'onChangeText' | 'value'\n> {\n description?: string\n errorMessage?: string\n label: string\n onChangeText: (value: string) => void\n ref?: Ref<TextInputInstance>\n value: string\n}\n\nexport const LumenTextarea = ({\n description,\n editable = true,\n errorMessage,\n label,\n numberOfLines = 4,\n onChangeText,\n placeholder,\n placeholderTextColor,\n ref,\n style,\n value,\n ...props\n}: LumenTextareaProps): ReactElement => {\n const theme = useLumenTheme()\n\n const supportingColor = resolveTextareaBorderColor(\n errorMessage,\n theme.colors.danger,\n theme.colors.inkMuted\n )\n\n const supportingMessage = resolveOptional(errorMessage, description)\n\n return (\n <View style={{ gap: theme.spacing.xs }}>\n <Text\n style={{\n color: theme.colors.ink,\n fontSize: theme.fontSizes.sm,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {label}\n </Text>\n <TextInput\n ref={ref}\n {...props}\n accessibilityHint={resolveOptional(errorMessage, description)}\n accessibilityLabel={resolveOptional(props.accessibilityLabel, label)}\n editable={editable}\n multiline\n numberOfLines={numberOfLines}\n onChangeText={onChangeText}\n placeholder={placeholder}\n placeholderTextColor={resolveOptional(placeholderTextColor, theme.colors.inkMuted)}\n style={[\n {\n backgroundColor: theme.colors.surface,\n borderColor: resolveTextareaBorderColor(\n errorMessage,\n theme.colors.danger,\n theme.colors.line\n ),\n borderRadius: theme.radii.sm,\n borderWidth: 1,\n color: theme.colors.ink,\n fontSize: theme.fontSizes.sm,\n minHeight: 112,\n opacity: resolveTextareaOpacity(editable),\n padding: theme.spacing.md,\n textAlignVertical: 'top'\n },\n style\n ]}\n value={value}\n />\n <LumenTextareaMessage\n color={supportingColor}\n fontSize={theme.fontSizes.xs}\n message={supportingMessage}\n />\n </View>\n )\n}\n\nexport interface LumenChipProps extends Omit<PressableProps, 'children'> {\n label: string\n onRemove?: () => void\n ref?: Ref<HostInstance>\n removeLabel?: string\n selected?: boolean\n}\n\nexport const LumenChip = ({\n disabled = false,\n label,\n onPress,\n onRemove,\n ref,\n removeLabel = `Remove ${label}`,\n selected = false,\n style,\n ...props\n}: LumenChipProps): ReactElement => {\n const theme = useLumenTheme()\n\n const content = (\n <>\n <Text\n style={{\n color: selected ? theme.colors.brand : theme.colors.inkSoft,\n fontSize: theme.fontSizes.xs,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {label}\n </Text>\n {onRemove ?\n (\n <Pressable\n accessibilityLabel={removeLabel}\n accessibilityRole=\"button\"\n disabled={disabled}\n hitSlop={6}\n onPress={event => {\n event.stopPropagation()\n\n onRemove()\n }}\n >\n <Text style={{ color: theme.colors.inkMuted, fontSize: theme.fontSizes.xs }}>×</Text>\n </Pressable>\n ) :\n null}\n </>\n )\n\n const baseStyle = {\n alignItems: 'center' as const,\n alignSelf: 'flex-start' as const,\n backgroundColor: selected ? theme.colors.brandSoft : theme.colors.surfaceMuted,\n borderColor: selected ? theme.colors.brand : theme.colors.line,\n borderRadius: theme.radii.full,\n borderWidth: 1,\n flexDirection: 'row' as const,\n gap: theme.spacing.xs,\n minHeight: 32,\n opacity: disabled ? 0.52 : 1,\n paddingHorizontal: theme.spacing.md\n }\n\n if (!onPress) {\n return <View ref={ref} {...props} style={[baseStyle, style as ViewProps['style']]}>{content}</View>\n }\n\n return (\n <Pressable\n ref={ref}\n {...props}\n accessibilityRole=\"button\"\n accessibilityState={{ ...props.accessibilityState, disabled, selected }}\n disabled={disabled}\n onPress={onPress}\n style={state => [\n baseStyle,\n { opacity: resolveLumenButtonOpacity(disabled, state.pressed) },\n typeof style === 'function' ? style(state) : style\n ]}\n >\n {content}\n </Pressable>\n )\n}\n\nexport interface LumenFieldGroupProps extends ViewProps {\n children: ReactNode\n description?: string\n errorMessage?: string\n label: string\n ref?: Ref<HostInstance>\n required?: boolean\n}\n\nexport const LumenFieldGroup = ({\n children,\n description,\n errorMessage,\n label,\n ref,\n required = false,\n style,\n ...props\n}: LumenFieldGroupProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <View ref={ref} {...props} style={[{ gap: theme.spacing.sm }, style]}>\n <Text\n style={{\n color: theme.colors.ink,\n fontSize: theme.fontSizes.sm,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {label}\n {required ? ' *' : ''}\n </Text>\n {description ?\n <Text style={{ color: theme.colors.inkMuted, fontSize: theme.fontSizes.xs }}>{description}</Text> :\n null}\n {children}\n {errorMessage ?\n (\n <Text accessibilityRole=\"alert\" style={{ color: theme.colors.danger, fontSize: theme.fontSizes.xs }}>\n {errorMessage}\n </Text>\n ) :\n null}\n </View>\n )\n}\n\nexport type LumenButtonGroupOrientation = 'horizontal' | 'vertical'\n\nexport interface LumenButtonGroupProps extends ViewProps {\n children: ReactNode\n orientation?: LumenButtonGroupOrientation\n ref?: Ref<HostInstance>\n}\n\nexport const LumenButtonGroup = ({\n children,\n orientation = 'horizontal',\n ref,\n style,\n ...props\n}: LumenButtonGroupProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <View\n ref={ref}\n {...props}\n style={[\n {\n alignItems: orientation === 'horizontal' ? 'center' : 'stretch',\n flexDirection: orientation === 'horizontal' ? 'row' : 'column',\n gap: theme.spacing.sm\n },\n style\n ]}\n >\n {children}\n </View>\n )\n}\n",
|
|
36818
|
-
"packages/react-native/src/content-components.tsx": "import { type ReactElement, type ReactNode, type Ref } from 'react'\nimport {\n type DimensionValue,\n type HostInstance,\n Pressable,\n Text,\n type TextStyle,\n View,\n type ViewProps\n} from 'react-native'\n\nimport { resolveLumenDisclosureState, resolveLumenSkeletonHeight } from './content-recipes.js'\nimport { resolveLumenButtonOpacity } from './recipes.js'\nimport { useLumenTheme } from './theme-context.js'\n\nexport type LumenSkeletonShape = 'circle' | 'rectangle' | 'text'\n\nexport interface LumenSkeletonProps extends Omit<ViewProps, 'children'> {\n height?: number\n label?: string\n ref?: Ref<HostInstance>\n shape?: LumenSkeletonShape\n width?: DimensionValue\n}\n\nexport const LumenSkeleton = ({\n height = 16,\n label,\n ref,\n shape = 'text',\n style,\n width,\n ...props\n}: LumenSkeletonProps): ReactElement => {\n const theme = useLumenTheme()\n const safeHeight = resolveLumenSkeletonHeight(height)\n const resolvedWidth = width ?? (shape === 'circle' ? safeHeight : '100%')\n let borderRadius: number = theme.radii.md\n\n if (shape === 'circle') borderRadius = safeHeight / 2\n\n if (shape === 'text') borderRadius = theme.radii.sm\n\n return (\n <View\n ref={ref}\n {...props}\n accessibilityElementsHidden={!label}\n accessibilityLabel={label}\n accessibilityRole={label ? 'progressbar' : undefined}\n aria-busy={label ? true : undefined}\n importantForAccessibility={label ? 'yes' : 'no-hide-descendants'}\n style={[\n {\n backgroundColor: theme.colors.surfaceStrong,\n borderRadius,\n height: safeHeight,\n opacity: 0.72,\n width: resolvedWidth\n },\n style\n ]}\n />\n )\n}\n\nexport interface LumenDisclosureProps extends Omit<ViewProps, 'children'> {\n children: ReactNode\n description?: string\n disabled?: boolean\n expanded: boolean\n graphic?: ReactNode\n onExpandedChange: (expanded: boolean) => void\n ref?: Ref<HostInstance>\n title: string\n}\n\nexport const LumenDisclosure = ({\n children,\n description,\n disabled = false,\n expanded,\n graphic,\n onExpandedChange,\n ref,\n style,\n title,\n ...props\n}: LumenDisclosureProps): ReactElement => {\n const theme = useLumenTheme()\n const state = resolveLumenDisclosureState(expanded, disabled)\n\n return (\n <View\n ref={ref}\n {...props}\n style={[\n {\n backgroundColor: theme.colors.surface,\n borderColor: theme.colors.line,\n borderRadius: theme.radii.md,\n borderWidth: 1,\n overflow: 'hidden'\n },\n style\n ]}\n >\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ disabled, expanded }}\n aria-disabled={disabled}\n aria-expanded={expanded}\n disabled={disabled}\n onPress={() => {\n onExpandedChange(!expanded)\n }}\n style={({ pressed }) => ({\n alignItems: 'center',\n flexDirection: 'row',\n gap: theme.spacing.md,\n minHeight: 48,\n opacity: state.opacity * resolveLumenButtonOpacity(disabled, pressed),\n paddingHorizontal: theme.spacing.lg,\n paddingVertical: theme.spacing.md\n })}\n >\n {graphic ? <View>{graphic}</View> : null}\n <View style={{ flex: 1, gap: theme.spacing.xs }}>\n <Text\n style={{\n color: theme.colors.ink,\n fontSize: theme.fontSizes.sm,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {title}\n </Text>\n {description ?\n <Text style={{ color: theme.colors.inkMuted, fontSize: theme.fontSizes.xs }}>{description}</Text> :\n null}\n </View>\n <Text\n accessibilityElementsHidden\n importantForAccessibility=\"no-hide-descendants\"\n style={{ color: theme.colors.inkSoft, fontSize: theme.fontSizes.lg }}\n >\n {expanded ? '−' : '+'}\n </Text>\n </Pressable>\n {state.contentVisible ?\n (\n <View\n style={{\n borderTopColor: theme.colors.line,\n borderTopWidth: 1,\n padding: theme.spacing.lg\n }}\n >\n {children}\n </View>\n ) :\n null}\n </View>\n )\n}\n",
|
|
36819
|
-
"packages/react-native/src/content-recipes.test.ts": "import { describe, expect, test } from 'vitest'\n\nimport {\n resolveLumenDisclosureState,\n resolveLumenSkeletonHeight\n} from './content-recipes.js'\n\ndescribe('content state recipes', () => {\n test('preserves controlled disclosure state', () => {\n expect(resolveLumenDisclosureState(true, false)).toEqual({\n contentVisible: true,\n disabled: false,\n opacity: 1\n })\n expect(resolveLumenDisclosureState(false, true)).toEqual({\n contentVisible: false,\n disabled: true,\n opacity: 0.52\n })\n })\n\n test('normalizes invalid skeleton heights', () => {\n expect(resolveLumenSkeletonHeight(24)).toBe(24)\n expect(resolveLumenSkeletonHeight(0)).toBe(16)\n expect(resolveLumenSkeletonHeight(Number.NaN)).toBe(16)\n })\n})\n",
|
|
36820
|
-
"packages/react-native/src/content-recipes.ts": "export interface LumenDisclosureState {\n contentVisible: boolean\n disabled: boolean\n opacity: number\n}\n\nexport const resolveLumenDisclosureState = (\n expanded: boolean,\n disabled: boolean\n): LumenDisclosureState => ({\n contentVisible: expanded,\n disabled,\n opacity: disabled ? 0.52 : 1\n})\n\nexport const resolveLumenSkeletonHeight = (height: number): number => (\n Number.isFinite(height) && height > 0 ? height : 16\n)\n",
|
|
38756
|
+
"packages/react-native/src/content-components.tsx": "import { type ReactElement, type ReactNode, type Ref } from 'react'\nimport {\n type DimensionValue,\n type HostInstance,\n Pressable,\n Text,\n type TextStyle,\n View,\n type ViewProps\n} from 'react-native'\n\nimport {\n type LumenBackdropIntensity,\n type LumenGraphicSize,\n type LumenIllustrationSize,\n resolveLumenBackdropOpacity,\n resolveLumenDisclosureState,\n resolveLumenGraphicSize,\n resolveLumenIllustrationSize,\n resolveLumenSkeletonHeight } from './content-recipes.js'\nimport { resolveLumenButtonOpacity } from './recipes.js'\nimport { useLumenTheme } from './theme-context.js'\n\nexport type LumenSkeletonShape = 'circle' | 'rectangle' | 'text'\n\nexport interface LumenSkeletonProps extends Omit<ViewProps, 'children'> {\n height?: number\n label?: string\n ref?: Ref<HostInstance>\n shape?: LumenSkeletonShape\n width?: DimensionValue\n}\n\nexport const LumenSkeleton = ({\n height = 16,\n label,\n ref,\n shape = 'text',\n style,\n width,\n ...props\n}: LumenSkeletonProps): ReactElement => {\n const theme = useLumenTheme()\n const safeHeight = resolveLumenSkeletonHeight(height)\n const resolvedWidth = width ?? (shape === 'circle' ? safeHeight : '100%')\n let borderRadius: number = theme.radii.md\n\n if (shape === 'circle') borderRadius = safeHeight / 2\n\n if (shape === 'text') borderRadius = theme.radii.sm\n\n return (\n <View\n ref={ref}\n {...props}\n accessibilityElementsHidden={!label}\n accessibilityLabel={label}\n accessibilityRole={label ? 'progressbar' : undefined}\n aria-busy={label ? true : undefined}\n importantForAccessibility={label ? 'yes' : 'no-hide-descendants'}\n style={[\n {\n backgroundColor: theme.colors.surfaceStrong,\n borderRadius,\n height: safeHeight,\n opacity: 0.72,\n width: resolvedWidth\n },\n style\n ]}\n />\n )\n}\n\nexport type LumenGraphicTone = 'accent' | 'brand' | 'neutral'\nexport type LumenGraphicVariant = 'glow' | 'grid' | 'orbit'\n\nexport interface LumenGraphicProps extends ViewProps {\n children?: ReactNode\n label?: string\n ref?: Ref<HostInstance>\n size?: LumenGraphicSize\n tone?: LumenGraphicTone\n variant?: LumenGraphicVariant\n}\n\nconst graphicLines = Array.from({ length: 7 }, (_, index) => index)\nconst backdropDots = Array.from({ length: 48 }, (_, index) => index)\n\nexport const LumenGraphic = ({\n children,\n label,\n ref,\n size = 'md',\n style,\n tone = 'brand',\n variant = 'orbit',\n ...props\n}: LumenGraphicProps): ReactElement => {\n const theme = useLumenTheme()\n const dimension = resolveLumenGraphicSize(size)\n\n const color = {\n accent: theme.colors.accent,\n brand: theme.colors.brand,\n neutral: theme.colors.inkMuted\n }[tone]\n\n return (\n <View\n ref={ref}\n {...props}\n accessible={Boolean(label)}\n accessibilityElementsHidden={!label}\n accessibilityLabel={label}\n accessibilityRole={label ? 'image' : undefined}\n importantForAccessibility={label ? 'yes' : 'no-hide-descendants'}\n style={[\n {\n alignItems: 'center',\n height: dimension,\n justifyContent: 'center',\n overflow: 'hidden',\n position: 'relative',\n width: dimension\n },\n style\n ]}\n >\n {variant === 'glow' ?\n <View style={{ backgroundColor: color, borderRadius: dimension / 2, height: '72%', opacity: 0.18, width: '72%' }} /> :\n null}\n {variant === 'grid' ?\n (\n <View style={{ height: '76%', opacity: 0.18, position: 'absolute', width: '76%' }}>\n {graphicLines.map(index => (\n <View\n key={`horizontal-${index}`}\n style={{ backgroundColor: color, height: 1, left: 0, position: 'absolute', right: 0, top: `${index * 16.66}%` }}\n />\n ))}\n {graphicLines.map(index => (\n <View\n key={`vertical-${index}`}\n style={{ backgroundColor: color, bottom: 0, left: `${index * 16.66}%`, position: 'absolute', top: 0, width: 1 }}\n />\n ))}\n </View>\n ) :\n null}\n {variant === 'orbit' ?\n (\n <>\n {[0.9, 0.62, 0.34].map(ratio => (\n <View\n key={ratio}\n style={{\n borderColor: color,\n borderRadius: dimension,\n borderWidth: 1,\n height: dimension * ratio,\n opacity: 0.24,\n position: 'absolute',\n width: dimension * ratio\n }}\n />\n ))}\n <View style={{ backgroundColor: color, height: 1, opacity: 0.18, position: 'absolute', width: '72%' }} />\n <View style={{ backgroundColor: color, height: '72%', opacity: 0.18, position: 'absolute', width: 1 }} />\n </>\n ) :\n null}\n <View style={{ alignItems: 'center', justifyContent: 'center', position: 'absolute' }}>\n {children}\n </View>\n </View>\n )\n}\n\nexport type LumenBackdropTone = 'accent' | 'brand' | 'neutral'\nexport type LumenBackdropVariant = 'aurora' | 'dots' | 'grid' | 'rays'\n\nexport interface LumenBackdropProps extends ViewProps {\n children?: ReactNode\n intensity?: LumenBackdropIntensity\n ref?: Ref<HostInstance>\n tone?: LumenBackdropTone\n variant?: LumenBackdropVariant\n}\n\nexport const LumenBackdrop = ({\n children,\n intensity = 'medium',\n ref,\n style,\n tone = 'brand',\n variant = 'aurora',\n ...props\n}: LumenBackdropProps): ReactElement => {\n const theme = useLumenTheme()\n const color = { accent: theme.colors.accent, brand: theme.colors.brand, neutral: theme.colors.inkMuted }[tone]\n const secondaryColor = tone === 'neutral' ? theme.colors.surfaceStrong : theme.colors.accent\n const opacity = resolveLumenBackdropOpacity(intensity)\n\n return (\n <View ref={ref} {...props} style={[{ minHeight: 160, overflow: 'hidden', position: 'relative' }, style]}>\n <View\n accessibilityElementsHidden\n importantForAccessibility=\"no-hide-descendants\"\n pointerEvents=\"none\"\n style={{ bottom: 0, left: 0, opacity, position: 'absolute', right: 0, top: 0 }}\n >\n {variant === 'aurora' ?\n (\n <>\n <View style={{ backgroundColor: color, borderRadius: 140, height: 280, left: -70, opacity: 0.22, position: 'absolute', top: -100, width: 280 }} />\n <View style={{ backgroundColor: secondaryColor, borderRadius: 120, bottom: -110, height: 240, opacity: 0.18, position: 'absolute', right: -50, width: 240 }} />\n </>\n ) :\n null}\n {variant === 'grid' ?\n (\n <>\n {graphicLines.map(index => (\n <View key={`backdrop-horizontal-${index}`} style={{ backgroundColor: color, height: 1, left: 0, opacity: 0.18, position: 'absolute', right: 0, top: `${index * 16.66}%` }} />\n ))}\n {graphicLines.map(index => (\n <View key={`backdrop-vertical-${index}`} style={{ backgroundColor: color, bottom: 0, left: `${index * 16.66}%`, opacity: 0.18, position: 'absolute', top: 0, width: 1 }} />\n ))}\n </>\n ) :\n null}\n {variant === 'dots' ?\n backdropDots.map(index => (\n <View\n key={`backdrop-dot-${index}`}\n style={{\n backgroundColor: color,\n borderRadius: 2,\n height: 3,\n left: `${(index % 8) * 14 + 1}%`,\n opacity: 0.28,\n position: 'absolute',\n top: `${Math.floor(index / 8) * 19 + 2}%`,\n width: 3\n }}\n />\n )) :\n null}\n {variant === 'rays' ?\n graphicLines.map(index => (\n <View\n key={`backdrop-ray-${index}`}\n style={{\n backgroundColor: color,\n height: 1,\n left: '50%',\n opacity: 0.2,\n position: 'absolute',\n top: '50%',\n transform: [{ rotate: `${index * 24 - 72}deg` }, { translateX: -240 }],\n width: 480\n }}\n />\n )) :\n null}\n </View>\n <View style={{ zIndex: 1 }}>{children}</View>\n </View>\n )\n}\n\nexport type LumenIllustrationTone = 'accent' | 'auto' | 'brand' | 'neutral'\nexport type LumenIllustrationVariant = 'empty' | 'error' | 'offline' | 'success'\n\nexport interface LumenIllustrationProps extends ViewProps {\n label?: string\n ref?: Ref<HostInstance>\n size?: LumenIllustrationSize\n tone?: LumenIllustrationTone\n variant?: LumenIllustrationVariant\n}\n\ninterface LumenIllustrationArtworkProps {\n color: string\n dimension: number\n strokeWidth: number\n variant: LumenIllustrationVariant\n}\n\nconst LumenIllustrationArtwork = ({\n color,\n dimension,\n strokeWidth,\n variant\n}: LumenIllustrationArtworkProps): ReactElement | null => {\n const line = (width: number, rotation: string, left: number, top: number): ReactElement => (\n <View\n style={{\n backgroundColor: color,\n borderRadius: strokeWidth,\n height: strokeWidth,\n left,\n position: 'absolute',\n top,\n transform: [{ rotate: rotation }],\n width\n }}\n />\n )\n\n if (variant === 'empty') {\n return (\n <View\n style={{\n borderColor: color,\n borderRadius: dimension * 0.08,\n borderWidth: strokeWidth,\n height: dimension * 0.34,\n marginTop: dimension * 0.14,\n width: dimension * 0.52\n }}\n >\n <View style={{ backgroundColor: color, height: strokeWidth, left: '18%', opacity: 0.7, position: 'absolute', top: '38%', width: '64%' }} />\n </View>\n )\n }\n\n if (variant === 'offline') {\n return (\n <View style={{ height: dimension * 0.52, position: 'relative', width: dimension * 0.64 }}>\n <View style={{ borderColor: color, borderRadius: dimension, borderWidth: strokeWidth, bottom: dimension * 0.08, height: dimension * 0.32, position: 'absolute', width: '100%' }} />\n {line(dimension * 0.72, '45deg', -dimension * 0.04, dimension * 0.24)}\n </View>\n )\n }\n\n return (\n <View style={{ borderColor: color, borderRadius: dimension, borderWidth: strokeWidth, height: dimension * 0.52, position: 'relative', width: dimension * 0.52 }}>\n {variant === 'success' ?\n (\n <>\n {line(dimension * 0.16, '45deg', dimension * 0.1, dimension * 0.25)}\n {line(dimension * 0.26, '-45deg', dimension * 0.19, dimension * 0.21)}\n </>\n ) :\n (\n <>\n {line(dimension * 0.28, '45deg', dimension * 0.11, dimension * 0.25)}\n {line(dimension * 0.28, '-45deg', dimension * 0.11, dimension * 0.25)}\n </>\n )}\n </View>\n )\n}\n\nexport const LumenIllustration = ({\n label,\n ref,\n size = 'md',\n style,\n tone = 'auto',\n variant = 'empty',\n ...props\n}: LumenIllustrationProps): ReactElement => {\n const theme = useLumenTheme()\n const dimension = resolveLumenIllustrationSize(size)\n\n const semanticColor = {\n empty: theme.colors.brand,\n error: theme.colors.danger,\n offline: theme.colors.inkMuted,\n success: theme.colors.success\n }[variant]\n\n const color = tone === 'auto' ?\n semanticColor :\n {\n accent: theme.colors.accent,\n brand: theme.colors.brand,\n neutral: theme.colors.inkMuted\n }[tone]\n\n const strokeWidth = Math.max(2, dimension / 48)\n\n return (\n <View\n ref={ref}\n {...props}\n accessible={Boolean(label)}\n accessibilityElementsHidden={!label}\n accessibilityLabel={label}\n accessibilityRole={label ? 'image' : undefined}\n importantForAccessibility={label ? 'yes' : 'no-hide-descendants'}\n style={[{ alignItems: 'center', height: dimension, justifyContent: 'center', position: 'relative', width: dimension }, style]}\n >\n <View style={{ backgroundColor: color, borderRadius: dimension, height: '82%', opacity: 0.1, position: 'absolute', width: '82%' }} />\n <LumenIllustrationArtwork color={color} dimension={dimension} strokeWidth={strokeWidth} variant={variant} />\n </View>\n )\n}\n\nexport interface LumenDisclosureProps extends Omit<ViewProps, 'children'> {\n children: ReactNode\n description?: string\n disabled?: boolean\n expanded: boolean\n graphic?: ReactNode\n onExpandedChange: (expanded: boolean) => void\n ref?: Ref<HostInstance>\n title: string\n}\n\nexport const LumenDisclosure = ({\n children,\n description,\n disabled = false,\n expanded,\n graphic,\n onExpandedChange,\n ref,\n style,\n title,\n ...props\n}: LumenDisclosureProps): ReactElement => {\n const theme = useLumenTheme()\n const state = resolveLumenDisclosureState(expanded, disabled)\n\n return (\n <View\n ref={ref}\n {...props}\n style={[\n {\n backgroundColor: theme.colors.surface,\n borderColor: theme.colors.line,\n borderRadius: theme.radii.md,\n borderWidth: 1,\n overflow: 'hidden'\n },\n style\n ]}\n >\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ disabled, expanded }}\n aria-disabled={disabled}\n aria-expanded={expanded}\n disabled={disabled}\n onPress={() => {\n onExpandedChange(!expanded)\n }}\n style={({ pressed }) => ({\n alignItems: 'center',\n flexDirection: 'row',\n gap: theme.spacing.md,\n minHeight: 48,\n opacity: state.opacity * resolveLumenButtonOpacity(disabled, pressed),\n paddingHorizontal: theme.spacing.lg,\n paddingVertical: theme.spacing.md\n })}\n >\n {graphic ? <View>{graphic}</View> : null}\n <View style={{ flex: 1, gap: theme.spacing.xs }}>\n <Text\n style={{\n color: theme.colors.ink,\n fontSize: theme.fontSizes.sm,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {title}\n </Text>\n {description ?\n <Text style={{ color: theme.colors.inkMuted, fontSize: theme.fontSizes.xs }}>{description}</Text> :\n null}\n </View>\n <Text\n accessibilityElementsHidden\n importantForAccessibility=\"no-hide-descendants\"\n style={{ color: theme.colors.inkSoft, fontSize: theme.fontSizes.lg }}\n >\n {expanded ? '−' : '+'}\n </Text>\n </Pressable>\n {state.contentVisible ?\n (\n <View\n style={{\n borderTopColor: theme.colors.line,\n borderTopWidth: 1,\n padding: theme.spacing.lg\n }}\n >\n {children}\n </View>\n ) :\n null}\n </View>\n )\n}\n",
|
|
38757
|
+
"packages/react-native/src/content-recipes.test.ts": "import { describe, expect, test } from 'vitest'\n\nimport {\n resolveLumenBackdropOpacity,\n resolveLumenDisclosureState,\n resolveLumenGraphicSize,\n resolveLumenIllustrationSize,\n resolveLumenSkeletonHeight\n} from './content-recipes.js'\n\ndescribe('content state recipes', () => {\n test('preserves controlled disclosure state', () => {\n expect(resolveLumenDisclosureState(true, false)).toEqual({\n contentVisible: true,\n disabled: false,\n opacity: 1\n })\n expect(resolveLumenDisclosureState(false, true)).toEqual({\n contentVisible: false,\n disabled: true,\n opacity: 0.52\n })\n })\n\n test('normalizes invalid skeleton heights', () => {\n expect(resolveLumenSkeletonHeight(24)).toBe(24)\n expect(resolveLumenSkeletonHeight(0)).toBe(16)\n expect(resolveLumenSkeletonHeight(Number.NaN)).toBe(16)\n })\n\n test('keeps graphic sizes aligned with the shared contract', () => {\n expect(resolveLumenGraphicSize('sm')).toBe(160)\n expect(resolveLumenGraphicSize('md')).toBe(240)\n expect(resolveLumenGraphicSize('lg')).toBe(320)\n })\n\n test('normalizes backdrop intensity and illustration sizes', () => {\n expect(resolveLumenBackdropOpacity('subtle')).toBe(0.4)\n expect(resolveLumenBackdropOpacity('medium')).toBe(0.68)\n expect(resolveLumenBackdropOpacity('strong')).toBe(1)\n expect(resolveLumenIllustrationSize('sm')).toBe(96)\n expect(resolveLumenIllustrationSize('md')).toBe(128)\n expect(resolveLumenIllustrationSize('lg')).toBe(176)\n })\n})\n",
|
|
38758
|
+
"packages/react-native/src/content-recipes.ts": "export interface LumenDisclosureState {\n contentVisible: boolean\n disabled: boolean\n opacity: number\n}\n\nexport const resolveLumenDisclosureState = (\n expanded: boolean,\n disabled: boolean\n): LumenDisclosureState => ({\n contentVisible: expanded,\n disabled,\n opacity: disabled ? 0.52 : 1\n})\n\nexport const resolveLumenSkeletonHeight = (height: number): number => (\n Number.isFinite(height) && height > 0 ? height : 16\n)\n\nexport type LumenGraphicSize = 'lg' | 'md' | 'sm'\n\nexport const resolveLumenGraphicSize = (size: LumenGraphicSize): number => ({\n lg: 320,\n md: 240,\n sm: 160\n})[size]\n\nexport type LumenBackdropIntensity = 'medium' | 'strong' | 'subtle'\n\nexport const resolveLumenBackdropOpacity = (\n intensity: LumenBackdropIntensity\n): number => ({ medium: 0.68, strong: 1, subtle: 0.4 })[intensity]\n\nexport type LumenIllustrationSize = 'lg' | 'md' | 'sm'\n\nexport const resolveLumenIllustrationSize = (\n size: LumenIllustrationSize\n): number => ({ lg: 176, md: 128, sm: 96 })[size]\n",
|
|
36821
38759
|
"packages/react-native/src/form-components.tsx": "import {\n type ReactElement,\n type ReactNode,\n type Ref\n} from 'react'\nimport {\n type HostInstance,\n Pressable,\n Switch,\n type SwitchProps,\n Text,\n TextInput,\n type TextInputInstance,\n type TextInputProps,\n type TextStyle,\n View,\n type ViewProps\n} from 'react-native'\n\nimport { resolveLumenSearchFieldState } from './form-recipes.js'\nimport { resolveLumenButtonOpacity } from './recipes.js'\nimport { useLumenTheme } from './theme-context.js'\n\nconst resolveSearchValue = <Value,>(value: Value | undefined, fallback: Value): Value => value ?? fallback\n\nexport interface LumenToggleProps extends Omit<SwitchProps, 'onValueChange' | 'value'> {\n description?: string\n label: string\n onValueChange: (value: boolean) => void\n ref?: Ref<HostInstance>\n showLabel?: boolean\n value: boolean\n}\n\nexport const LumenToggle = ({\n description,\n disabled = false,\n label,\n onValueChange,\n ref,\n showLabel = true,\n style,\n value,\n ...props\n}: LumenToggleProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <View\n style={{\n alignItems: 'center',\n flexDirection: 'row',\n gap: theme.spacing.lg,\n minHeight: 44\n }}\n >\n {showLabel ?\n (\n <View style={{ flex: 1, gap: theme.spacing.xs }}>\n <Text\n style={{\n color: theme.colors.ink,\n fontSize: theme.fontSizes.sm,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {label}\n </Text>\n {description ?\n <Text style={{ color: theme.colors.inkMuted, fontSize: theme.fontSizes.xs }}>{description}</Text> :\n null}\n </View>\n ) :\n null}\n <Switch\n ref={ref}\n {...props}\n accessibilityLabel={label}\n disabled={disabled}\n ios_backgroundColor={theme.colors.surfaceStrong}\n onValueChange={onValueChange}\n style={[{ opacity: disabled ? 0.52 : 1 }, style]}\n thumbColor={value ? theme.colors.onBrand : theme.colors.surface}\n trackColor={{ false: theme.colors.surfaceStrong, true: theme.colors.brandSolid }}\n value={value}\n />\n </View>\n )\n}\n\nexport interface LumenSettingsRowProps extends ViewProps {\n control: ReactNode\n description?: string\n graphic?: ReactNode\n ref?: Ref<HostInstance>\n title: string\n}\n\nexport const LumenSettingsRow = ({\n control,\n description,\n graphic,\n ref,\n style,\n title,\n ...props\n}: LumenSettingsRowProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <View\n ref={ref}\n {...props}\n style={[\n {\n alignItems: 'center',\n flexDirection: 'row',\n gap: theme.spacing.lg,\n minHeight: 56,\n paddingVertical: theme.spacing.sm\n },\n style\n ]}\n >\n {graphic ? <View>{graphic}</View> : null}\n <View style={{ flex: 1, gap: theme.spacing.xs }}>\n <Text\n style={{\n color: theme.colors.ink,\n fontSize: theme.fontSizes.sm,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {title}\n </Text>\n {description ?\n <Text style={{ color: theme.colors.inkMuted, fontSize: theme.fontSizes.xs }}>{description}</Text> :\n null}\n </View>\n <View>{control}</View>\n </View>\n )\n}\n\nexport interface LumenSearchFieldProps extends Omit<TextInputProps, 'onChangeText' | 'value'> {\n clearLabel?: string\n graphic?: ReactNode\n onChangeText: (value: string) => void\n prompt?: string\n ref?: Ref<TextInputInstance>\n value: string\n}\n\nexport const LumenSearchField = ({\n clearLabel = 'Clear search',\n editable = true,\n graphic,\n onChangeText,\n placeholder,\n placeholderTextColor,\n prompt = 'Search',\n ref,\n returnKeyType = 'search',\n style,\n value,\n ...props\n}: LumenSearchFieldProps): ReactElement => {\n const theme = useLumenTheme()\n const searchState = resolveLumenSearchFieldState(value, editable)\n\n return (\n <View\n style={{\n alignItems: 'center',\n backgroundColor: theme.colors.surface,\n borderColor: theme.colors.line,\n borderRadius: theme.radii.sm,\n borderWidth: 1,\n flexDirection: 'row',\n gap: theme.spacing.sm,\n minHeight: 44,\n opacity: searchState.opacity,\n paddingLeft: theme.spacing.md,\n paddingRight: theme.spacing.xs\n }}\n >\n {graphic}\n <TextInput\n ref={ref}\n {...props}\n accessibilityLabel={resolveSearchValue(props.accessibilityLabel, prompt)}\n editable={editable}\n onChangeText={onChangeText}\n placeholder={resolveSearchValue(placeholder, prompt)}\n placeholderTextColor={resolveSearchValue(placeholderTextColor, theme.colors.inkMuted)}\n returnKeyType={returnKeyType}\n style={[\n {\n color: theme.colors.ink,\n flex: 1,\n fontSize: theme.fontSizes.sm,\n minHeight: 42,\n paddingVertical: theme.spacing.sm\n },\n style\n ]}\n value={value}\n />\n {searchState.showClearAction ?\n (\n <Pressable\n accessibilityLabel={clearLabel}\n accessibilityRole=\"button\"\n hitSlop={4}\n onPress={() => {\n onChangeText('')\n }}\n style={({ pressed }) => ({\n opacity: resolveLumenButtonOpacity(false, pressed),\n padding: theme.spacing.sm\n })}\n >\n <Text\n style={{\n color: theme.colors.inkSoft,\n fontSize: theme.fontSizes.xs,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {clearLabel}\n </Text>\n </Pressable>\n ) :\n null}\n </View>\n )\n}\n",
|
|
36822
38760
|
"packages/react-native/src/form-recipes.test.ts": "import { describe, expect, test } from 'vitest'\n\nimport { resolveLumenSearchFieldState } from './form-recipes.js'\n\ndescribe('Lumen React Native form component recipes', () => {\n test('shows the clear action only for editable non-empty queries', () => {\n expect(resolveLumenSearchFieldState('lumen', true)).toEqual({\n opacity: 1,\n showClearAction: true\n })\n expect(resolveLumenSearchFieldState('', true).showClearAction).toBe(false)\n expect(resolveLumenSearchFieldState('lumen', false)).toEqual({\n opacity: 0.52,\n showClearAction: false\n })\n })\n})\n",
|
|
36823
38761
|
"packages/react-native/src/form-recipes.ts": "export interface LumenSearchFieldState {\n opacity: number\n showClearAction: boolean\n}\n\nexport const resolveLumenSearchFieldState = (\n value: string,\n editable: boolean\n): LumenSearchFieldState => ({\n opacity: editable ? 1 : 0.52,\n showClearAction: editable && value.length > 0\n})\n",
|
|
36824
38762
|
"packages/react-native/src/index.test.ts": "import { describe, expect, test } from 'vitest'\n\nimport {\n createLumenTheme,\n lumenDarkTheme,\n lumenLightTheme\n} from './theme.js'\nimport { lumenColorTokens } from './tokens.generated.js'\n\ndescribe('Lumen React Native foundations', () => {\n test('provides matching semantic roles for light and dark themes', () => {\n expect(Object.keys(lumenColorTokens.light)).toEqual(\n Object.keys(lumenColorTokens.dark)\n )\n expect(lumenLightTheme.colors.brand).toBe('#2463EB')\n expect(lumenDarkTheme.colors.brand).toBe('#29B4F5')\n })\n\n test('creates a complete theme for the requested scheme', () => {\n const theme = createLumenTheme('dark')\n\n expect(theme.scheme).toBe('dark')\n expect(theme.spacing.lg).toBe(16)\n expect(theme.radii.md).toBe(10)\n expect(theme.durations.standard).toBe(160)\n })\n})\n",
|
|
36825
|
-
"packages/react-native/src/index.ts": "export {\n LumenButtonGroup,\n type LumenButtonGroupOrientation,\n type LumenButtonGroupProps,\n LumenChip,\n type LumenChipProps,\n LumenFieldGroup,\n type LumenFieldGroupProps,\n LumenTextarea,\n type LumenTextareaProps,\n LumenToast,\n type LumenToastProps\n} from './additional-components.js'\nexport {\n LumenDisclosure,\n type LumenDisclosureProps,\n LumenSkeleton,\n type LumenSkeletonProps,\n type LumenSkeletonShape\n} from './content-components.js'\nexport {\n LumenSearchField,\n type LumenSearchFieldProps,\n LumenSettingsRow,\n type LumenSettingsRowProps,\n LumenToggle,\n type LumenToggleProps\n} from './form-components.js'\nexport {\n LumenBadge,\n type LumenBadgeProps,\n LumenButton,\n type LumenButtonIntent,\n type LumenButtonProps,\n type LumenControlSize,\n LumenDivider,\n type LumenDividerProps,\n LumenIcon,\n LumenIconButton,\n type LumenIconButtonProps,\n type LumenIconGraphic,\n type LumenIconGraphicProps,\n type LumenIconProps,\n type LumenIconSize,\n LumenSpinner,\n type LumenSpinnerProps,\n LumenSurface,\n type LumenSurfacePadding,\n type LumenSurfaceProps,\n type LumenSurfaceRadius,\n type LumenSurfaceTone,\n LumenText,\n LumenTextField,\n type LumenTextFieldProps,\n type LumenTextProps,\n type LumenTextTone,\n type LumenTextVariant\n} from './primitives.js'\nexport {\n LumenProvider,\n type LumenProviderProps\n} from './provider.js'\nexport {\n LumenCheckbox,\n type LumenCheckboxProps,\n LumenRadioGroup,\n type LumenRadioGroupProps,\n LumenSegmentedControl,\n type LumenSegmentedControlProps,\n type LumenSelectionOption } from './selection-components.js'\nexport {\n LumenAlert,\n LumenAlertDescription,\n type LumenAlertDescriptionProps,\n type LumenAlertProps,\n LumenAlertTitle,\n type LumenAlertTitleProps,\n LumenAvatar,\n type LumenAvatarProps,\n LumenCard,\n type LumenCardProps,\n LumenProgress,\n type LumenProgressProps\n} from './shared-components.js'\nexport {\n type LumenAlertVariant,\n type LumenAvatarSize,\n type LumenCardVariant\n} from './shared-recipes.js'\nexport {\n LumenBanner,\n type LumenBannerProps,\n LumenEmptyState,\n type LumenEmptyStateProps,\n LumenListRow,\n type LumenListRowProps,\n LumenSectionHeader,\n type LumenSectionHeaderProps,\n LumenStat,\n type LumenStatProps,\n LumenStatusBar,\n type LumenStatusBarProps\n} from './structured-components.js'\nexport {\n type LumenBannerVariant,\n type LumenMetricTone\n} from './structured-recipes.js'\nexport {\n createLumenTheme,\n lumenDarkTheme,\n lumenLightTheme,\n type LumenTheme\n} from './theme.js'\nexport { useLumenTheme } from './theme-context.js'\nexport {\n type LumenColorScheme,\n lumenColorTokens,\n lumenDurations,\n lumenEasings,\n lumenElevation,\n lumenFontFamilies,\n lumenFontSizes,\n lumenFontWeights,\n lumenRadii,\n type LumenSemanticColor,\n lumenSpacing } from './tokens.generated.js'\n",
|
|
38763
|
+
"packages/react-native/src/index.ts": "export {\n LumenButtonGroup,\n type LumenButtonGroupOrientation,\n type LumenButtonGroupProps,\n LumenChip,\n type LumenChipProps,\n LumenFieldGroup,\n type LumenFieldGroupProps,\n LumenTextarea,\n type LumenTextareaProps,\n LumenToast,\n type LumenToastProps\n} from './additional-components.js'\nexport {\n LumenBackdrop,\n type LumenBackdropProps,\n type LumenBackdropTone,\n type LumenBackdropVariant,\n LumenDisclosure,\n type LumenDisclosureProps,\n LumenGraphic,\n type LumenGraphicProps,\n type LumenGraphicTone,\n type LumenGraphicVariant,\n LumenIllustration,\n type LumenIllustrationProps,\n type LumenIllustrationTone,\n type LumenIllustrationVariant,\n LumenSkeleton,\n type LumenSkeletonProps,\n type LumenSkeletonShape\n} from './content-components.js'\nexport {\n type LumenBackdropIntensity,\n type LumenGraphicSize,\n type LumenIllustrationSize\n} from './content-recipes.js'\nexport {\n LumenSearchField,\n type LumenSearchFieldProps,\n LumenSettingsRow,\n type LumenSettingsRowProps,\n LumenToggle,\n type LumenToggleProps\n} from './form-components.js'\nexport {\n LumenAlertDialog,\n type LumenAlertDialogProps,\n LumenMenu,\n type LumenMenuItem,\n type LumenMenuProps,\n LumenShareButton,\n type LumenShareButtonProps,\n LumenSheet,\n type LumenSheetProps\n} from './overlay-components.js'\nexport {\n type LumenMenuPosition,\n type LumenMenuPositionInput,\n resolveLumenMenuPosition\n} from './overlay-recipes.js'\nexport {\n LumenNavigationBar,\n type LumenNavigationBarProps,\n type LumenNavigationItem,\n LumenRefreshControl,\n type LumenRefreshControlProps\n} from './platform-components.js'\nexport { type LumenRefreshIndicatorTone } from './platform-recipes.js'\nexport {\n LumenBadge,\n type LumenBadgeProps,\n LumenButton,\n type LumenButtonIntent,\n type LumenButtonProps,\n type LumenControlSize,\n LumenDivider,\n type LumenDividerProps,\n LumenIcon,\n LumenIconButton,\n type LumenIconButtonProps,\n type LumenIconGraphic,\n type LumenIconGraphicProps,\n type LumenIconProps,\n type LumenIconSize,\n LumenSpinner,\n type LumenSpinnerProps,\n LumenSurface,\n type LumenSurfacePadding,\n type LumenSurfaceProps,\n type LumenSurfaceRadius,\n type LumenSurfaceTone,\n LumenText,\n LumenTextField,\n type LumenTextFieldProps,\n type LumenTextProps,\n type LumenTextTone,\n type LumenTextVariant\n} from './primitives.js'\nexport {\n LumenProvider,\n type LumenProviderProps\n} from './provider.js'\nexport {\n LumenCheckbox,\n type LumenCheckboxProps,\n LumenRadioGroup,\n type LumenRadioGroupProps,\n LumenSegmentedControl,\n type LumenSegmentedControlProps,\n type LumenSelectionOption } from './selection-components.js'\nexport {\n LumenAlert,\n LumenAlertDescription,\n type LumenAlertDescriptionProps,\n type LumenAlertProps,\n LumenAlertTitle,\n type LumenAlertTitleProps,\n LumenAvatar,\n type LumenAvatarProps,\n LumenCard,\n type LumenCardProps,\n LumenProgress,\n type LumenProgressProps\n} from './shared-components.js'\nexport {\n type LumenAlertVariant,\n type LumenAvatarSize,\n type LumenCardVariant\n} from './shared-recipes.js'\nexport {\n LumenBanner,\n type LumenBannerProps,\n LumenEmptyState,\n type LumenEmptyStateProps,\n LumenListRow,\n type LumenListRowProps,\n LumenSectionHeader,\n type LumenSectionHeaderProps,\n LumenStat,\n type LumenStatProps,\n LumenStatusBar,\n type LumenStatusBarProps\n} from './structured-components.js'\nexport {\n type LumenBannerVariant,\n type LumenMetricTone\n} from './structured-recipes.js'\nexport {\n createLumenTheme,\n lumenDarkTheme,\n lumenLightTheme,\n type LumenTheme\n} from './theme.js'\nexport { useLumenTheme } from './theme-context.js'\nexport {\n type LumenColorScheme,\n lumenColorTokens,\n lumenDurations,\n lumenEasings,\n lumenElevation,\n lumenFontFamilies,\n lumenFontSizes,\n lumenFontWeights,\n lumenRadii,\n type LumenSemanticColor,\n lumenSpacing } from './tokens.generated.js'\n",
|
|
38764
|
+
"packages/react-native/src/overlay-components.tsx": "import {\n type ComponentRef,\n type ReactElement,\n type ReactNode,\n useRef,\n useState\n} from 'react'\nimport {\n Modal,\n Pressable,\n Share,\n type ShareContent,\n type ShareOptions,\n Text,\n type TextStyle,\n useWindowDimensions,\n View,\n type ViewStyle\n} from 'react-native'\n\nimport {\n type LumenMenuPosition,\n resolveLumenMenuPosition\n} from './overlay-recipes.js'\nimport { LumenButton } from './primitives.js'\nimport { useLumenTheme } from './theme-context.js'\n\nexport interface LumenAlertDialogProps {\n cancelLabel?: string\n confirmDisabled?: boolean\n confirmLabel: string\n confirmLoading?: boolean\n destructive?: boolean\n description?: string\n onConfirm: () => void\n onDismiss: () => void\n title: string\n visible: boolean\n}\n\n/** A controlled native confirmation dialog with explicit cancel and confirm actions. */\nexport const LumenAlertDialog = ({\n cancelLabel = 'Cancel',\n confirmDisabled = false,\n confirmLabel,\n confirmLoading = false,\n destructive = false,\n description,\n onConfirm,\n onDismiss,\n title,\n visible\n}: LumenAlertDialogProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <Modal\n animationType=\"fade\"\n onRequestClose={onDismiss}\n transparent\n visible={visible}\n >\n <View\n accessibilityViewIsModal\n style={{\n alignItems: 'center',\n backgroundColor: '#00000080',\n flex: 1,\n justifyContent: 'center',\n padding: theme.spacing.xl\n }}\n >\n <View\n accessibilityRole=\"alert\"\n style={{\n backgroundColor: theme.colors.surface,\n borderColor: theme.colors.line,\n borderRadius: theme.radii.lg,\n borderWidth: 1,\n gap: theme.spacing.lg,\n maxWidth: 440,\n padding: theme.spacing.xl,\n width: '100%'\n }}\n >\n <View style={{ gap: theme.spacing.sm }}>\n <Text\n style={{\n color: theme.colors.ink,\n fontSize: theme.fontSizes.lg,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {title}\n </Text>\n {description ?\n (\n <Text style={{ color: theme.colors.inkSoft, fontSize: theme.fontSizes.sm }}>\n {description}\n </Text>\n ) :\n null}\n </View>\n <View\n style={{\n flexDirection: 'row',\n flexWrap: 'wrap',\n gap: theme.spacing.sm,\n justifyContent: 'flex-end'\n }}\n >\n <LumenButton intent=\"quiet\" onPress={onDismiss}>\n {cancelLabel}\n </LumenButton>\n <LumenButton\n disabled={confirmDisabled}\n intent={destructive ? 'danger' : 'primary'}\n loading={confirmLoading}\n onPress={onConfirm}\n >\n {confirmLabel}\n </LumenButton>\n </View>\n </View>\n </View>\n </Modal>\n )\n}\n\nexport interface LumenSheetProps {\n actions?: ReactNode\n children: ReactNode\n description?: string\n onDismiss: () => void\n title?: string\n visible: boolean\n}\n\n/** A controlled bottom sheet that preserves native modal focus and dismissal behavior. */\nexport const LumenSheet = ({\n actions,\n children,\n description,\n onDismiss,\n title,\n visible\n}: LumenSheetProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <Modal\n animationType=\"slide\"\n onRequestClose={onDismiss}\n transparent\n visible={visible}\n >\n <View accessibilityViewIsModal style={{ flex: 1, justifyContent: 'flex-end' }}>\n <Pressable\n accessibilityElementsHidden\n importantForAccessibility=\"no-hide-descendants\"\n onPress={onDismiss}\n style={{ backgroundColor: '#00000066', flex: 1 }}\n />\n <View\n style={{\n backgroundColor: theme.colors.surface,\n borderColor: theme.colors.line,\n borderTopLeftRadius: theme.radii.lg,\n borderTopRightRadius: theme.radii.lg,\n borderWidth: 1,\n gap: theme.spacing.lg,\n maxHeight: '90%',\n padding: theme.spacing.xl\n }}\n >\n {title || description ?\n (\n <View style={{ gap: theme.spacing.sm }}>\n {title ?\n (\n <Text\n accessibilityRole=\"header\"\n style={{\n color: theme.colors.ink,\n fontSize: theme.fontSizes.lg,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n }}\n >\n {title}\n </Text>\n ) :\n null}\n {description ?\n <Text style={{ color: theme.colors.inkSoft }}>{description}</Text> :\n null}\n </View>\n ) :\n null}\n {children}\n {actions ? <View style={{ alignItems: 'flex-end' }}>{actions}</View> : null}\n </View>\n </View>\n </Modal>\n )\n}\n\nexport interface LumenMenuItem {\n destructive?: boolean\n disabled?: boolean\n label: string\n onPress: () => void\n}\n\nexport interface LumenMenuProps {\n accessibilityLabel: string\n items: readonly LumenMenuItem[]\n trigger: ReactNode\n}\n\nconst menuWidth = 240\n\n/** An anchored action menu with native modal focus containment and labeled item states. */\nexport const LumenMenu = ({\n accessibilityLabel,\n items,\n trigger\n}: LumenMenuProps): ReactElement => {\n const theme = useLumenTheme()\n const triggerRef = useRef<ComponentRef<typeof Pressable>>(null)\n const { height: windowHeight, width: windowWidth } = useWindowDimensions()\n\n const [position, setPosition] = useState<LumenMenuPosition>({\n left: theme.spacing.lg,\n top: theme.spacing.lg\n })\n\n const [visible, setVisible] = useState(false)\n\n const open = (): void => {\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setPosition(resolveLumenMenuPosition({\n anchorHeight: height,\n anchorWidth: width,\n anchorX: x,\n anchorY: y,\n itemCount: items.length,\n margin: theme.spacing.lg,\n menuWidth,\n windowHeight,\n windowWidth\n }))\n\n setVisible(true)\n })\n }\n\n return (\n <>\n <Pressable\n ref={triggerRef}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole=\"button\"\n accessibilityState={{ expanded: visible }}\n onPress={open}\n >\n {trigger}\n </Pressable>\n <Modal\n animationType=\"fade\"\n onRequestClose={() => {\n setVisible(false)\n }}\n transparent\n visible={visible}\n >\n <Pressable\n accessibilityViewIsModal\n onPress={() => {\n setVisible(false)\n }}\n style={{ flex: 1 }}\n >\n <View\n accessibilityLabel={accessibilityLabel}\n accessibilityRole=\"menu\"\n style={{\n backgroundColor: theme.colors.surface,\n borderColor: theme.colors.line,\n borderRadius: theme.radii.md,\n borderWidth: 1,\n left: position.left,\n padding: theme.spacing.sm,\n position: 'absolute',\n top: position.top,\n width: menuWidth\n }}\n >\n {items.map(item => (\n <Pressable\n key={item.label}\n accessibilityRole=\"menuitem\"\n accessibilityState={{ disabled: item.disabled }}\n disabled={item.disabled}\n onPress={() => {\n setVisible(false)\n\n item.onPress()\n }}\n style={({ pressed }): ViewStyle => ({\n backgroundColor: pressed ? theme.colors.surfaceMuted : 'transparent',\n borderRadius: theme.radii.sm,\n minHeight: 44,\n opacity: item.disabled ? 0.52 : 1,\n paddingHorizontal: theme.spacing.md,\n paddingVertical: theme.spacing.md\n })}\n >\n <Text\n style={{\n color: item.destructive ? theme.colors.danger : theme.colors.ink,\n fontSize: theme.fontSizes.sm\n }}\n >\n {item.label}\n </Text>\n </Pressable>\n ))}\n </View>\n </Pressable>\n </Modal>\n </>\n )\n}\n\nexport interface LumenShareButtonProps {\n content: ShareContent\n disabled?: boolean\n label?: string\n onError?: (error: unknown) => void\n onShared?: () => void\n options?: ShareOptions\n}\n\n/** A Lumen button that opens the operating system's share sheet. */\nexport const LumenShareButton = ({\n content,\n disabled = false,\n label = 'Share',\n onError,\n onShared,\n options\n}: LumenShareButtonProps): ReactElement => {\n const share = async (): Promise<void> => {\n try {\n const result = await Share.share(content, options)\n\n if (result.action === Share.sharedAction) onShared?.()\n } catch (error) {\n onError?.(error)\n }\n }\n\n return (\n <LumenButton\n disabled={disabled}\n intent=\"secondary\"\n onPress={() => {\n void share()\n }}\n >\n {label}\n </LumenButton>\n )\n}\n",
|
|
38765
|
+
"packages/react-native/src/overlay-recipes.test.ts": "import { describe, expect, test } from 'vitest'\n\nimport { resolveLumenMenuPosition } from './overlay-recipes.js'\n\ndescribe('React Native overlay recipes', () => {\n test('keeps anchored menus within the horizontal viewport', () => {\n expect(resolveLumenMenuPosition({\n anchorHeight: 44,\n anchorWidth: 44,\n anchorX: 350,\n anchorY: 40,\n itemCount: 2,\n margin: 16,\n menuWidth: 240,\n windowHeight: 800,\n windowWidth: 390\n })).toEqual({ left: 134, top: 88 })\n })\n\n test('places a menu above its trigger when the space below is insufficient', () => {\n expect(resolveLumenMenuPosition({\n anchorHeight: 44,\n anchorWidth: 44,\n anchorX: 24,\n anchorY: 700,\n itemCount: 3,\n margin: 16,\n menuWidth: 240,\n windowHeight: 800,\n windowWidth: 390\n })).toEqual({ left: 16, top: 524 })\n })\n})\n",
|
|
38766
|
+
"packages/react-native/src/overlay-recipes.ts": "export interface LumenMenuPositionInput {\n anchorHeight: number\n anchorWidth: number\n anchorX: number\n anchorY: number\n itemCount: number\n margin: number\n menuWidth: number\n windowHeight: number\n windowWidth: number\n}\n\nexport interface LumenMenuPosition {\n left: number\n top: number\n}\n\nexport const resolveLumenMenuPosition = ({\n anchorHeight,\n anchorWidth,\n anchorX,\n anchorY,\n itemCount,\n margin,\n menuWidth,\n windowHeight,\n windowWidth\n}: LumenMenuPositionInput): LumenMenuPosition => {\n const left = Math.max(\n margin,\n Math.min(anchorX + anchorWidth - menuWidth, windowWidth - menuWidth - margin)\n )\n\n const estimatedHeight = Math.max(1, itemCount) * 48 + margin * 2\n const below = anchorY + anchorHeight + 4\n const top = below + estimatedHeight <= windowHeight ? below : Math.max(margin, anchorY - estimatedHeight)\n\n return { left, top }\n}\n",
|
|
38767
|
+
"packages/react-native/src/platform-components.tsx": "import { type ComponentRef, type ReactElement, type Ref } from 'react'\nimport {\n type HostInstance,\n Pressable,\n RefreshControl,\n type RefreshControlProps,\n Text,\n type TextStyle,\n View,\n type ViewProps\n} from 'react-native'\n\nimport {\n type LumenRefreshIndicatorTone,\n resolveLumenNavigationItemState,\n resolveLumenRefreshColors\n} from './platform-recipes.js'\nimport {\n LumenIcon,\n type LumenIconGraphic\n} from './primitives.js'\nimport { useLumenTheme } from './theme-context.js'\n\nexport interface LumenNavigationItem {\n disabled?: boolean\n icon?: LumenIconGraphic\n label: string\n selectedIcon?: LumenIconGraphic\n value: string\n}\n\nexport interface LumenNavigationBarProps extends Omit<ViewProps, 'children'> {\n accessibilityLabel?: string\n items: readonly LumenNavigationItem[]\n onValueChange: (value: string) => void\n ref?: Ref<HostInstance>\n value: string\n}\n\n/**\n * A controlled, token-aware destination bar for app-level navigation.\n * Screen routing and navigation history remain application-owned.\n */\nexport const LumenNavigationBar = ({\n accessibilityLabel = 'Primary navigation',\n items,\n onValueChange,\n ref,\n style,\n value,\n ...props\n}: LumenNavigationBarProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <View\n ref={ref}\n {...props}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole=\"tablist\"\n style={[\n {\n backgroundColor: theme.colors.surface,\n borderColor: theme.colors.line,\n borderTopWidth: 1,\n flexDirection: 'row',\n minHeight: 64,\n paddingHorizontal: theme.spacing.sm\n },\n style\n ]}\n >\n {items.map(item => {\n const itemState = resolveLumenNavigationItemState(\n theme.colors,\n item.value,\n value,\n Boolean(item.disabled),\n false\n )\n\n const selected = itemState.selected\n const Graphic = selected && item.selectedIcon ? item.selectedIcon : item.icon\n\n return (\n <Pressable\n accessibilityLabel={item.label}\n accessibilityRole=\"tab\"\n accessibilityState={{ disabled: item.disabled, selected }}\n disabled={item.disabled}\n key={item.value}\n onPress={() => {\n onValueChange(item.value)\n }}\n style={({ pressed }) => ({\n alignItems: 'center',\n flex: 1,\n gap: theme.spacing.xs,\n justifyContent: 'center',\n minHeight: 64,\n opacity: resolveLumenNavigationItemState(\n theme.colors,\n item.value,\n value,\n Boolean(item.disabled),\n pressed\n ).opacity,\n paddingHorizontal: theme.spacing.xs,\n paddingVertical: theme.spacing.sm\n })}\n >\n {Graphic ?\n (\n <LumenIcon color={itemState.color} decorative icon={Graphic} size=\"md\" />\n ) :\n null}\n <Text\n numberOfLines={1}\n style={{\n color: itemState.color,\n fontSize: theme.fontSizes.xs,\n fontWeight: String(\n selected ? theme.fontWeights.semibold : theme.fontWeights.medium\n ) as TextStyle['fontWeight']\n }}\n >\n {item.label}\n </Text>\n </Pressable>\n )\n })}\n </View>\n )\n}\n\nexport interface LumenRefreshControlProps extends Omit<\n RefreshControlProps,\n 'accessibilityLabel' | 'colors' | 'onRefresh' | 'progressBackgroundColor' | 'tintColor' | 'titleColor'\n> {\n accessibilityLabel: string\n indicatorTone?: LumenRefreshIndicatorTone\n onRefresh: NonNullable<RefreshControlProps['onRefresh']>\n ref?: Ref<ComponentRef<typeof RefreshControl>>\n}\n\n/**\n * A token-aware native pull-to-refresh indicator for React Native scroll containers.\n */\nexport const LumenRefreshControl = ({\n indicatorTone = 'brand',\n ref,\n ...props\n}: LumenRefreshControlProps): ReactElement => {\n const theme = useLumenTheme()\n const colors = resolveLumenRefreshColors(theme.colors, indicatorTone)\n\n return (\n <RefreshControl\n ref={ref}\n {...props}\n colors={[colors.indicator]}\n progressBackgroundColor={colors.surface}\n tintColor={colors.indicator}\n titleColor={colors.title}\n />\n )\n}\n",
|
|
38768
|
+
"packages/react-native/src/platform-recipes.test.ts": "import { describe, expect, test } from 'vitest'\n\nimport {\n resolveLumenNavigationItemState,\n resolveLumenRefreshColors\n} from './platform-recipes.js'\nimport { lumenDarkTheme, lumenLightTheme } from './theme.js'\n\ndescribe('React Native platform recipes', () => {\n test('maps refresh indicators to semantic theme colors', () => {\n expect(resolveLumenRefreshColors(lumenLightTheme.colors, 'brand')).toEqual({\n indicator: lumenLightTheme.colors.brand,\n surface: lumenLightTheme.colors.surface,\n title: lumenLightTheme.colors.inkMuted\n })\n expect(resolveLumenRefreshColors(lumenDarkTheme.colors, 'accent').indicator).toBe(\n lumenDarkTheme.colors.accent\n )\n expect(resolveLumenRefreshColors(lumenDarkTheme.colors, 'neutral').indicator).toBe(\n lumenDarkTheme.colors.inkSoft\n )\n })\n\n test('resolves selected and disabled destination states', () => {\n expect(resolveLumenNavigationItemState(\n lumenLightTheme.colors,\n 'home',\n 'home',\n false,\n false\n )).toEqual({\n color: lumenLightTheme.colors.brand,\n opacity: 1,\n selected: true\n })\n expect(resolveLumenNavigationItemState(\n lumenDarkTheme.colors,\n 'search',\n 'home',\n true,\n true\n )).toEqual({\n color: lumenDarkTheme.colors.inkMuted,\n opacity: 0.52,\n selected: false\n })\n })\n})\n",
|
|
38769
|
+
"packages/react-native/src/platform-recipes.ts": "import type { LumenTheme } from './theme.js'\n\nexport type LumenRefreshIndicatorTone = 'accent' | 'brand' | 'neutral'\n\nexport interface LumenNavigationItemState {\n color: string\n opacity: number\n selected: boolean\n}\n\nexport const resolveLumenNavigationItemState = (\n colors: LumenTheme['colors'],\n itemValue: string,\n selectedValue: string,\n disabled: boolean,\n pressed: boolean\n): LumenNavigationItemState => {\n const selected = itemValue === selectedValue\n let opacity = 1\n\n if (disabled) opacity = 0.52\n\n else if (pressed) opacity = 0.72\n\n return {\n color: selected ? colors.brand : colors.inkMuted,\n opacity,\n selected\n }\n}\n\nexport interface LumenRefreshColors {\n indicator: string\n surface: string\n title: string\n}\n\nconst resolveIndicatorColor = (\n colors: LumenTheme['colors'],\n tone: LumenRefreshIndicatorTone\n): string => {\n if (tone === 'accent') return colors.accent\n\n if (tone === 'neutral') return colors.inkSoft\n\n return colors.brand\n}\n\nexport const resolveLumenRefreshColors = (\n colors: LumenTheme['colors'],\n tone: LumenRefreshIndicatorTone\n): LumenRefreshColors => ({\n indicator: resolveIndicatorColor(colors, tone),\n surface: colors.surface,\n title: colors.inkMuted\n})\n",
|
|
36826
38770
|
"packages/react-native/src/primitives.tsx": "import {\n type ComponentType,\n type ReactElement,\n type ReactNode,\n type Ref\n} from 'react'\nimport {\n ActivityIndicator,\n type ActivityIndicatorProps,\n type ColorValue,\n type HostInstance,\n Pressable,\n type PressableProps,\n Text,\n TextInput,\n type TextInputInstance,\n type TextInputProps,\n type TextProps,\n type TextStyle,\n View,\n type ViewProps\n} from 'react-native'\n\nimport {\n resolveLumenButtonColors,\n resolveLumenButtonOpacity,\n resolveLumenButtonSize,\n resolveLumenControlMinHeight,\n resolveLumenIconButtonSize,\n resolveLumenIconSize,\n resolveLumenPressableStyle,\n resolveLumenSurfaceColor,\n resolveLumenTextColor,\n resolveLumenTextStyle\n} from './recipes.js'\nimport { useLumenTheme } from './theme-context.js'\n\nexport type LumenTextTone =\n | 'danger' |\n 'default' |\n 'muted' |\n 'soft' |\n 'success' |\n 'warning'\nexport type LumenTextVariant = 'body' | 'caption' | 'label' | 'title'\n\nexport interface LumenTextProps extends TextProps {\n ref?: Ref<HostInstance>\n tone?: LumenTextTone\n variant?: LumenTextVariant\n}\n\nexport const LumenText = ({\n ref,\n style,\n tone = 'default',\n variant = 'body',\n ...props\n}: LumenTextProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <Text\n ref={ref}\n {...props}\n style={[\n { color: resolveLumenTextColor(theme.colors, tone) },\n resolveLumenTextStyle(theme, variant),\n style\n ]}\n />\n )\n}\n\nexport type LumenSurfacePadding = 'lg' | 'md' | 'none' | 'sm'\nexport type LumenSurfaceRadius = 'lg' | 'md' | 'none' | 'sm'\nexport type LumenSurfaceTone = 'canvas' | 'muted' | 'strong' | 'surface'\n\nexport interface LumenSurfaceProps extends ViewProps {\n padding?: LumenSurfacePadding\n radius?: LumenSurfaceRadius\n ref?: Ref<HostInstance>\n tone?: LumenSurfaceTone\n}\n\nexport const LumenSurface = ({\n padding = 'md',\n radius = 'md',\n ref,\n style,\n tone = 'surface',\n ...props\n}: LumenSurfaceProps): ReactElement => {\n const theme = useLumenTheme()\n const backgroundColor = resolveLumenSurfaceColor(theme, tone)\n const paddingValue = padding === 'none' ? 0 : theme.spacing[padding]\n const radiusValue = radius === 'none' ? 0 : theme.radii[radius]\n\n return (\n <View\n ref={ref}\n {...props}\n style={[{ backgroundColor, borderRadius: radiusValue, padding: paddingValue }, style]}\n />\n )\n}\n\nexport type LumenButtonIntent = 'danger' | 'primary' | 'quiet' | 'secondary'\nexport type LumenControlSize = 'lg' | 'md' | 'sm'\nexport type LumenIconSize = 'lg' | 'md' | 'sm'\n\nexport interface LumenIconGraphicProps {\n color?: ColorValue\n size?: number\n strokeWidth?: number\n}\n\nexport type LumenIconGraphic = ComponentType<LumenIconGraphicProps>\n\nexport interface LumenIconProps extends Omit<ViewProps, 'children'> {\n color?: ColorValue\n decorative?: boolean\n icon: LumenIconGraphic\n label?: string\n ref?: Ref<HostInstance>\n size?: LumenIconSize\n strokeWidth?: number\n}\n\nexport const LumenIcon = ({\n color,\n decorative,\n icon: Graphic,\n label,\n ref,\n size = 'md',\n strokeWidth = 2,\n style,\n ...props\n}: LumenIconProps): ReactElement => {\n const theme = useLumenTheme()\n const dimension = resolveLumenIconSize(size)\n const isDecorative = decorative ?? !label\n\n return (\n <View\n ref={ref}\n {...props}\n accessible={!isDecorative}\n accessibilityElementsHidden={isDecorative}\n accessibilityLabel={isDecorative ? undefined : label}\n accessibilityRole={isDecorative ? undefined : 'image'}\n importantForAccessibility={isDecorative ? 'no' : 'yes'}\n style={[\n {\n alignItems: 'center',\n height: dimension,\n justifyContent: 'center',\n width: dimension\n },\n style\n ]}\n >\n <Graphic\n color={color ?? theme.colors.ink}\n size={dimension}\n strokeWidth={strokeWidth}\n />\n </View>\n )\n}\n\nexport interface LumenIconButtonProps extends Omit<PressableProps, 'children'> {\n icon: LumenIconGraphic\n intent?: LumenButtonIntent\n label: string\n ref?: Ref<HostInstance>\n size?: LumenControlSize\n strokeWidth?: number\n}\n\nexport const LumenIconButton = ({\n accessibilityState,\n disabled = false,\n icon,\n intent = 'quiet',\n label,\n ref,\n size = 'md',\n strokeWidth = 2,\n style,\n ...props\n}: LumenIconButtonProps): ReactElement => {\n const theme = useLumenTheme()\n const colors = resolveLumenButtonColors(theme.colors, intent)\n const metrics = resolveLumenIconButtonSize(size)\n\n return (\n <Pressable\n ref={ref}\n {...props}\n accessibilityLabel={label}\n accessibilityRole=\"button\"\n accessibilityState={{ ...accessibilityState, disabled }}\n disabled={disabled}\n style={state => [\n {\n alignItems: 'center',\n backgroundColor: colors.backgroundColor,\n borderColor: colors.borderColor,\n borderRadius: theme.radii.sm,\n borderWidth: 1,\n height: metrics.touchTarget,\n justifyContent: 'center',\n opacity: resolveLumenButtonOpacity(disabled, state.pressed),\n width: metrics.touchTarget\n },\n resolveLumenPressableStyle(style, state)\n ]}\n >\n <LumenIcon\n color={colors.color}\n decorative\n icon={icon}\n size={metrics.iconSize}\n strokeWidth={strokeWidth}\n />\n </Pressable>\n )\n}\n\nexport interface LumenButtonProps extends Omit<PressableProps, 'children'> {\n children: ReactNode\n intent?: LumenButtonIntent\n loading?: boolean\n ref?: Ref<HostInstance>\n size?: LumenControlSize\n}\n\nexport const LumenButton = ({\n accessibilityState,\n children,\n disabled = false,\n intent = 'primary',\n loading = false,\n ref,\n size = 'md',\n style,\n ...props\n}: LumenButtonProps): ReactElement => {\n const theme = useLumenTheme()\n const colors = resolveLumenButtonColors(theme.colors, intent)\n const dimensions = resolveLumenButtonSize(theme, size)\n const isDisabled = disabled || loading\n\n return (\n <Pressable\n ref={ref}\n accessibilityRole=\"button\"\n {...props}\n accessibilityState={{\n ...accessibilityState,\n busy: loading,\n disabled: isDisabled\n }}\n disabled={isDisabled}\n style={state => [\n {\n alignItems: 'center',\n backgroundColor: colors.backgroundColor,\n borderColor: colors.borderColor,\n borderRadius: theme.radii.sm,\n borderWidth: 1,\n flexDirection: 'row',\n gap: theme.spacing.sm,\n justifyContent: 'center',\n opacity: resolveLumenButtonOpacity(isDisabled, state.pressed)\n },\n dimensions.container,\n resolveLumenPressableStyle(style, state)\n ]}\n >\n {loading ? <ActivityIndicator color={colors.color} size=\"small\" /> : null}\n <Text\n style={[\n {\n color: colors.color,\n fontWeight: String(theme.fontWeights.semibold) as TextStyle['fontWeight']\n },\n dimensions.label\n ]}\n >\n {children}\n </Text>\n </Pressable>\n )\n}\n\nexport interface LumenTextFieldProps extends TextInputProps {\n error?: boolean\n ref?: Ref<TextInputInstance>\n size?: LumenControlSize\n}\n\nexport const LumenTextField = ({\n editable = true,\n error = false,\n placeholderTextColor,\n ref,\n size = 'md',\n style,\n ...props\n}: LumenTextFieldProps): ReactElement => {\n const theme = useLumenTheme()\n const minHeight = resolveLumenControlMinHeight(size)\n const fontSize = size === 'lg' ? theme.fontSizes.md : theme.fontSizes.sm\n\n return (\n <TextInput\n ref={ref}\n {...props}\n aria-invalid={error || undefined}\n editable={editable}\n placeholderTextColor={placeholderTextColor ?? theme.colors.inkMuted}\n style={[\n {\n backgroundColor: theme.colors.surface,\n borderColor: error ? theme.colors.danger : theme.colors.line,\n borderRadius: theme.radii.sm,\n borderWidth: 1,\n color: theme.colors.ink,\n fontSize,\n minHeight,\n opacity: editable ? 1 : 0.52,\n paddingHorizontal: theme.spacing.md,\n paddingVertical: theme.spacing.sm\n },\n style\n ]}\n />\n )\n}\n\nexport interface LumenBadgeProps extends ViewProps {\n children: ReactNode\n ref?: Ref<HostInstance>\n tone?: 'accent' | 'danger' | 'neutral' | 'success' | 'warning'\n}\n\nexport const LumenBadge = ({\n children,\n ref,\n style,\n tone = 'neutral',\n ...props\n}: LumenBadgeProps): ReactElement => {\n const theme = useLumenTheme()\n const color = tone === 'neutral' ? theme.colors.inkSoft : theme.colors[tone]\n const backgroundColor = tone === 'neutral' ? theme.colors.surfaceMuted : `${color}1F`\n\n return (\n <View\n ref={ref}\n {...props}\n style={[\n {\n alignSelf: 'flex-start',\n backgroundColor,\n borderRadius: theme.radii.full,\n paddingHorizontal: theme.spacing.sm,\n paddingVertical: theme.spacing.xs\n },\n style\n ]}\n >\n <Text style={{ color, fontSize: theme.fontSizes.xs, fontWeight: '600' }}>\n {children}\n </Text>\n </View>\n )\n}\n\nexport interface LumenDividerProps extends ViewProps {\n ref?: Ref<HostInstance>\n}\n\nexport const LumenDivider = ({\n ref,\n style,\n ...props\n}: LumenDividerProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <View\n ref={ref}\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n {...props}\n style={[{ backgroundColor: theme.colors.line, height: 1 }, style]}\n />\n )\n}\n\nexport interface LumenSpinnerProps extends ActivityIndicatorProps {\n ref?: Ref<HostInstance>\n}\n\nexport const LumenSpinner = ({\n color,\n ref,\n ...props\n}: LumenSpinnerProps): ReactElement => {\n const theme = useLumenTheme()\n\n return (\n <ActivityIndicator\n ref={ref}\n accessibilityRole=\"progressbar\"\n color={color ?? theme.colors.brand}\n {...props}\n />\n )\n}\n",
|
|
36827
38771
|
"packages/react-native/src/provider.tsx": "import {\n type ReactNode,\n useMemo } from 'react'\nimport { useColorScheme } from 'react-native'\n\nimport {\n createLumenTheme,\n type LumenTheme\n} from './theme.js'\nimport { LumenThemeContext } from './theme-context.js'\nimport type { LumenColorScheme } from './tokens.generated.js'\n\nexport interface LumenProviderProps {\n children: ReactNode\n scheme?: LumenColorScheme | 'system'\n theme?: LumenTheme\n}\n\nexport const LumenProvider = ({\n children,\n scheme = 'system',\n theme\n}: LumenProviderProps) => {\n const systemScheme = useColorScheme()\n const resolvedScheme = scheme === 'system' ? systemScheme ?? 'light' : scheme\n\n const value = useMemo(\n () => theme ?? createLumenTheme(resolvedScheme),\n [resolvedScheme, theme]\n )\n\n return (\n <LumenThemeContext value={value}>\n {children}\n </LumenThemeContext>\n )\n}\n",
|
|
36828
38772
|
"packages/react-native/src/recipes.test.ts": "import { describe, expect, test } from 'vitest'\n\nimport {\n resolveLumenButtonColors,\n resolveLumenButtonOpacity,\n resolveLumenButtonSize,\n resolveLumenControlMinHeight,\n resolveLumenIconButtonSize,\n resolveLumenIconSize,\n resolveLumenSurfaceColor,\n resolveLumenTextColor\n} from './recipes.js'\nimport {\n lumenDarkTheme,\n lumenLightTheme\n} from './theme.js'\n\ndescribe('Lumen React Native primitive recipes', () => {\n test('maps shared button intents to semantic theme roles', () => {\n expect(resolveLumenButtonColors(lumenLightTheme.colors, 'primary')).toEqual({\n backgroundColor: lumenLightTheme.colors.brandSolid,\n borderColor: lumenLightTheme.colors.brandSolid,\n color: lumenLightTheme.colors.onBrand\n })\n expect(resolveLumenButtonColors(lumenDarkTheme.colors, 'danger').color).toBe(\n lumenDarkTheme.colors.onDanger\n )\n })\n\n test('keeps touch targets and disabled feedback predictable', () => {\n expect(resolveLumenButtonSize(lumenLightTheme, 'md').container.minHeight).toBe(44)\n expect(resolveLumenControlMinHeight('lg')).toBe(52)\n expect(resolveLumenButtonOpacity(true, false)).toBe(0.52)\n expect(resolveLumenButtonOpacity(false, true)).toBe(0.84)\n expect(resolveLumenIconSize('sm')).toBe(16)\n expect(resolveLumenIconSize('md')).toBe(20)\n expect(resolveLumenIconSize('lg')).toBe(24)\n expect(resolveLumenIconButtonSize('sm')).toEqual({ iconSize: 'sm', touchTarget: 44 })\n expect(resolveLumenIconButtonSize('lg')).toEqual({ iconSize: 'lg', touchTarget: 52 })\n })\n\n test('maps content and surfaces without platform-specific color literals', () => {\n expect(resolveLumenTextColor(lumenDarkTheme.colors, 'muted')).toBe(\n lumenDarkTheme.colors.inkMuted\n )\n expect(resolveLumenSurfaceColor(lumenDarkTheme, 'strong')).toBe(\n lumenDarkTheme.colors.surfaceStrong\n )\n })\n})\n",
|
|
@@ -36842,16 +38786,19 @@
|
|
|
36842
38786
|
},
|
|
36843
38787
|
"swiftui": {
|
|
36844
38788
|
"packages/swift/Sources/LumenUI/AdditionalComponents.swift": "import SwiftUI\n\npublic enum LumenButtonGroupOrientation: Sendable {\n case horizontal\n case vertical\n}\n\npublic struct LumenButtonGroup<Content: View>: View {\n private let content: Content\n private let orientation: LumenButtonGroupOrientation\n\n public init(\n orientation: LumenButtonGroupOrientation = .horizontal,\n @ViewBuilder content: () -> Content\n ) {\n self.orientation = orientation\n self.content = content()\n }\n\n public var body: some View {\n Group {\n if orientation == .horizontal {\n HStack(spacing: LumenSpacing.sm) { content }\n } else {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) { content }\n }\n }\n .accessibilityElement(children: .contain)\n }\n}\n\npublic struct LumenFieldGroup<Content: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let content: Content\n private let description: LocalizedStringKey?\n private let errorMessage: LocalizedStringKey?\n private let required: Bool\n private let title: LocalizedStringKey\n\n public init(\n _ title: LocalizedStringKey,\n description: LocalizedStringKey? = nil,\n errorMessage: LocalizedStringKey? = nil,\n required: Bool = false,\n @ViewBuilder content: () -> Content\n ) {\n self.title = title\n self.description = description\n self.errorMessage = errorMessage\n self.required = required\n self.content = content()\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n HStack(spacing: LumenSpacing.xs) {\n Text(title)\n .font(.callout.weight(.semibold))\n .foregroundStyle(theme.colors.ink)\n if required {\n Text(\"*\").foregroundStyle(theme.colors.danger).accessibilityLabel(\"Required\")\n }\n }\n if let description {\n Text(description)\n .font(.caption)\n .foregroundStyle(theme.colors.inkMuted)\n }\n content\n if let errorMessage {\n Text(errorMessage)\n .font(.caption)\n .foregroundStyle(theme.colors.danger)\n }\n }\n .accessibilityElement(children: .contain)\n }\n}\n\npublic struct LumenTextarea: View {\n @Binding private var text: String\n @Environment(\\.isEnabled) private var isEnabled\n @Environment(\\.lumenTheme) private var theme\n\n private let description: LocalizedStringKey?\n private let errorMessage: LocalizedStringKey?\n private let minHeight: CGFloat\n private let title: LocalizedStringKey\n\n public init(\n _ title: LocalizedStringKey,\n text: Binding<String>,\n description: LocalizedStringKey? = nil,\n errorMessage: LocalizedStringKey? = nil,\n minHeight: CGFloat = 112\n ) {\n self.title = title\n _text = text\n self.description = description\n self.errorMessage = errorMessage\n self.minHeight = minHeight\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n Text(title)\n .font(.callout.weight(.semibold))\n .foregroundStyle(theme.colors.ink)\n TextEditor(text: $text)\n .scrollContentBackground(.hidden)\n .font(.callout)\n .foregroundStyle(theme.colors.ink)\n .frame(minHeight: minHeight)\n .padding(LumenSpacing.sm)\n .background(theme.colors.surface)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous)\n .stroke(errorMessage == nil ? theme.colors.line : theme.colors.danger, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous))\n .opacity(isEnabled ? 1 : 0.52)\n .accessibilityLabel(title)\n .accessibilityValue(errorMessage ?? LocalizedStringKey(text))\n if let errorMessage {\n Text(errorMessage).font(.caption).foregroundStyle(theme.colors.danger)\n } else if let description {\n Text(description).font(.caption).foregroundStyle(theme.colors.inkMuted)\n }\n }\n }\n}\n\npublic struct LumenChip: View {\n @Environment(\\.isEnabled) private var isEnabled\n @Environment(\\.lumenTheme) private var theme\n\n private let label: LocalizedStringKey\n private let onPress: (() -> Void)?\n private let onRemove: (() -> Void)?\n private let removeLabel: LocalizedStringKey\n private let selected: Bool\n\n public init(\n _ label: LocalizedStringKey,\n selected: Bool = false,\n removeLabel: LocalizedStringKey = \"Remove\",\n onPress: (() -> Void)? = nil,\n onRemove: (() -> Void)? = nil\n ) {\n self.label = label\n self.selected = selected\n self.removeLabel = removeLabel\n self.onPress = onPress\n self.onRemove = onRemove\n }\n\n public var body: some View {\n HStack(spacing: LumenSpacing.xs) {\n if let onPress {\n Button(action: onPress) {\n Text(label).font(.caption.weight(.semibold))\n }\n .buttonStyle(.plain)\n .accessibilityAddTraits(selected ? .isSelected : [])\n } else {\n Text(label).font(.caption.weight(.semibold))\n }\n if let onRemove {\n Button(action: onRemove) {\n Image(systemName: \"xmark\").font(.caption2.weight(.bold))\n }\n .buttonStyle(.plain)\n .accessibilityLabel(removeLabel)\n }\n }\n .foregroundStyle(selected ? theme.colors.brand : theme.colors.inkSoft)\n .padding(.horizontal, LumenSpacing.md)\n .frame(minHeight: 32)\n .background(selected ? theme.colors.brandSoft : theme.colors.surfaceMuted)\n .overlay {\n Capsule().stroke(selected ? theme.colors.brand : theme.colors.line, lineWidth: 1)\n }\n .clipShape(Capsule())\n .opacity(isEnabled ? 1 : 0.52)\n .accessibilityElement(children: .contain)\n }\n}\n\npublic struct LumenToast<Actions: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let actions: Actions\n private let description: LocalizedStringKey?\n private let onDismiss: (() -> Void)?\n private let title: LocalizedStringKey\n private let variant: LumenBannerVariant\n\n public init(\n _ title: LocalizedStringKey,\n description: LocalizedStringKey? = nil,\n variant: LumenBannerVariant = .default,\n onDismiss: (() -> Void)? = nil,\n @ViewBuilder actions: () -> Actions\n ) {\n self.title = title\n self.description = description\n self.variant = variant\n self.onDismiss = onDismiss\n self.actions = actions()\n }\n\n public var body: some View {\n HStack(spacing: LumenSpacing.md) {\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n Text(title).font(.callout.weight(.semibold)).foregroundStyle(accentColor)\n if let description {\n Text(description).font(.caption).foregroundStyle(theme.colors.inkSoft)\n }\n }\n .frame(maxWidth: .infinity, alignment: .leading)\n actions\n if let onDismiss {\n LumenIconButton(\n systemName: \"xmark\",\n label: \"Dismiss\",\n intent: .quiet,\n size: .sm,\n action: onDismiss\n )\n }\n }\n .padding(.horizontal, LumenSpacing.lg)\n .padding(.vertical, LumenSpacing.md)\n .background(backgroundColor)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous)\n .stroke(borderColor, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous))\n .accessibilityElement(children: .contain)\n }\n\n private var accentColor: Color {\n switch variant {\n case .accent: theme.colors.accent\n case .default: theme.colors.brand\n case .destructive: theme.colors.danger\n case .success: theme.colors.success\n case .warning: theme.colors.warning\n }\n }\n\n private var backgroundColor: Color {\n variant == .default ? theme.colors.surface : accentColor.opacity(0.08)\n }\n\n private var borderColor: Color {\n variant == .default ? theme.colors.line : accentColor.opacity(0.32)\n }\n}\n\npublic extension LumenToast where Actions == EmptyView {\n init(\n _ title: LocalizedStringKey,\n description: LocalizedStringKey? = nil,\n variant: LumenBannerVariant = .default,\n onDismiss: (() -> Void)? = nil\n ) {\n self.init(title, description: description, variant: variant, onDismiss: onDismiss) {\n EmptyView()\n }\n }\n}\n",
|
|
36845
|
-
"packages/swift/Sources/LumenUI/
|
|
38789
|
+
"packages/swift/Sources/LumenUI/AppleComponents.swift": "#if os(iOS) || os(macOS)\nimport SwiftUI\n\npublic enum LumenDateFieldComponents: Sendable {\n case date\n case dateAndTime\n case time\n\n fileprivate var displayedComponents: DatePickerComponents {\n switch self {\n case .date:\n [.date]\n case .dateAndTime:\n [.date, .hourAndMinute]\n case .time:\n [.hourAndMinute]\n }\n }\n}\n\npublic enum LumenDateFieldBounds: Equatable, Sendable {\n case closed(ClosedRange<Date>)\n case from(Date)\n case through(Date)\n case unbounded\n\n public func clamped(_ value: Date) -> Date {\n switch self {\n case let .closed(range):\n min(max(value, range.lowerBound), range.upperBound)\n case let .from(lowerBound):\n max(value, lowerBound)\n case let .through(upperBound):\n min(value, upperBound)\n case .unbounded:\n value\n }\n }\n}\n\n/// A token-aware Apple date input with native picker behavior and Lumen validation messaging.\npublic struct LumenDateField: View {\n @Binding private var selection: Date\n @Environment(\\.lumenTheme) private var theme\n\n private let bounds: LumenDateFieldBounds\n private let components: LumenDateFieldComponents\n private let description: LocalizedStringKey?\n private let errorMessage: LocalizedStringKey?\n private let title: LocalizedStringKey\n\n public init(\n _ title: LocalizedStringKey,\n selection: Binding<Date>,\n components: LumenDateFieldComponents = .date,\n bounds: LumenDateFieldBounds = .unbounded,\n description: LocalizedStringKey? = nil,\n errorMessage: LocalizedStringKey? = nil\n ) {\n self.title = title\n _selection = selection\n self.components = components\n self.bounds = bounds\n self.description = description\n self.errorMessage = errorMessage\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n datePicker\n .tint(theme.colors.brandSolid)\n\n if let errorMessage {\n Text(errorMessage)\n .font(.caption)\n .foregroundStyle(theme.colors.danger)\n .fixedSize(horizontal: false, vertical: true)\n } else if let description {\n Text(description)\n .font(.caption)\n .foregroundStyle(theme.colors.inkMuted)\n .fixedSize(horizontal: false, vertical: true)\n }\n }\n .accessibilityElement(children: .contain)\n }\n\n @ViewBuilder\n private var datePicker: some View {\n switch bounds {\n case let .closed(range):\n DatePicker(\n title,\n selection: boundedSelection,\n in: range,\n displayedComponents: components.displayedComponents\n )\n case let .from(lowerBound):\n DatePicker(\n title,\n selection: boundedSelection,\n in: lowerBound...,\n displayedComponents: components.displayedComponents\n )\n case let .through(upperBound):\n DatePicker(\n title,\n selection: boundedSelection,\n in: ...upperBound,\n displayedComponents: components.displayedComponents\n )\n case .unbounded:\n DatePicker(\n title,\n selection: boundedSelection,\n displayedComponents: components.displayedComponents\n )\n }\n }\n\n private var boundedSelection: Binding<Date> {\n Binding(\n get: { bounds.clamped(selection) },\n set: { selection = bounds.clamped($0) }\n )\n }\n}\n#endif\n",
|
|
38790
|
+
"packages/swift/Sources/LumenUI/ContentComponents.swift": "#if os(iOS) || os(macOS)\nimport SwiftUI\n\npublic enum LumenSkeletonShape: Sendable {\n case circle\n case rectangle\n case text\n}\n\npublic struct LumenSkeleton: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let height: CGFloat\n private let label: String?\n private let shape: LumenSkeletonShape\n private let width: CGFloat?\n\n public init(\n width: CGFloat? = nil,\n height: CGFloat = 16,\n shape: LumenSkeletonShape = .text,\n label: String? = nil\n ) {\n self.width = width\n self.height = height.isFinite && height > 0 ? height : 16\n self.shape = shape\n self.label = label\n }\n\n public var body: some View {\n skeletonShape\n .foregroundStyle(theme.colors.surfaceStrong.opacity(0.72))\n .frame(maxWidth: fillsAvailableWidth ? .infinity : nil, alignment: .leading)\n .frame(width: resolvedWidth, height: height)\n .accessibilityElement(children: .ignore)\n .accessibilityHidden(label == nil)\n .accessibilityLabel(Text(label ?? \"\"))\n .accessibilityValue(Text(label == nil ? \"\" : \"Loading\"))\n }\n\n private var fillsAvailableWidth: Bool {\n width == nil && shape != .circle\n }\n\n private var resolvedWidth: CGFloat? {\n shape == .circle ? height : width\n }\n\n @ViewBuilder\n private var skeletonShape: some View {\n switch shape {\n case .circle:\n Circle()\n case .rectangle:\n RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous)\n case .text:\n RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous)\n }\n }\n}\n\npublic enum LumenGraphicSize: Sendable {\n case lg\n case md\n case sm\n\n var dimension: CGFloat {\n switch self {\n case .lg: 320\n case .md: 240\n case .sm: 160\n }\n }\n}\n\npublic enum LumenGraphicTone: Sendable {\n case accent\n case brand\n case neutral\n}\n\npublic enum LumenGraphicVariant: Sendable {\n case glow\n case grid\n case orbit\n}\n\npublic struct LumenGraphic<Content: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let content: Content\n private let label: String?\n private let size: LumenGraphicSize\n private let tone: LumenGraphicTone\n private let variant: LumenGraphicVariant\n\n public init(\n label: String? = nil,\n size: LumenGraphicSize = .md,\n tone: LumenGraphicTone = .brand,\n variant: LumenGraphicVariant = .orbit,\n @ViewBuilder content: () -> Content\n ) {\n self.label = label\n self.size = size\n self.tone = tone\n self.variant = variant\n self.content = content()\n }\n\n public var body: some View {\n ZStack {\n graphicLayer\n content\n }\n .frame(width: size.dimension, height: size.dimension)\n .clipped()\n .accessibilityElement(children: label == nil ? .ignore : .combine)\n .accessibilityHidden(label == nil)\n .accessibilityLabel(Text(label ?? \"\"))\n }\n\n private var color: Color {\n switch tone {\n case .accent: theme.colors.accent\n case .brand: theme.colors.brand\n case .neutral: theme.colors.inkMuted\n }\n }\n\n @ViewBuilder\n private var graphicLayer: some View {\n switch variant {\n case .glow:\n Circle()\n .fill(\n RadialGradient(\n colors: [color.opacity(0.3), color.opacity(0)],\n center: .center,\n startRadius: 0,\n endRadius: size.dimension * 0.42\n )\n )\n .padding(size.dimension * 0.08)\n case .grid:\n Canvas { context, canvasSize in\n var path = Path()\n for index in 0...6 {\n let position = CGFloat(index) * canvasSize.width / 6\n path.move(to: CGPoint(x: position, y: 0))\n path.addLine(to: CGPoint(x: position, y: canvasSize.height))\n path.move(to: CGPoint(x: 0, y: position))\n path.addLine(to: CGPoint(x: canvasSize.width, y: position))\n }\n context.stroke(path, with: .color(color.opacity(0.18)), lineWidth: 1)\n }\n .padding(size.dimension * 0.12)\n case .orbit:\n Canvas { context, canvasSize in\n let bounds = CGRect(origin: .zero, size: canvasSize)\n for ratio in [0.9, 0.62, 0.34] {\n let inset = canvasSize.width * (1 - ratio) / 2\n context.stroke(\n Path(ellipseIn: bounds.insetBy(dx: inset, dy: inset)),\n with: .color(color.opacity(0.24)),\n lineWidth: 1\n )\n }\n var axes = Path()\n axes.move(to: CGPoint(x: canvasSize.width * 0.14, y: canvasSize.height / 2))\n axes.addLine(to: CGPoint(x: canvasSize.width * 0.86, y: canvasSize.height / 2))\n axes.move(to: CGPoint(x: canvasSize.width / 2, y: canvasSize.height * 0.14))\n axes.addLine(to: CGPoint(x: canvasSize.width / 2, y: canvasSize.height * 0.86))\n context.stroke(axes, with: .color(color.opacity(0.18)), lineWidth: 1)\n }\n }\n }\n}\n\npublic enum LumenBackdropIntensity: Sendable {\n case medium\n case strong\n case subtle\n\n var opacity: Double {\n switch self {\n case .medium: 0.68\n case .strong: 1\n case .subtle: 0.4\n }\n }\n}\n\npublic enum LumenBackdropTone: Sendable {\n case accent\n case brand\n case neutral\n}\n\npublic enum LumenBackdropVariant: Sendable {\n case aurora\n case dots\n case grid\n case rays\n}\n\npublic struct LumenBackdrop<Content: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let content: Content\n private let intensity: LumenBackdropIntensity\n private let tone: LumenBackdropTone\n private let variant: LumenBackdropVariant\n\n public init(\n intensity: LumenBackdropIntensity = .medium,\n tone: LumenBackdropTone = .brand,\n variant: LumenBackdropVariant = .aurora,\n @ViewBuilder content: () -> Content\n ) {\n self.intensity = intensity\n self.tone = tone\n self.variant = variant\n self.content = content()\n }\n\n public var body: some View {\n ZStack {\n backdropLayer\n .opacity(intensity.opacity)\n .accessibilityHidden(true)\n content\n }\n .clipped()\n }\n\n private var color: Color {\n switch tone {\n case .accent: theme.colors.accent\n case .brand: theme.colors.brand\n case .neutral: theme.colors.inkMuted\n }\n }\n\n private var secondaryColor: Color {\n tone == .neutral ? theme.colors.surfaceStrong : theme.colors.accent\n }\n\n @ViewBuilder\n private var backdropLayer: some View {\n switch variant {\n case .aurora:\n GeometryReader { proxy in\n Circle()\n .fill(color.opacity(0.22))\n .frame(width: proxy.size.width * 0.72)\n .offset(x: -proxy.size.width * 0.2, y: -proxy.size.height * 0.46)\n Circle()\n .fill(secondaryColor.opacity(0.18))\n .frame(width: proxy.size.width * 0.62)\n .offset(x: proxy.size.width * 0.58, y: proxy.size.height * 0.44)\n }\n case .dots:\n Canvas { context, canvasSize in\n for row in 0..<6 {\n for column in 0..<8 {\n let point = CGPoint(\n x: (CGFloat(column) + 0.5) * canvasSize.width / 8,\n y: (CGFloat(row) + 0.5) * canvasSize.height / 6\n )\n context.fill(\n Path(ellipseIn: CGRect(x: point.x - 1.5, y: point.y - 1.5, width: 3, height: 3)),\n with: .color(color.opacity(0.28))\n )\n }\n }\n }\n case .grid:\n Canvas { context, canvasSize in\n var path = Path()\n for index in 0...6 {\n let x = CGFloat(index) * canvasSize.width / 6\n let y = CGFloat(index) * canvasSize.height / 6\n path.move(to: CGPoint(x: x, y: 0))\n path.addLine(to: CGPoint(x: x, y: canvasSize.height))\n path.move(to: CGPoint(x: 0, y: y))\n path.addLine(to: CGPoint(x: canvasSize.width, y: y))\n }\n context.stroke(path, with: .color(color.opacity(0.18)), lineWidth: 1)\n }\n case .rays:\n Canvas { context, canvasSize in\n var path = Path()\n let center = CGPoint(x: canvasSize.width / 2, y: canvasSize.height / 2)\n let radius = sqrt(\n canvasSize.width * canvasSize.width + canvasSize.height * canvasSize.height\n )\n for index in 0..<12 {\n let angle = CGFloat(index) * .pi / 6\n path.move(to: center)\n path.addLine(to: CGPoint(\n x: center.x + cos(angle) * radius,\n y: center.y + sin(angle) * radius\n ))\n }\n context.stroke(path, with: .color(color.opacity(0.2)), lineWidth: 1)\n }\n }\n }\n}\n\npublic enum LumenIllustrationSize: Sendable {\n case lg\n case md\n case sm\n\n var dimension: CGFloat {\n switch self {\n case .lg: 176\n case .md: 128\n case .sm: 96\n }\n }\n}\n\npublic enum LumenIllustrationTone: Sendable {\n case accent\n case auto\n case brand\n case neutral\n}\n\npublic enum LumenIllustrationVariant: Sendable {\n case empty\n case error\n case offline\n case success\n}\n\npublic struct LumenIllustration: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let label: String?\n private let size: LumenIllustrationSize\n private let tone: LumenIllustrationTone\n private let variant: LumenIllustrationVariant\n\n public init(\n variant: LumenIllustrationVariant = .empty,\n tone: LumenIllustrationTone = .auto,\n size: LumenIllustrationSize = .md,\n label: String? = nil\n ) {\n self.variant = variant\n self.tone = tone\n self.size = size\n self.label = label\n }\n\n public var body: some View {\n Canvas { context, canvasSize in\n let scale = min(canvasSize.width, canvasSize.height) / 120\n let lineWidth = max(2, 2.5 * scale)\n let bounds = CGRect(origin: .zero, size: canvasSize)\n context.fill(\n Path(ellipseIn: bounds.insetBy(dx: 11 * scale, dy: 11 * scale)),\n with: .color(color.opacity(0.1))\n )\n drawArtwork(context: &context, scale: scale, lineWidth: lineWidth)\n }\n .frame(width: size.dimension, height: size.dimension)\n .accessibilityElement(children: .ignore)\n .accessibilityHidden(label == nil)\n .accessibilityLabel(Text(label ?? \"\"))\n }\n\n private var color: Color {\n switch tone {\n case .accent: theme.colors.accent\n case .brand: theme.colors.brand\n case .neutral: theme.colors.inkMuted\n case .auto:\n switch variant {\n case .empty: theme.colors.brand\n case .error: theme.colors.danger\n case .offline: theme.colors.inkMuted\n case .success: theme.colors.success\n }\n }\n }\n\n private func drawArtwork(context: inout GraphicsContext, scale: CGFloat, lineWidth: CGFloat) {\n switch variant {\n case .empty:\n var tray = Path(roundedRect: CGRect(x: 29, y: 43, width: 62, height: 40).scaled(by: scale), cornerRadius: 8 * scale)\n tray.move(to: CGPoint(x: 40 * scale, y: 60 * scale))\n tray.addLine(to: CGPoint(x: 80 * scale, y: 60 * scale))\n context.stroke(tray, with: .color(color), lineWidth: lineWidth)\n case .error, .success:\n context.stroke(\n Path(ellipseIn: CGRect(x: 30, y: 30, width: 60, height: 60).scaled(by: scale)),\n with: .color(color),\n lineWidth: lineWidth\n )\n var symbol = Path()\n if variant == .success {\n symbol.move(to: CGPoint(x: 44 * scale, y: 61 * scale))\n symbol.addLine(to: CGPoint(x: 55 * scale, y: 72 * scale))\n symbol.addLine(to: CGPoint(x: 77 * scale, y: 48 * scale))\n } else {\n symbol.move(to: CGPoint(x: 47 * scale, y: 47 * scale))\n symbol.addLine(to: CGPoint(x: 73 * scale, y: 73 * scale))\n symbol.move(to: CGPoint(x: 73 * scale, y: 47 * scale))\n symbol.addLine(to: CGPoint(x: 47 * scale, y: 73 * scale))\n }\n context.stroke(symbol, with: .color(color), style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))\n case .offline:\n var cloud = Path(roundedRect: CGRect(x: 24, y: 45, width: 72, height: 35).scaled(by: scale), cornerRadius: 18 * scale)\n cloud.move(to: CGPoint(x: 30 * scale, y: 30 * scale))\n cloud.addLine(to: CGPoint(x: 90 * scale, y: 90 * scale))\n context.stroke(cloud, with: .color(color), style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))\n }\n }\n}\n\nprivate extension CGRect {\n func scaled(by scale: CGFloat) -> CGRect {\n CGRect(x: origin.x * scale, y: origin.y * scale, width: width * scale, height: height * scale)\n }\n}\n\npublic struct LumenDisclosure<Content: View>: View {\n @Binding private var isExpanded: Bool\n @Environment(\\.lumenTheme) private var theme\n\n private let content: Content\n private let description: String?\n private let title: String\n\n public init(\n _ title: String,\n isExpanded: Binding<Bool>,\n description: String? = nil,\n @ViewBuilder content: () -> Content\n ) {\n self.title = title\n _isExpanded = isExpanded\n self.description = description\n self.content = content()\n }\n\n public var body: some View {\n DisclosureGroup(isExpanded: $isExpanded) {\n content\n .frame(maxWidth: .infinity, alignment: .leading)\n .padding(.top, LumenSpacing.md)\n } label: {\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n Text(title)\n .font(.callout.weight(.semibold))\n .foregroundStyle(theme.colors.ink)\n\n if let description {\n Text(description)\n .font(.caption)\n .foregroundStyle(theme.colors.inkMuted)\n .fixedSize(horizontal: false, vertical: true)\n }\n }\n }\n .tint(theme.colors.brandSolid)\n .padding(.horizontal, LumenSpacing.lg)\n .padding(.vertical, LumenSpacing.md)\n .background(theme.colors.surface)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous)\n .stroke(theme.colors.line, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous))\n }\n}\n#endif\n",
|
|
36846
38791
|
"packages/swift/Sources/LumenUI/FormControls.swift": "#if os(iOS) || os(macOS)\nimport SwiftUI\n\npublic struct LumenToggle<Label: View>: View {\n @Binding private var isOn: Bool\n @Environment(\\.lumenTheme) private var theme\n\n private let label: Label\n\n public init(\n isOn: Binding<Bool>,\n @ViewBuilder label: () -> Label\n ) {\n _isOn = isOn\n self.label = label()\n }\n\n public var body: some View {\n Toggle(isOn: $isOn) {\n label\n }\n .toggleStyle(.switch)\n .tint(theme.colors.brandSolid)\n }\n}\n\npublic extension LumenToggle where Label == Text {\n init(_ title: LocalizedStringKey, isOn: Binding<Bool>) {\n self.init(isOn: isOn) {\n Text(title)\n }\n }\n}\n\npublic struct LumenSettingsRow<Control: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let control: Control\n private let description: LocalizedStringKey?\n private let systemName: String?\n private let title: LocalizedStringKey\n\n public init(\n _ title: LocalizedStringKey,\n description: LocalizedStringKey? = nil,\n systemName: String? = nil,\n @ViewBuilder control: () -> Control\n ) {\n self.title = title\n self.description = description\n self.systemName = systemName\n self.control = control()\n }\n\n public var body: some View {\n HStack(alignment: .center, spacing: LumenSpacing.lg) {\n if let systemName {\n LumenIcon(systemName: systemName, size: .sm, color: theme.colors.inkSoft)\n }\n\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n Text(title)\n .font(.callout.weight(.semibold))\n .foregroundStyle(theme.colors.ink)\n\n if let description {\n Text(description)\n .font(.caption)\n .foregroundStyle(theme.colors.inkMuted)\n .fixedSize(horizontal: false, vertical: true)\n }\n }\n .frame(maxWidth: .infinity, alignment: .leading)\n\n control\n .fixedSize(horizontal: true, vertical: false)\n }\n .accessibilityElement(children: .contain)\n }\n}\n\npublic enum LumenPickerStyle: Sendable {\n case automatic\n case menu\n case segmented\n}\n\npublic struct LumenPicker<SelectionValue: Hashable, Content: View>: View {\n @Binding private var selection: SelectionValue\n @Environment(\\.lumenTheme) private var theme\n\n private let content: Content\n private let showsLabel: Bool\n private let style: LumenPickerStyle\n private let title: LocalizedStringKey\n\n public init(\n _ title: LocalizedStringKey,\n selection: Binding<SelectionValue>,\n style: LumenPickerStyle = .automatic,\n showsLabel: Bool = true,\n @ViewBuilder content: () -> Content\n ) {\n self.title = title\n _selection = selection\n self.style = style\n self.showsLabel = showsLabel\n self.content = content()\n }\n\n public var body: some View {\n styledPicker\n .tint(theme.colors.brandSolid)\n }\n\n @ViewBuilder\n private var styledPicker: some View {\n switch style {\n case .automatic:\n picker\n case .menu:\n #if os(watchOS)\n picker\n #else\n picker.pickerStyle(.menu)\n #endif\n case .segmented:\n picker.pickerStyle(.segmented)\n }\n }\n\n @ViewBuilder\n private var picker: some View {\n let picker = Picker(title, selection: $selection) {\n content\n }\n\n if showsLabel {\n picker\n } else {\n picker.labelsHidden()\n }\n }\n}\n\npublic struct LumenSliderConfiguration: Equatable, Sendable {\n public let bounds: ClosedRange<Double>\n public let step: Double?\n\n public static func resolve(\n bounds: ClosedRange<Double>,\n step: Double?\n ) -> LumenSliderConfiguration {\n let safeStep = step.flatMap { value in\n value.isFinite && value > 0 ? value : nil\n }\n\n return LumenSliderConfiguration(bounds: bounds, step: safeStep)\n }\n}\n\npublic struct LumenSlider: View {\n @Binding private var value: Double\n @Environment(\\.lumenTheme) private var theme\n\n private let configuration: LumenSliderConfiguration\n private let label: LocalizedStringKey\n private let valueLabel: String?\n\n public init(\n _ label: LocalizedStringKey,\n value: Binding<Double>,\n in bounds: ClosedRange<Double>,\n step: Double? = nil,\n valueLabel: String? = nil\n ) {\n self.label = label\n _value = value\n configuration = LumenSliderConfiguration.resolve(bounds: bounds, step: step)\n self.valueLabel = valueLabel\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n HStack(alignment: .firstTextBaseline, spacing: LumenSpacing.md) {\n Text(label)\n .font(.callout.weight(.semibold))\n .foregroundStyle(theme.colors.ink)\n\n Spacer(minLength: LumenSpacing.md)\n\n if let valueLabel {\n Text(valueLabel)\n .font(.caption.monospacedDigit())\n .foregroundStyle(theme.colors.inkMuted)\n }\n }\n\n slider\n .tint(theme.colors.brandSolid)\n }\n }\n\n @ViewBuilder\n private var slider: some View {\n if let step = configuration.step {\n Slider(\n value: $value,\n in: configuration.bounds,\n step: step,\n label: { Text(label) }\n )\n } else {\n Slider(\n value: $value,\n in: configuration.bounds,\n label: { Text(label) }\n )\n }\n }\n}\n\npublic struct LumenSearchField: View {\n @Binding private var text: String\n @Environment(\\.isEnabled) private var isEnabled\n @Environment(\\.lumenControlDensity) private var density\n @Environment(\\.lumenTheme) private var theme\n\n private let prompt: String\n\n public init(_ prompt: String = \"Search\", text: Binding<String>) {\n self.prompt = prompt\n _text = text\n }\n\n public var body: some View {\n HStack(spacing: LumenSpacing.sm) {\n LumenIcon(systemName: \"magnifyingglass\", size: .sm, color: theme.colors.inkMuted)\n\n TextField(prompt, text: $text)\n .textFieldStyle(.plain)\n .foregroundStyle(theme.colors.ink)\n\n if !text.isEmpty {\n Button {\n text = \"\"\n } label: {\n Image(systemName: \"xmark.circle.fill\")\n .foregroundStyle(theme.colors.inkMuted)\n }\n .buttonStyle(.plain)\n .accessibilityLabel(\"Clear search\")\n }\n }\n .frame(minHeight: LumenButtonMetrics.resolve(.md, density: density).minHeight)\n .padding(.horizontal, LumenSpacing.md)\n .background(theme.colors.surface)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous)\n .stroke(theme.colors.line, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous))\n .opacity(isEnabled ? 1 : 0.52)\n }\n}\n#endif\n",
|
|
36847
|
-
"packages/swift/Sources/LumenUI/LumenTheme.swift": "import SwiftUI\n\npublic enum LumenColorScheme: Sendable {\n case dark\n case light\n}\n\npublic struct LumenTheme: Sendable {\n public let colors: LumenColorPalette\n public let scheme: LumenColorScheme\n\n public init(colors: LumenColorPalette, scheme: LumenColorScheme) {\n self.colors = colors\n self.scheme = scheme\n }\n\n public static let light = LumenTheme(colors: LumenColors.light, scheme: .light)\n public static let dark = LumenTheme(colors: LumenColors.dark, scheme: .dark)\n}\n\nprivate struct LumenThemeKey: EnvironmentKey {\n static let defaultValue = LumenTheme.light\n}\n\npublic extension EnvironmentValues {\n var lumenTheme: LumenTheme {\n get { self[LumenThemeKey.self] }\n set { self[LumenThemeKey.self] = newValue }\n }\n}\n\npublic extension View {\n func lumenTheme(_ theme: LumenTheme) -> some View {\n environment(\\.lumenTheme, theme)\n .preferredColorScheme(theme.
|
|
38792
|
+
"packages/swift/Sources/LumenUI/LumenTheme.swift": "import SwiftUI\n\npublic enum LumenColorScheme: Sendable {\n case dark\n case light\n}\n\npublic struct LumenTheme: Sendable {\n public let colors: LumenColorPalette\n public let scheme: LumenColorScheme\n\n public init(colors: LumenColorPalette, scheme: LumenColorScheme) {\n self.colors = colors\n self.scheme = scheme\n }\n\n public static let light = LumenTheme(colors: LumenColors.light, scheme: .light)\n public static let dark = LumenTheme(colors: LumenColors.dark, scheme: .dark)\n}\n\nextension LumenTheme {\n func resolvedPreferredColorScheme(enforceColorScheme: Bool) -> ColorScheme? {\n guard enforceColorScheme else { return nil }\n\n return scheme == .dark ? .dark : .light\n }\n}\n\nprivate struct LumenThemeKey: EnvironmentKey {\n static let defaultValue = LumenTheme.light\n}\n\npublic extension EnvironmentValues {\n var lumenTheme: LumenTheme {\n get { self[LumenThemeKey.self] }\n set { self[LumenThemeKey.self] = newValue }\n }\n}\n\npublic extension View {\n func lumenTheme(_ theme: LumenTheme, enforceColorScheme: Bool = true) -> some View {\n environment(\\.lumenTheme, theme)\n .preferredColorScheme(theme.resolvedPreferredColorScheme(enforceColorScheme: enforceColorScheme))\n }\n}\n",
|
|
36848
38793
|
"packages/swift/Sources/LumenUI/MacComponents.swift": "#if os(macOS)\nimport AppKit\nimport SwiftUI\n\npublic struct LumenShortcut: Hashable {\n public let display: String\n public let keyCode: UInt16\n public let modifiers: NSEvent.ModifierFlags\n\n public init(\n keyCode: UInt16,\n modifiers: NSEvent.ModifierFlags,\n display: String? = nil\n ) {\n self.keyCode = keyCode\n self.modifiers = modifiers.intersection(.deviceIndependentFlagsMask)\n self.display = display ?? Self.format(\n keyCode: keyCode,\n modifiers: modifiers,\n characters: nil\n )\n }\n\n public func hash(into hasher: inout Hasher) {\n hasher.combine(keyCode)\n hasher.combine(modifiers.rawValue)\n }\n\n public static func == (lhs: LumenShortcut, rhs: LumenShortcut) -> Bool {\n lhs.keyCode == rhs.keyCode && lhs.modifiers.rawValue == rhs.modifiers.rawValue\n }\n\n static func from(_ event: NSEvent) -> LumenShortcut {\n LumenShortcut(\n keyCode: event.keyCode,\n modifiers: event.modifierFlags,\n display: format(\n keyCode: event.keyCode,\n modifiers: event.modifierFlags,\n characters: event.charactersIgnoringModifiers\n )\n )\n }\n\n static func format(\n keyCode: UInt16,\n modifiers: NSEvent.ModifierFlags,\n characters: String?\n ) -> String {\n let flags = modifiers.intersection(.deviceIndependentFlagsMask)\n var parts: [String] = []\n\n if flags.contains(.control) { parts.append(\"⌃\") }\n if flags.contains(.option) { parts.append(\"⌥\") }\n if flags.contains(.shift) { parts.append(\"⇧\") }\n if flags.contains(.command) { parts.append(\"⌘\") }\n\n let specialKeys: [UInt16: String] = [\n 36: \"↩\",\n 48: \"⇥\",\n 49: \"Space\",\n 51: \"⌫\",\n 115: \"↖\",\n 116: \"⇞\",\n 119: \"↘\",\n 121: \"⇟\",\n 123: \"←\",\n 124: \"→\",\n 125: \"↓\",\n 126: \"↑\",\n ]\n\n if let specialKey = specialKeys[keyCode] {\n parts.append(specialKey)\n } else if let characters, let character = characters.first {\n parts.append(String(character).uppercased())\n } else {\n parts.append(\"Key \\(keyCode)\")\n }\n\n return parts.joined()\n }\n}\n\n@MainActor\nprivate final class LumenShortcutRecorderState: ObservableObject {\n @Published var errorMessage: String?\n @Published var isRecording = false\n var monitor: Any?\n}\n\npublic struct LumenShortcutRecorder: View {\n @Binding private var shortcut: LumenShortcut?\n @Environment(\\.lumenTheme) private var theme\n @StateObject private var state = LumenShortcutRecorderState()\n\n private let label: LocalizedStringKey\n private let validation: ((LumenShortcut) -> String?)?\n\n public init(\n _ label: LocalizedStringKey,\n shortcut: Binding<LumenShortcut?>,\n validation: ((LumenShortcut) -> String?)? = nil\n ) {\n self.label = label\n _shortcut = shortcut\n self.validation = validation\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n HStack(spacing: LumenSpacing.md) {\n Text(label)\n .font(.callout.weight(.semibold))\n .foregroundStyle(theme.colors.ink)\n\n Spacer(minLength: LumenSpacing.md)\n\n if state.isRecording {\n Text(\"Press keys…\")\n .font(.caption.monospaced())\n .foregroundStyle(theme.colors.inkMuted)\n\n LumenButton(\"Cancel\", intent: .quiet, size: .sm) {\n stopRecording()\n }\n } else {\n if let shortcut {\n Text(shortcut.display)\n .font(.caption.weight(.semibold).monospaced())\n .foregroundStyle(theme.colors.ink)\n .padding(.horizontal, LumenSpacing.sm)\n .padding(.vertical, LumenSpacing.xs)\n .background(theme.colors.surfaceStrong)\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm))\n .accessibilityLabel(\"Current shortcut \\(shortcut.display)\")\n }\n\n LumenButton(shortcut == nil ? \"Record…\" : \"Change…\", intent: .secondary, size: .sm) {\n startRecording()\n }\n\n if shortcut != nil {\n LumenButton(\"Clear\", intent: .quiet, size: .sm) {\n shortcut = nil\n state.errorMessage = nil\n }\n }\n }\n }\n\n if let errorMessage = state.errorMessage {\n HStack(spacing: LumenSpacing.sm) {\n LumenIcon(\n systemName: \"exclamationmark.triangle\",\n size: .sm,\n color: theme.colors.warning\n )\n Text(errorMessage)\n .font(.caption)\n .foregroundStyle(theme.colors.warning)\n }\n .accessibilityElement(children: .combine)\n }\n }\n .onDisappear {\n stopRecording()\n }\n }\n\n private func startRecording() {\n guard state.monitor == nil else { return }\n\n state.errorMessage = nil\n state.isRecording = true\n state.monitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in\n handle(event)\n return nil\n }\n }\n\n private func stopRecording() {\n if let monitor = state.monitor {\n NSEvent.removeMonitor(monitor)\n }\n state.monitor = nil\n state.isRecording = false\n }\n\n private func handle(_ event: NSEvent) {\n defer { stopRecording() }\n\n if event.keyCode == 53 {\n return\n }\n\n let shortcut = LumenShortcut.from(event)\n let commandModifiers: NSEvent.ModifierFlags = [.command, .control, .option, .shift]\n\n guard !shortcut.modifiers.intersection(commandModifiers).isEmpty else {\n state.errorMessage = \"Include at least one modifier key.\"\n return\n }\n\n if let validationMessage = validation?(shortcut) {\n state.errorMessage = validationMessage\n return\n }\n\n self.shortcut = shortcut\n state.errorMessage = nil\n }\n}\n\npublic struct LumenSymbolOption: Hashable, Identifiable, Sendable {\n public let category: String\n public let label: String\n public let name: String\n\n public var id: String { name }\n\n public init(name: String, label: String, category: String) {\n self.name = name\n self.label = label\n self.category = category\n }\n\n public static let common: [LumenSymbolOption] = [\n LumenSymbolOption(name: \"folder\", label: \"Folder\", category: \"Files\"),\n LumenSymbolOption(name: \"doc\", label: \"Document\", category: \"Files\"),\n LumenSymbolOption(name: \"tray\", label: \"Tray\", category: \"Files\"),\n LumenSymbolOption(name: \"archivebox\", label: \"Archive\", category: \"Files\"),\n LumenSymbolOption(name: \"desktopcomputer\", label: \"Desktop\", category: \"Devices\"),\n LumenSymbolOption(name: \"laptopcomputer\", label: \"Laptop\", category: \"Devices\"),\n LumenSymbolOption(name: \"display\", label: \"Display\", category: \"Devices\"),\n LumenSymbolOption(name: \"iphone\", label: \"Phone\", category: \"Devices\"),\n LumenSymbolOption(name: \"hammer\", label: \"Build\", category: \"Work\"),\n LumenSymbolOption(name: \"wrench.and.screwdriver\", label: \"Tools\", category: \"Work\"),\n LumenSymbolOption(name: \"briefcase\", label: \"Business\", category: \"Work\"),\n LumenSymbolOption(name: \"chart.bar\", label: \"Analytics\", category: \"Work\"),\n LumenSymbolOption(name: \"person.2\", label: \"People\", category: \"People\"),\n LumenSymbolOption(name: \"bubble.left.and.bubble.right\", label: \"Messages\", category: \"People\"),\n LumenSymbolOption(name: \"video\", label: \"Video\", category: \"People\"),\n LumenSymbolOption(name: \"calendar\", label: \"Calendar\", category: \"Planning\"),\n LumenSymbolOption(name: \"checklist\", label: \"Tasks\", category: \"Planning\"),\n LumenSymbolOption(name: \"clock\", label: \"Time\", category: \"Planning\"),\n LumenSymbolOption(name: \"flag\", label: \"Milestone\", category: \"Planning\"),\n LumenSymbolOption(name: \"star\", label: \"Favorite\", category: \"General\"),\n LumenSymbolOption(name: \"heart\", label: \"Health\", category: \"General\"),\n LumenSymbolOption(name: \"house\", label: \"Home\", category: \"General\"),\n LumenSymbolOption(name: \"gearshape\", label: \"Settings\", category: \"General\"),\n ]\n}\n\npublic struct LumenSymbolPicker: View {\n @Binding private var selectedName: String\n @Environment(\\.lumenTheme) private var theme\n @State private var query = \"\"\n\n private let options: [LumenSymbolOption]\n private let title: LocalizedStringKey\n\n public init(\n _ title: LocalizedStringKey = \"Choose a symbol\",\n selectedName: Binding<String>,\n options: [LumenSymbolOption] = LumenSymbolOption.common\n ) {\n self.title = title\n _selectedName = selectedName\n self.options = options\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.md) {\n Text(title)\n .font(.headline)\n .foregroundStyle(theme.colors.ink)\n\n LumenSearchField(\"Search symbols\", text: $query)\n\n LumenDivider()\n\n if matchingOptions.isEmpty {\n LumenEmptyState(\n \"No symbols found\",\n systemName: \"magnifyingglass\",\n description: \"Try a different search term.\"\n )\n } else {\n ScrollView {\n LazyVStack(alignment: .leading, spacing: LumenSpacing.lg) {\n ForEach(categories, id: \\.self) { category in\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n Text(category)\n .font(.caption.weight(.semibold))\n .foregroundStyle(theme.colors.inkMuted)\n\n LazyVGrid(columns: columns, alignment: .leading, spacing: LumenSpacing.sm) {\n ForEach(matchingOptions.filter { $0.category == category }) { option in\n symbolButton(option)\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n private var categories: [String] {\n options.map(\\.category).reduce(into: []) { result, category in\n if !result.contains(category) && matchingOptions.contains(where: { $0.category == category }) {\n result.append(category)\n }\n }\n }\n\n private var columns: [GridItem] {\n [GridItem(.adaptive(minimum: 64, maximum: 88), spacing: LumenSpacing.sm)]\n }\n\n private var matchingOptions: [LumenSymbolOption] {\n guard !query.isEmpty else { return options }\n\n return options.filter {\n $0.label.localizedCaseInsensitiveContains(query)\n || $0.name.localizedCaseInsensitiveContains(query)\n || $0.category.localizedCaseInsensitiveContains(query)\n }\n }\n\n private func symbolButton(_ option: LumenSymbolOption) -> some View {\n let isSelected = selectedName == option.name\n\n return Button {\n selectedName = option.name\n } label: {\n VStack(spacing: LumenSpacing.xs) {\n LumenIcon(\n systemName: option.name,\n size: .md,\n color: isSelected ? theme.colors.brand : theme.colors.inkSoft\n )\n Text(option.label)\n .font(.caption2)\n .lineLimit(1)\n }\n .frame(maxWidth: .infinity, minHeight: 56)\n .padding(.horizontal, LumenSpacing.xs)\n .background(isSelected ? theme.colors.brandSoft : theme.colors.surfaceMuted)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous)\n .stroke(isSelected ? theme.colors.brand : theme.colors.line, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous))\n }\n .buttonStyle(.plain)\n .help(option.label)\n .accessibilityLabel(option.label)\n .accessibilityAddTraits(isSelected ? .isSelected : [])\n }\n}\n\npublic struct LumenSymbolPickerButton: View {\n @Binding private var selectedName: String\n @Environment(\\.lumenTheme) private var theme\n @State private var isPresented = false\n\n private let label: LocalizedStringKey\n private let options: [LumenSymbolOption]\n\n public init(\n _ label: LocalizedStringKey = \"Choose a symbol\",\n selectedName: Binding<String>,\n options: [LumenSymbolOption] = LumenSymbolOption.common\n ) {\n self.label = label\n _selectedName = selectedName\n self.options = options\n }\n\n public var body: some View {\n Button {\n isPresented = true\n } label: {\n ZStack(alignment: .bottomTrailing) {\n LumenIcon(systemName: selectedName, size: .lg, color: theme.colors.brand)\n .frame(width: 42, height: 42)\n .background(theme.colors.brandSoft)\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous))\n\n Image(systemName: \"pencil.circle.fill\")\n .font(.system(size: 14, weight: .semibold))\n .foregroundStyle(theme.colors.onBrand, theme.colors.brandSolid)\n .offset(x: 4, y: 4)\n }\n }\n .buttonStyle(.plain)\n .accessibilityLabel(label)\n .help(Text(label))\n .popover(isPresented: $isPresented, arrowEdge: .bottom) {\n LumenSymbolPicker(label, selectedName: $selectedName, options: options)\n .padding(LumenSpacing.lg)\n .frame(width: 400, height: 480)\n .lumenTheme(theme)\n }\n }\n}\n#endif\n",
|
|
38794
|
+
"packages/swift/Sources/LumenUI/NavigationComponents.swift": "import SwiftUI\n\npublic struct LumenNavigationItem<Value: Hashable>: Identifiable {\n public let disabled: Bool\n public let label: LocalizedStringKey\n public let selectedSystemName: String?\n public let systemName: String?\n public let value: Value\n\n public var id: Value { value }\n\n public init(\n _ label: LocalizedStringKey,\n value: Value,\n systemName: String? = nil,\n selectedSystemName: String? = nil,\n disabled: Bool = false\n ) {\n self.label = label\n self.value = value\n self.systemName = systemName\n self.selectedSystemName = selectedSystemName\n self.disabled = disabled\n }\n}\n\n/// A controlled destination bar that leaves navigation paths and presented content app-owned.\npublic struct LumenNavigationBar<Selection: Hashable>: View {\n @Binding private var selection: Selection\n @Environment(\\.lumenTheme) private var theme\n\n private let items: [LumenNavigationItem<Selection>]\n private let label: LocalizedStringKey\n\n public init(\n _ label: LocalizedStringKey = \"Primary navigation\",\n selection: Binding<Selection>,\n items: [LumenNavigationItem<Selection>]\n ) {\n self.label = label\n _selection = selection\n self.items = items\n }\n\n public var body: some View {\n HStack(spacing: LumenSpacing.xs) {\n ForEach(items) { item in\n let selected = selection == item.value\n\n Button {\n selection = item.value\n } label: {\n VStack(spacing: LumenSpacing.xs) {\n if let systemName = selected ? item.selectedSystemName ?? item.systemName : item.systemName {\n LumenIcon(\n systemName: systemName,\n size: .md,\n color: selected ? theme.colors.brand : theme.colors.inkMuted\n )\n }\n\n Text(item.label)\n .font(.caption.weight(selected ? .semibold : .medium))\n .foregroundStyle(selected ? theme.colors.brand : theme.colors.inkMuted)\n .lineLimit(1)\n }\n .frame(maxWidth: .infinity, minHeight: 48)\n .contentShape(Rectangle())\n }\n .buttonStyle(.plain)\n .disabled(item.disabled)\n .opacity(item.disabled ? 0.52 : 1)\n .accessibilityAddTraits(selected ? .isSelected : [])\n }\n }\n .padding(.horizontal, LumenSpacing.sm)\n .padding(.vertical, LumenSpacing.sm)\n .background(theme.colors.surface)\n .overlay(alignment: .top) {\n Rectangle()\n .fill(theme.colors.line)\n .frame(height: 1)\n .accessibilityHidden(true)\n }\n .accessibilityElement(children: .contain)\n .accessibilityLabel(label)\n }\n}\n",
|
|
38795
|
+
"packages/swift/Sources/LumenUI/OverlayComponents.swift": "import CoreTransferable\nimport SwiftUI\n\nprivate struct LumenAlertDialogModifier: ViewModifier {\n @Binding var isPresented: Bool\n\n let cancelLabel: LocalizedStringKey\n let confirmDisabled: Bool\n let confirmLabel: LocalizedStringKey\n let confirmLoading: Bool\n let confirmRole: ButtonRole?\n let description: LocalizedStringKey?\n let onConfirm: () -> Void\n let title: LocalizedStringKey\n\n func body(content: Content) -> some View {\n content.alert(title, isPresented: $isPresented) {\n Button(cancelLabel, role: .cancel) {}\n Button(role: confirmRole, action: onConfirm) {\n HStack(spacing: LumenSpacing.sm) {\n if confirmLoading {\n ProgressView()\n .controlSize(.small)\n }\n Text(confirmLabel)\n }\n }\n .disabled(confirmDisabled || confirmLoading)\n .accessibilityValue(confirmLoading ? Text(\"Loading\") : Text(\"\"))\n } message: {\n if let description {\n Text(description)\n }\n }\n }\n}\n\npublic extension View {\n /// Presents a controlled native confirmation alert with explicit cancel and confirm actions.\n func lumenAlertDialog(\n isPresented: Binding<Bool>,\n title: LocalizedStringKey,\n description: LocalizedStringKey? = nil,\n confirmLabel: LocalizedStringKey,\n cancelLabel: LocalizedStringKey = \"Cancel\",\n confirmRole: ButtonRole? = nil,\n confirmDisabled: Bool = false,\n confirmLoading: Bool = false,\n onConfirm: @escaping () -> Void\n ) -> some View {\n modifier(\n LumenAlertDialogModifier(\n isPresented: isPresented,\n cancelLabel: cancelLabel,\n confirmDisabled: confirmDisabled,\n confirmLabel: confirmLabel,\n confirmLoading: confirmLoading,\n confirmRole: confirmRole,\n description: description,\n onConfirm: onConfirm,\n title: title\n )\n )\n }\n}\n\nprivate struct LumenSheetModifier<SheetContent: View, Actions: View>: ViewModifier {\n @Binding var isPresented: Bool\n\n let actions: Actions\n let description: LocalizedStringKey?\n let onDismiss: () -> Void\n let sheetContent: SheetContent\n let title: LocalizedStringKey?\n\n func body(content: Content) -> some View {\n content.sheet(isPresented: $isPresented, onDismiss: onDismiss) {\n VStack(alignment: .leading, spacing: LumenSpacing.lg) {\n if title != nil || description != nil {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n if let title {\n Text(title)\n .font(.title2.weight(.semibold))\n .accessibilityAddTraits(.isHeader)\n }\n if let description {\n Text(description)\n .font(.callout)\n .foregroundStyle(.secondary)\n }\n }\n }\n\n sheetContent\n\n actions\n .frame(maxWidth: .infinity, alignment: .trailing)\n }\n .padding(LumenSpacing.xl)\n }\n }\n}\n\npublic extension View {\n /// Presents application-owned content in a native sheet with an optional heading and actions.\n func lumenSheet<SheetContent: View, Actions: View>(\n isPresented: Binding<Bool>,\n title: LocalizedStringKey? = nil,\n description: LocalizedStringKey? = nil,\n onDismiss: @escaping () -> Void = {},\n @ViewBuilder actions: () -> Actions,\n @ViewBuilder content: () -> SheetContent\n ) -> some View {\n modifier(\n LumenSheetModifier(\n isPresented: isPresented,\n actions: actions(),\n description: description,\n onDismiss: onDismiss,\n sheetContent: content(),\n title: title\n )\n )\n }\n\n func lumenSheet<SheetContent: View>(\n isPresented: Binding<Bool>,\n title: LocalizedStringKey? = nil,\n description: LocalizedStringKey? = nil,\n onDismiss: @escaping () -> Void = {},\n @ViewBuilder content: () -> SheetContent\n ) -> some View {\n lumenSheet(\n isPresented: isPresented,\n title: title,\n description: description,\n onDismiss: onDismiss,\n actions: { EmptyView() },\n content: content\n )\n }\n}\n\npublic struct LumenMenuItem {\n fileprivate let action: () -> Void\n fileprivate let disabled: Bool\n fileprivate let role: ButtonRole?\n fileprivate let systemName: String?\n fileprivate let title: LocalizedStringKey\n\n public init(\n _ title: LocalizedStringKey,\n systemName: String? = nil,\n role: ButtonRole? = nil,\n disabled: Bool = false,\n action: @escaping () -> Void\n ) {\n self.title = title\n self.systemName = systemName\n self.role = role\n self.disabled = disabled\n self.action = action\n }\n}\n\n/// A native anchored menu that preserves disabled and destructive action semantics.\npublic struct LumenMenu<MenuLabel: View>: View {\n private let items: [LumenMenuItem]\n private let label: MenuLabel\n\n public init(\n items: [LumenMenuItem],\n @ViewBuilder label: () -> MenuLabel\n ) {\n self.items = items\n self.label = label()\n }\n\n public var body: some View {\n Menu {\n ForEach(Array(items.enumerated()), id: \\.offset) { _, item in\n Button(role: item.role, action: item.action) {\n if let systemName = item.systemName {\n SwiftUI.Label(item.title, systemImage: systemName)\n } else {\n Text(item.title)\n }\n }\n .disabled(item.disabled)\n }\n } label: {\n label\n }\n }\n}\n\n/// A Lumen-styled button backed by SwiftUI's native ShareLink presentation.\npublic struct LumenShareButton: View {\n private let shareLink: AnyView\n\n public init<Label: View>(\n item: String,\n @ViewBuilder label: () -> Label\n ) {\n shareLink = AnyView(\n ShareLink(item: item, label: label)\n .buttonStyle(LumenButtonStyle(intent: .secondary))\n )\n }\n\n public init<Label: View>(\n item: URL,\n @ViewBuilder label: () -> Label\n ) {\n shareLink = AnyView(\n ShareLink(item: item, label: label)\n .buttonStyle(LumenButtonStyle(intent: .secondary))\n )\n }\n\n @available(tvOS, unavailable)\n public init<\n Item: Transferable,\n PreviewImage: Transferable,\n PreviewIcon: Transferable,\n Label: View\n >(\n item: Item,\n preview: SharePreview<PreviewImage, PreviewIcon>,\n @ViewBuilder label: () -> Label\n ) {\n shareLink = AnyView(\n ShareLink(item: item, preview: preview, label: label)\n .buttonStyle(LumenButtonStyle(intent: .secondary))\n )\n }\n\n public init(_ label: LocalizedStringKey = \"Share\", item: String) {\n self.init(item: item) {\n Text(label)\n }\n }\n\n public init(_ label: LocalizedStringKey = \"Share\", item: URL) {\n self.init(item: item) {\n Text(label)\n }\n }\n\n public var body: some View {\n shareLink\n }\n}\n",
|
|
36849
38796
|
"packages/swift/Sources/LumenUI/Platform.swift": "import SwiftUI\n\n/// The amount of space Lumen controls reserve for pointer or touch interaction.\npublic enum LumenControlDensity: Equatable, Sendable {\n /// Compact controls sized for pointer-first macOS interfaces.\n case compact\n /// Larger controls sized for touch-first iOS interfaces.\n case regular\n\n public static var platformDefault: LumenControlDensity {\n #if os(macOS) || targetEnvironment(macCatalyst)\n .compact\n #else\n .regular\n #endif\n }\n}\n\nprivate struct LumenControlDensityKey: EnvironmentKey {\n static let defaultValue = LumenControlDensity.platformDefault\n}\n\npublic extension EnvironmentValues {\n var lumenControlDensity: LumenControlDensity {\n get { self[LumenControlDensityKey.self] }\n set { self[LumenControlDensityKey.self] = newValue }\n }\n}\n\npublic extension View {\n /// Overrides Lumen's automatic macOS or iOS control density for this view hierarchy.\n func lumenControlDensity(_ density: LumenControlDensity) -> some View {\n environment(\\.lumenControlDensity, density)\n }\n}\n",
|
|
36850
38797
|
"packages/swift/Sources/LumenUI/Primitives.swift": "import SwiftUI\n\npublic enum LumenTextTone: Sendable {\n case danger\n case `default`\n case muted\n case soft\n case success\n case warning\n}\n\npublic enum LumenTextVariant: Sendable {\n case body\n case caption\n case label\n case title\n}\n\npublic struct LumenText: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let content: LocalizedStringKey\n private let tone: LumenTextTone\n private let variant: LumenTextVariant\n\n public init(\n _ content: LocalizedStringKey,\n variant: LumenTextVariant = .body,\n tone: LumenTextTone = .default\n ) {\n self.content = content\n self.variant = variant\n self.tone = tone\n }\n\n public var body: some View {\n Text(content)\n .font(font)\n .foregroundStyle(foregroundColor)\n }\n\n private var font: Font {\n switch variant {\n case .body: .body\n case .caption: .caption\n case .label: .callout.weight(.semibold)\n case .title: .title2.weight(.bold)\n }\n }\n\n private var foregroundColor: Color {\n switch tone {\n case .danger: theme.colors.danger\n case .default: theme.colors.ink\n case .muted: theme.colors.inkMuted\n case .soft: theme.colors.inkSoft\n case .success: theme.colors.success\n case .warning: theme.colors.warning\n }\n }\n}\n\npublic enum LumenSurfacePadding: Sendable {\n case lg\n case md\n case none\n case sm\n\n var value: CGFloat {\n switch self {\n case .lg: LumenSpacing.lg\n case .md: LumenSpacing.md\n case .none: 0\n case .sm: LumenSpacing.sm\n }\n }\n}\n\npublic enum LumenSurfaceRadius: Sendable {\n case lg\n case md\n case none\n case sm\n\n var value: CGFloat {\n switch self {\n case .lg: LumenRadius.lg\n case .md: LumenRadius.md\n case .none: 0\n case .sm: LumenRadius.sm\n }\n }\n}\n\npublic enum LumenSurfaceTone: Sendable {\n case canvas\n case muted\n case strong\n case surface\n}\n\npublic struct LumenSurface<Content: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let content: Content\n private let padding: LumenSurfacePadding\n private let radius: LumenSurfaceRadius\n private let tone: LumenSurfaceTone\n\n public init(\n tone: LumenSurfaceTone = .surface,\n padding: LumenSurfacePadding = .md,\n radius: LumenSurfaceRadius = .md,\n @ViewBuilder content: () -> Content\n ) {\n self.content = content()\n self.padding = padding\n self.radius = radius\n self.tone = tone\n }\n\n public var body: some View {\n content\n .padding(padding.value)\n .background(backgroundColor)\n .clipShape(RoundedRectangle(cornerRadius: radius.value, style: .continuous))\n }\n\n private var backgroundColor: Color {\n switch tone {\n case .canvas: theme.colors.canvas\n case .muted: theme.colors.surfaceMuted\n case .strong: theme.colors.surfaceStrong\n case .surface: theme.colors.surface\n }\n }\n}\n\npublic enum LumenButtonIntent: Sendable {\n case danger\n case primary\n case quiet\n case secondary\n}\n\npublic enum LumenControlSize: Sendable {\n case lg\n case md\n case sm\n}\n\npublic enum LumenIconSize: Sendable {\n case lg\n case md\n case sm\n\n var dimension: CGFloat {\n switch self {\n case .lg: 24\n case .md: 20\n case .sm: 16\n }\n }\n}\n\npublic struct LumenIcon: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let color: Color?\n private let label: LocalizedStringKey?\n private let size: LumenIconSize\n private let systemName: String\n\n public init(\n systemName: String,\n size: LumenIconSize = .md,\n color: Color? = nil,\n label: LocalizedStringKey? = nil\n ) {\n self.systemName = systemName\n self.size = size\n self.color = color\n self.label = label\n }\n\n public var body: some View {\n Image(systemName: systemName)\n .font(.system(size: size.dimension, weight: .medium))\n .foregroundStyle(color ?? theme.colors.ink)\n .frame(width: size.dimension, height: size.dimension)\n .accessibilityHidden(label == nil)\n .accessibilityLabel(label ?? \"\")\n }\n}\n\npublic struct LumenIconButtonMetrics: Equatable, Sendable {\n public let iconSize: LumenIconSize\n public let touchTarget: CGFloat\n\n public static func resolve(\n _ size: LumenControlSize,\n density: LumenControlDensity = .platformDefault\n ) -> LumenIconButtonMetrics {\n switch (density, size) {\n case (.compact, .lg): LumenIconButtonMetrics(iconSize: .lg, touchTarget: 40)\n case (.compact, .md): LumenIconButtonMetrics(iconSize: .md, touchTarget: 32)\n case (.compact, .sm): LumenIconButtonMetrics(iconSize: .sm, touchTarget: 28)\n case (.regular, .lg): LumenIconButtonMetrics(iconSize: .lg, touchTarget: 52)\n case (.regular, .md): LumenIconButtonMetrics(iconSize: .md, touchTarget: 44)\n case (.regular, .sm): LumenIconButtonMetrics(iconSize: .sm, touchTarget: 44)\n }\n }\n}\n\npublic struct LumenIconButtonStyle: ButtonStyle {\n @Environment(\\.lumenControlDensity) private var density\n @Environment(\\.isEnabled) private var isEnabled\n @Environment(\\.lumenTheme) private var theme\n\n private let intent: LumenButtonIntent\n private let size: LumenControlSize\n\n public init(intent: LumenButtonIntent = .quiet, size: LumenControlSize = .md) {\n self.intent = intent\n self.size = size\n }\n\n public func makeBody(configuration: Configuration) -> some View {\n let metrics = LumenIconButtonMetrics.resolve(size, density: density)\n\n configuration.label\n .frame(width: metrics.touchTarget, height: metrics.touchTarget)\n .background(backgroundColor)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous)\n .stroke(borderColor, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous))\n .opacity(isEnabled ? (configuration.isPressed ? 0.84 : 1) : 0.52)\n }\n\n private var backgroundColor: Color {\n switch intent {\n case .danger: theme.colors.danger\n case .primary: theme.colors.brandSolid\n case .quiet: .clear\n case .secondary: theme.colors.surfaceMuted\n }\n }\n\n private var borderColor: Color {\n switch intent {\n case .danger: theme.colors.danger\n case .primary: theme.colors.brandSolid\n case .quiet: .clear\n case .secondary: theme.colors.line\n }\n }\n}\n\npublic struct LumenIconButton: View {\n @Environment(\\.lumenControlDensity) private var density\n @Environment(\\.lumenTheme) private var theme\n\n private let action: () -> Void\n private let disabled: Bool\n private let intent: LumenButtonIntent\n private let label: LocalizedStringKey\n private let size: LumenControlSize\n private let systemName: String\n\n public init(\n systemName: String,\n label: LocalizedStringKey,\n intent: LumenButtonIntent = .quiet,\n size: LumenControlSize = .md,\n disabled: Bool = false,\n action: @escaping () -> Void\n ) {\n self.systemName = systemName\n self.label = label\n self.intent = intent\n self.size = size\n self.disabled = disabled\n self.action = action\n }\n\n public var body: some View {\n let metrics = LumenIconButtonMetrics.resolve(size, density: density)\n\n Button(action: action) {\n LumenIcon(\n systemName: systemName,\n size: metrics.iconSize,\n color: foregroundColor\n )\n }\n .buttonStyle(LumenIconButtonStyle(intent: intent, size: size))\n .disabled(disabled)\n .accessibilityLabel(label)\n }\n\n private var foregroundColor: Color {\n switch intent {\n case .danger: theme.colors.onDanger\n case .primary: theme.colors.onBrand\n case .quiet: theme.colors.inkSoft\n case .secondary: theme.colors.ink\n }\n }\n}\n\npublic struct LumenButtonMetrics: Equatable, Sendable {\n public let horizontalPadding: CGFloat\n public let minHeight: CGFloat\n\n public static func resolve(\n _ size: LumenControlSize,\n density: LumenControlDensity = .platformDefault\n ) -> LumenButtonMetrics {\n switch (density, size) {\n case (.compact, .lg):\n LumenButtonMetrics(horizontalPadding: LumenSpacing.lg, minHeight: 40)\n case (.compact, .md):\n LumenButtonMetrics(horizontalPadding: LumenSpacing.md, minHeight: 32)\n case (.compact, .sm):\n LumenButtonMetrics(horizontalPadding: LumenSpacing.md, minHeight: 28)\n case (.regular, .lg):\n LumenButtonMetrics(horizontalPadding: LumenSpacing.xl, minHeight: 52)\n case (.regular, .md):\n LumenButtonMetrics(horizontalPadding: LumenSpacing.lg, minHeight: 44)\n case (.regular, .sm):\n LumenButtonMetrics(horizontalPadding: LumenSpacing.md, minHeight: 36)\n }\n }\n}\n\npublic struct LumenButtonStyle: ButtonStyle {\n @Environment(\\.lumenControlDensity) private var density\n @Environment(\\.isEnabled) private var isEnabled\n @Environment(\\.lumenTheme) private var theme\n\n private let intent: LumenButtonIntent\n private let size: LumenControlSize\n\n public init(\n intent: LumenButtonIntent = .primary,\n size: LumenControlSize = .md\n ) {\n self.intent = intent\n self.size = size\n }\n\n public func makeBody(configuration: Configuration) -> some View {\n let metrics = LumenButtonMetrics.resolve(size, density: density)\n\n configuration.label\n .font(size == .lg ? .body.weight(.semibold) : .callout.weight(.semibold))\n .foregroundStyle(foregroundColor)\n .frame(minHeight: metrics.minHeight)\n .padding(.horizontal, metrics.horizontalPadding)\n .background(backgroundColor)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous)\n .stroke(borderColor, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous))\n .opacity(isEnabled ? (configuration.isPressed ? 0.84 : 1) : 0.52)\n }\n\n private var backgroundColor: Color {\n switch intent {\n case .danger: theme.colors.danger\n case .primary: theme.colors.brandSolid\n case .quiet: .clear\n case .secondary: theme.colors.surfaceMuted\n }\n }\n\n private var borderColor: Color {\n switch intent {\n case .danger: theme.colors.danger\n case .primary: theme.colors.brandSolid\n case .quiet: .clear\n case .secondary: theme.colors.line\n }\n }\n\n private var foregroundColor: Color {\n switch intent {\n case .danger: theme.colors.onDanger\n case .primary: theme.colors.onBrand\n case .quiet: theme.colors.inkSoft\n case .secondary: theme.colors.ink\n }\n }\n}\n\npublic struct LumenButton<Label: View>: View {\n private let action: () -> Void\n private let disabled: Bool\n private let intent: LumenButtonIntent\n private let label: Label\n private let loading: Bool\n private let size: LumenControlSize\n\n public init(\n intent: LumenButtonIntent = .primary,\n size: LumenControlSize = .md,\n loading: Bool = false,\n disabled: Bool = false,\n action: @escaping () -> Void,\n @ViewBuilder label: () -> Label\n ) {\n self.action = action\n self.disabled = disabled\n self.intent = intent\n self.label = label()\n self.loading = loading\n self.size = size\n }\n\n public var body: some View {\n Button(action: action) {\n HStack(spacing: LumenSpacing.sm) {\n if loading {\n ProgressView()\n .controlSize(.small)\n }\n label\n }\n }\n .buttonStyle(LumenButtonStyle(intent: intent, size: size))\n .disabled(disabled || loading)\n .accessibilityValue(loading ? \"Loading\" : \"\")\n }\n}\n\npublic extension LumenButton where Label == Text {\n init(\n _ title: LocalizedStringKey,\n intent: LumenButtonIntent = .primary,\n size: LumenControlSize = .md,\n loading: Bool = false,\n disabled: Bool = false,\n action: @escaping () -> Void\n ) {\n self.init(\n intent: intent,\n size: size,\n loading: loading,\n disabled: disabled,\n action: action\n ) {\n Text(title)\n }\n }\n}\n\npublic struct LumenTextField: View {\n @Binding private var text: String\n @Environment(\\.isEnabled) private var isEnabled\n @Environment(\\.lumenControlDensity) private var density\n @Environment(\\.lumenTheme) private var theme\n\n private let error: Bool\n private let errorMessage: String?\n private let size: LumenControlSize\n private let title: String\n\n public init(\n _ title: String,\n text: Binding<String>,\n size: LumenControlSize = .md,\n error: Bool = false,\n errorMessage: String? = nil\n ) {\n self.title = title\n _text = text\n self.size = size\n self.error = error\n self.errorMessage = errorMessage\n }\n\n public var body: some View {\n TextField(title, text: $text)\n .textFieldStyle(.plain)\n .font(size == .lg ? .body : .callout)\n .foregroundStyle(theme.colors.ink)\n .frame(minHeight: LumenButtonMetrics.resolve(size, density: density).minHeight)\n .padding(.horizontal, LumenSpacing.md)\n .background(theme.colors.surface)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous)\n .stroke(error ? theme.colors.danger : theme.colors.line, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous))\n .opacity(isEnabled ? 1 : 0.52)\n .accessibilityValue(error ? errorMessage ?? \"Invalid value\" : text)\n }\n}\n\npublic enum LumenBadgeTone: Sendable {\n case accent\n case danger\n case neutral\n case success\n case warning\n}\n\npublic struct LumenBadge: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let content: LocalizedStringKey\n private let tone: LumenBadgeTone\n\n public init(\n _ content: LocalizedStringKey,\n tone: LumenBadgeTone = .neutral\n ) {\n self.content = content\n self.tone = tone\n }\n\n public var body: some View {\n Text(content)\n .font(.caption.weight(.semibold))\n .foregroundStyle(foregroundColor)\n .padding(.horizontal, LumenSpacing.sm)\n .padding(.vertical, LumenSpacing.xs)\n .background(backgroundColor)\n .clipShape(Capsule())\n }\n\n private var foregroundColor: Color {\n switch tone {\n case .accent: theme.colors.accent\n case .danger: theme.colors.danger\n case .neutral: theme.colors.inkSoft\n case .success: theme.colors.success\n case .warning: theme.colors.warning\n }\n }\n\n private var backgroundColor: Color {\n tone == .neutral ? theme.colors.surfaceMuted : foregroundColor.opacity(0.12)\n }\n}\n\npublic struct LumenDivider: View {\n @Environment(\\.lumenTheme) private var theme\n\n public init() {}\n\n public var body: some View {\n Rectangle()\n .fill(theme.colors.line)\n .frame(height: 1)\n .accessibilityHidden(true)\n }\n}\n\npublic struct LumenSpinner: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let label: LocalizedStringKey\n\n public init(_ label: LocalizedStringKey = \"Loading\") {\n self.label = label\n }\n\n public var body: some View {\n ProgressView()\n .tint(theme.colors.brand)\n .accessibilityLabel(label)\n }\n}\n",
|
|
36851
38798
|
"packages/swift/Sources/LumenUI/SelectionControls.swift": "#if os(iOS) || os(macOS)\nimport SwiftUI\n\npublic struct LumenSelectionOption<Value: Hashable>: Identifiable {\n public let description: String?\n public let id: Value\n public let isDisabled: Bool\n public let title: String\n\n public init(\n _ title: String,\n value: Value,\n description: String? = nil,\n isDisabled: Bool = false\n ) {\n self.title = title\n id = value\n self.description = description\n self.isDisabled = isDisabled\n }\n}\n\npublic struct LumenCheckbox: View {\n @Binding private var isChecked: Bool\n @Environment(\\.isEnabled) private var isEnabled\n @Environment(\\.lumenTheme) private var theme\n\n private let description: String?\n private let label: String\n\n public init(_ label: String, isChecked: Binding<Bool>, description: String? = nil) {\n self.label = label\n _isChecked = isChecked\n self.description = description\n }\n\n public var body: some View {\n Button { isChecked.toggle() } label: {\n HStack(alignment: .top, spacing: LumenSpacing.md) {\n Image(systemName: isChecked ? \"checkmark.square.fill\" : \"square\")\n .font(.system(size: 20, weight: .semibold))\n .foregroundStyle(isChecked ? theme.colors.brandSolid : theme.colors.inkMuted)\n .frame(width: 24, height: 24)\n .accessibilityHidden(true)\n\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n Text(label).font(.callout.weight(.semibold)).foregroundStyle(theme.colors.ink)\n if let description {\n Text(description)\n .font(.caption)\n .foregroundStyle(theme.colors.inkMuted)\n .fixedSize(horizontal: false, vertical: true)\n }\n }\n .frame(maxWidth: .infinity, alignment: .leading)\n }\n .contentShape(Rectangle())\n }\n .buttonStyle(.plain)\n .opacity(isEnabled ? 1 : 0.52)\n .accessibilityLabel(Text(label))\n .accessibilityValue(Text(isChecked ? \"Checked\" : \"Not checked\"))\n }\n}\n\npublic struct LumenRadioGroup<Value: Hashable>: View {\n @Binding private var selection: Value\n @Environment(\\.lumenTheme) private var theme\n\n private let label: String\n private let options: [LumenSelectionOption<Value>]\n\n public init(_ label: String, selection: Binding<Value>, options: [LumenSelectionOption<Value>]) {\n self.label = label\n _selection = selection\n self.options = options\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n Text(label).font(.callout.weight(.semibold)).foregroundStyle(theme.colors.ink)\n ForEach(options) { option in\n let isSelected = selection == option.id\n\n Button { selection = option.id } label: {\n HStack(alignment: .top, spacing: LumenSpacing.md) {\n Image(systemName: isSelected ? \"largecircle.fill.circle\" : \"circle\")\n .font(.system(size: 20, weight: .semibold))\n .foregroundStyle(isSelected ? theme.colors.brandSolid : theme.colors.inkMuted)\n .frame(width: 24, height: 24)\n .accessibilityHidden(true)\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n Text(option.title).font(.callout).foregroundStyle(theme.colors.ink)\n if let description = option.description {\n Text(description)\n .font(.caption)\n .foregroundStyle(theme.colors.inkMuted)\n .fixedSize(horizontal: false, vertical: true)\n }\n }\n .frame(maxWidth: .infinity, alignment: .leading)\n }\n .contentShape(Rectangle())\n }\n .buttonStyle(.plain)\n .disabled(option.isDisabled)\n .opacity(option.isDisabled ? 0.52 : 1)\n .accessibilityLabel(Text(option.title))\n .accessibilityValue(Text(isSelected ? \"Selected\" : \"Not selected\"))\n .accessibilityAddTraits(isSelected ? .isSelected : [])\n }\n }\n .accessibilityElement(children: .contain)\n .accessibilityLabel(Text(label))\n }\n}\n\npublic struct LumenSegmentedControl<Value: Hashable>: View {\n @Binding private var selection: Value\n @Environment(\\.lumenTheme) private var theme\n\n private let label: String\n private let options: [LumenSelectionOption<Value>]\n private let showsLabel: Bool\n\n public init(\n _ label: String,\n selection: Binding<Value>,\n options: [LumenSelectionOption<Value>],\n showsLabel: Bool = true\n ) {\n self.label = label\n _selection = selection\n self.options = options\n self.showsLabel = showsLabel\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n if showsLabel {\n Text(label).font(.callout.weight(.semibold)).foregroundStyle(theme.colors.ink)\n }\n HStack(spacing: 2) {\n ForEach(options) { option in\n let isSelected = selection == option.id\n\n Button { selection = option.id } label: {\n Text(option.title)\n .font(.caption.weight(isSelected ? .bold : .semibold))\n .lineLimit(1)\n .frame(maxWidth: .infinity, minHeight: 36)\n .padding(.horizontal, LumenSpacing.sm)\n .foregroundStyle(isSelected ? theme.colors.brand : theme.colors.inkSoft)\n .background(isSelected ? theme.colors.surface : .clear)\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous))\n }\n .buttonStyle(.plain)\n .disabled(option.isDisabled)\n .opacity(option.isDisabled ? 0.52 : 1)\n .accessibilityLabel(Text(option.title))\n .accessibilityValue(Text(isSelected ? \"Selected\" : \"Not selected\"))\n .accessibilityAddTraits(isSelected ? .isSelected : [])\n }\n }\n .padding(2)\n .background(theme.colors.surfaceMuted)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous)\n .stroke(theme.colors.line, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous))\n .accessibilityElement(children: .contain)\n .accessibilityLabel(Text(label))\n }\n }\n}\n#endif\n",
|
|
36852
38799
|
"packages/swift/Sources/LumenUI/SharedComponents.swift": "import SwiftUI\n\npublic enum LumenCardVariant: Sendable {\n case accent\n case `default`\n case destructive\n case muted\n case success\n case warning\n}\n\npublic struct LumenCard<Content: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let action: (() -> Void)?\n private let content: Content\n private let variant: LumenCardVariant\n\n public init(\n variant: LumenCardVariant = .default,\n action: (() -> Void)? = nil,\n @ViewBuilder content: () -> Content\n ) {\n self.variant = variant\n self.action = action\n self.content = content()\n }\n\n public var body: some View {\n Group {\n if let action {\n Button(action: action) {\n cardSurface\n }\n .buttonStyle(LumenCardButtonStyle())\n } else {\n cardSurface\n }\n }\n }\n\n private var cardSurface: some View {\n content\n .frame(maxWidth: .infinity, alignment: .leading)\n .padding(LumenSpacing.xl)\n .background(backgroundColor)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.lg, style: .continuous)\n .stroke(borderColor, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.lg, style: .continuous))\n }\n\n private var accentColor: Color? {\n switch variant {\n case .accent: theme.colors.accent\n case .default, .muted: nil\n case .destructive: theme.colors.danger\n case .success: theme.colors.success\n case .warning: theme.colors.warning\n }\n }\n\n private var backgroundColor: Color {\n switch variant {\n case .default: theme.colors.surface\n case .muted: theme.colors.surfaceMuted\n case .accent, .destructive, .success, .warning:\n accentColor?.opacity(0.06) ?? theme.colors.surface\n }\n }\n\n private var borderColor: Color {\n accentColor?.opacity(0.24) ?? theme.colors.line\n }\n}\n\nprivate struct LumenCardButtonStyle: ButtonStyle {\n func makeBody(configuration: Configuration) -> some View {\n configuration.label\n .opacity(configuration.isPressed ? 0.84 : 1)\n .scaleEffect(configuration.isPressed ? 0.995 : 1)\n }\n}\n\npublic enum LumenAlertVariant: Sendable {\n case `default`\n case destructive\n case success\n case warning\n}\n\npublic struct LumenAlert<Content: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let content: Content\n private let variant: LumenAlertVariant\n\n public init(\n variant: LumenAlertVariant = .default,\n @ViewBuilder content: () -> Content\n ) {\n self.variant = variant\n self.content = content()\n }\n\n public var body: some View {\n content\n .frame(maxWidth: .infinity, alignment: .leading)\n .padding(.horizontal, LumenSpacing.lg)\n .padding(.vertical, LumenSpacing.md)\n .foregroundStyle(foregroundColor)\n .background(backgroundColor)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous)\n .stroke(borderColor, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous))\n }\n\n private var accentColor: Color {\n switch variant {\n case .default: theme.colors.ink\n case .destructive: theme.colors.danger\n case .success: theme.colors.success\n case .warning: theme.colors.warning\n }\n }\n\n private var backgroundColor: Color {\n variant == .default ? theme.colors.surface : accentColor.opacity(0.08)\n }\n\n private var borderColor: Color {\n variant == .default ? theme.colors.line : accentColor.opacity(0.36)\n }\n\n private var foregroundColor: Color {\n variant == .default ? theme.colors.ink : accentColor\n }\n}\n\npublic struct LumenProgressValue: Equatable, Sendable {\n public let max: Double\n public let value: Double\n\n public var fraction: Double { value / max }\n\n public static func resolve(value: Double, max: Double) -> LumenProgressValue {\n let safeMax = max.isFinite && max > 0 ? max : 100\n let finiteValue = value.isFinite ? value : 0\n let safeValue = min(safeMax, Swift.max(0, finiteValue))\n\n return LumenProgressValue(max: safeMax, value: safeValue)\n }\n}\n\npublic struct LumenProgress: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let label: String?\n private let progress: LumenProgressValue\n\n public init(\n value: Double = 0,\n max: Double = 100,\n label: String? = nil\n ) {\n progress = LumenProgressValue.resolve(value: value, max: max)\n self.label = label\n }\n\n public var body: some View {\n GeometryReader { geometry in\n ZStack(alignment: .leading) {\n Capsule().fill(theme.colors.surfaceStrong)\n Capsule()\n .fill(theme.colors.brandSolid)\n .frame(width: geometry.size.width * progress.fraction)\n }\n }\n .frame(height: LumenSpacing.sm)\n .accessibilityElement(children: .ignore)\n .accessibilityLabel(Text(label ?? \"Progress\"))\n .accessibilityValue(Text(\"\\(Int(progress.fraction * 100)) percent\"))\n }\n}\n\npublic enum LumenAvatarSize: Sendable {\n case lg\n case md\n case sm\n\n var dimension: CGFloat {\n switch self {\n case .lg: 56\n case .md: 40\n case .sm: 32\n }\n }\n}\n\npublic struct LumenAvatar: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let fallback: String\n private let image: Image?\n private let label: String?\n private let size: LumenAvatarSize\n\n public init(\n image: Image? = nil,\n fallback: String = \"?\",\n size: LumenAvatarSize = .md,\n label: String? = nil\n ) {\n self.image = image\n self.fallback = fallback\n self.size = size\n self.label = label\n }\n\n public var body: some View {\n ZStack {\n Circle().fill(theme.colors.surfaceMuted)\n\n if let image {\n image\n .resizable()\n .scaledToFill()\n } else {\n Text(fallback)\n .font(fallbackFont)\n .foregroundStyle(theme.colors.ink)\n }\n }\n .frame(width: size.dimension, height: size.dimension)\n .clipShape(Circle())\n .overlay {\n Circle().stroke(theme.colors.line, lineWidth: 1)\n }\n .accessibilityElement(children: .ignore)\n .accessibilityHidden(label == nil)\n .accessibilityLabel(Text(label ?? \"\"))\n }\n\n private var fallbackFont: Font {\n size == .lg ? .headline.weight(.bold) : .callout.weight(.bold)\n }\n}\n",
|
|
36853
38800
|
"packages/swift/Sources/LumenUI/StructuredComponents.swift": "import SwiftUI\n\npublic struct LumenEmptyState<Actions: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let actions: Actions\n private let description: LocalizedStringKey?\n private let systemName: String\n private let title: LocalizedStringKey\n\n public init(\n _ title: LocalizedStringKey,\n systemName: String,\n description: LocalizedStringKey? = nil,\n @ViewBuilder actions: () -> Actions\n ) {\n self.title = title\n self.systemName = systemName\n self.description = description\n self.actions = actions()\n }\n\n public var body: some View {\n VStack(spacing: LumenSpacing.lg) {\n LumenIcon(systemName: systemName, size: .lg, color: theme.colors.inkMuted)\n .padding(LumenSpacing.md)\n .background(theme.colors.surfaceMuted, in: Circle())\n\n VStack(spacing: LumenSpacing.sm) {\n Text(title)\n .font(.headline)\n .foregroundStyle(theme.colors.ink)\n\n if let description {\n Text(description)\n .font(.callout)\n .foregroundStyle(theme.colors.inkMuted)\n .multilineTextAlignment(.center)\n .fixedSize(horizontal: false, vertical: true)\n }\n }\n\n actions\n }\n .frame(maxWidth: 440)\n .padding(LumenSpacing.xl)\n .frame(maxWidth: .infinity, maxHeight: .infinity)\n .accessibilityElement(children: .contain)\n }\n}\n\npublic extension LumenEmptyState where Actions == EmptyView {\n init(\n _ title: LocalizedStringKey,\n systemName: String,\n description: LocalizedStringKey? = nil\n ) {\n self.init(title, systemName: systemName, description: description) {\n EmptyView()\n }\n }\n}\n\npublic struct LumenListRow<Leading: View, Content: View, Trailing: View>: View {\n private let content: Content\n private let leading: Leading\n private let trailing: Trailing\n\n public init(\n @ViewBuilder leading: () -> Leading,\n @ViewBuilder content: () -> Content,\n @ViewBuilder trailing: () -> Trailing\n ) {\n self.leading = leading()\n self.content = content()\n self.trailing = trailing()\n }\n\n public var body: some View {\n HStack(alignment: .center, spacing: LumenSpacing.md) {\n leading\n .fixedSize()\n\n content\n .frame(maxWidth: .infinity, alignment: .leading)\n\n trailing\n .fixedSize(horizontal: true, vertical: false)\n }\n .padding(.horizontal, LumenSpacing.lg)\n .padding(.vertical, LumenSpacing.md)\n .contentShape(Rectangle())\n .accessibilityElement(children: .contain)\n }\n}\n\npublic extension LumenListRow where Trailing == EmptyView {\n init(\n @ViewBuilder leading: () -> Leading,\n @ViewBuilder content: () -> Content\n ) {\n self.init(leading: leading, content: content) {\n EmptyView()\n }\n }\n}\n\npublic enum LumenBannerVariant: Sendable {\n case accent\n case `default`\n case destructive\n case success\n case warning\n}\n\npublic struct LumenBanner<Actions: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let actions: Actions\n private let description: LocalizedStringKey?\n private let onDismiss: (() -> Void)?\n private let systemName: String?\n private let title: LocalizedStringKey\n private let variant: LumenBannerVariant\n\n public init(\n _ title: LocalizedStringKey,\n description: LocalizedStringKey? = nil,\n systemName: String? = nil,\n variant: LumenBannerVariant = .default,\n onDismiss: (() -> Void)? = nil,\n @ViewBuilder actions: () -> Actions\n ) {\n self.title = title\n self.description = description\n self.systemName = systemName\n self.variant = variant\n self.onDismiss = onDismiss\n self.actions = actions()\n }\n\n public var body: some View {\n HStack(alignment: .center, spacing: LumenSpacing.md) {\n LumenIcon(\n systemName: systemName ?? defaultSystemName,\n size: .md,\n color: accentColor\n )\n\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n Text(title)\n .font(.callout.weight(.semibold))\n .foregroundStyle(theme.colors.ink)\n\n if let description {\n Text(description)\n .font(.caption)\n .foregroundStyle(theme.colors.inkSoft)\n .fixedSize(horizontal: false, vertical: true)\n }\n }\n .frame(maxWidth: .infinity, alignment: .leading)\n\n actions\n\n if let onDismiss {\n LumenIconButton(\n systemName: \"xmark\",\n label: \"Dismiss\",\n intent: .quiet,\n size: .sm,\n action: onDismiss\n )\n }\n }\n .padding(.horizontal, LumenSpacing.lg)\n .padding(.vertical, LumenSpacing.md)\n .background(backgroundColor)\n .overlay(alignment: .leading) {\n Rectangle()\n .fill(accentColor)\n .frame(width: 3)\n }\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous)\n .stroke(borderColor, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous))\n .accessibilityElement(children: .contain)\n }\n\n private var accentColor: Color {\n switch variant {\n case .accent: theme.colors.accent\n case .default: theme.colors.brand\n case .destructive: theme.colors.danger\n case .success: theme.colors.success\n case .warning: theme.colors.warning\n }\n }\n\n private var backgroundColor: Color {\n variant == .default ? theme.colors.surface : accentColor.opacity(0.08)\n }\n\n private var borderColor: Color {\n variant == .default ? theme.colors.line : accentColor.opacity(0.32)\n }\n\n private var defaultSystemName: String {\n switch variant {\n case .accent: \"sparkles\"\n case .default: \"info.circle\"\n case .destructive: \"xmark.octagon\"\n case .success: \"checkmark.circle\"\n case .warning: \"exclamationmark.triangle\"\n }\n }\n}\n\npublic extension LumenBanner where Actions == EmptyView {\n init(\n _ title: LocalizedStringKey,\n description: LocalizedStringKey? = nil,\n systemName: String? = nil,\n variant: LumenBannerVariant = .default,\n onDismiss: (() -> Void)? = nil\n ) {\n self.init(\n title,\n description: description,\n systemName: systemName,\n variant: variant,\n onDismiss: onDismiss\n ) {\n EmptyView()\n }\n }\n}\n\npublic enum LumenMetricTone: Sendable {\n case accent\n case brand\n case danger\n case neutral\n case success\n case warning\n}\n\npublic struct LumenStat: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let detail: LocalizedStringKey?\n private let label: LocalizedStringKey\n private let systemName: String?\n private let tone: LumenMetricTone\n private let value: String\n\n public init(\n _ label: LocalizedStringKey,\n value: String,\n detail: LocalizedStringKey? = nil,\n systemName: String? = nil,\n tone: LumenMetricTone = .brand\n ) {\n self.label = label\n self.value = value\n self.detail = detail\n self.systemName = systemName\n self.tone = tone\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n if let systemName {\n LumenIcon(systemName: systemName, size: .sm, color: accentColor)\n }\n\n Text(value)\n .font(.title2.weight(.bold))\n .monospacedDigit()\n .foregroundStyle(theme.colors.ink)\n\n Text(label)\n .font(.caption.weight(.semibold))\n .foregroundStyle(theme.colors.inkSoft)\n\n if let detail {\n Text(detail)\n .font(.caption)\n .foregroundStyle(theme.colors.inkMuted)\n }\n }\n .frame(maxWidth: .infinity, alignment: .leading)\n .padding(LumenSpacing.lg)\n .background(accentColor.opacity(0.06))\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous)\n .stroke(accentColor.opacity(0.18), lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous))\n .accessibilityElement(children: .combine)\n }\n\n private var accentColor: Color {\n metricColor(tone, theme: theme)\n }\n}\n\npublic struct LumenGauge: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let label: LocalizedStringKey\n private let progress: LumenProgressValue\n private let systemName: String?\n private let tone: LumenMetricTone\n private let valueLabel: String\n\n public init(\n _ label: LocalizedStringKey,\n value: Double,\n max: Double = 100,\n valueLabel: String,\n systemName: String? = nil,\n tone: LumenMetricTone = .brand\n ) {\n self.label = label\n progress = LumenProgressValue.resolve(value: value, max: max)\n self.valueLabel = valueLabel\n self.systemName = systemName\n self.tone = tone\n }\n\n public var body: some View {\n VStack(spacing: LumenSpacing.md) {\n ZStack {\n Circle()\n .stroke(theme.colors.surfaceStrong, lineWidth: 10)\n\n Circle()\n .trim(from: 0, to: progress.fraction)\n .stroke(accentColor, style: StrokeStyle(lineWidth: 10, lineCap: .round))\n .rotationEffect(.degrees(-90))\n\n VStack(spacing: LumenSpacing.xs) {\n if let systemName {\n LumenIcon(systemName: systemName, size: .md, color: accentColor)\n }\n Text(valueLabel)\n .font(.headline.monospacedDigit())\n .foregroundStyle(theme.colors.ink)\n }\n }\n .frame(width: 108, height: 108)\n\n Text(label)\n .font(.caption.weight(.semibold))\n .foregroundStyle(theme.colors.inkSoft)\n }\n .accessibilityElement(children: .ignore)\n .accessibilityLabel(Text(label))\n .accessibilityValue(Text(valueLabel))\n }\n\n private var accentColor: Color {\n metricColor(tone, theme: theme)\n }\n}\n\npublic struct LumenSectionHeader<Actions: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let actions: Actions\n private let count: String?\n private let subtitle: LocalizedStringKey?\n private let title: LocalizedStringKey\n\n public init(\n _ title: LocalizedStringKey,\n subtitle: LocalizedStringKey? = nil,\n count: String? = nil,\n @ViewBuilder actions: () -> Actions\n ) {\n self.title = title\n self.subtitle = subtitle\n self.count = count\n self.actions = actions()\n }\n\n public var body: some View {\n HStack(alignment: .top, spacing: LumenSpacing.md) {\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n HStack(alignment: .firstTextBaseline, spacing: LumenSpacing.sm) {\n Text(title)\n .font(.headline)\n .foregroundStyle(theme.colors.ink)\n\n if let count {\n LumenBadge(LocalizedStringKey(count), tone: .neutral)\n .monospacedDigit()\n }\n }\n\n if let subtitle {\n Text(subtitle)\n .font(.caption)\n .foregroundStyle(theme.colors.inkMuted)\n }\n }\n .frame(maxWidth: .infinity, alignment: .leading)\n\n actions\n }\n .accessibilityElement(children: .contain)\n }\n}\n\npublic extension LumenSectionHeader where Actions == EmptyView {\n init(\n _ title: LocalizedStringKey,\n subtitle: LocalizedStringKey? = nil,\n count: String? = nil\n ) {\n self.init(title, subtitle: subtitle, count: count) {\n EmptyView()\n }\n }\n}\n\npublic struct LumenStatusBar<Trailing: View>: View {\n @Environment(\\.lumenTheme) private var theme\n\n private let message: LocalizedStringKey\n private let tone: LumenMetricTone\n private let trailing: Trailing\n\n public init(\n _ message: LocalizedStringKey,\n tone: LumenMetricTone = .neutral,\n @ViewBuilder trailing: () -> Trailing\n ) {\n self.message = message\n self.tone = tone\n self.trailing = trailing()\n }\n\n public var body: some View {\n HStack(spacing: LumenSpacing.sm) {\n Circle()\n .fill(metricColor(tone, theme: theme))\n .frame(width: 7, height: 7)\n .accessibilityHidden(true)\n\n Text(message)\n .lineLimit(1)\n\n Spacer(minLength: LumenSpacing.md)\n\n trailing\n }\n .font(.caption)\n .foregroundStyle(theme.colors.inkSoft)\n .padding(.horizontal, LumenSpacing.lg)\n .frame(minHeight: 36)\n .background(theme.colors.surfaceMuted)\n .accessibilityElement(children: .contain)\n }\n}\n\npublic extension LumenStatusBar where Trailing == EmptyView {\n init(_ message: LocalizedStringKey, tone: LumenMetricTone = .neutral) {\n self.init(message, tone: tone) {\n EmptyView()\n }\n }\n}\n\nprivate func metricColor(_ tone: LumenMetricTone, theme: LumenTheme) -> Color {\n switch tone {\n case .accent: theme.colors.accent\n case .brand: theme.colors.brand\n case .danger: theme.colors.danger\n case .neutral: theme.colors.inkMuted\n case .success: theme.colors.success\n case .warning: theme.colors.warning\n }\n}\n",
|
|
36854
|
-
"packages/swift/Sources/LumenUI/Tokens.generated.swift": "// This file is generated by scripts/generate-platform-tokens.mjs.\nimport SwiftUI\n\npublic struct LumenColorPalette: Sendable {\n public let canvas: Color\n public let surface: Color\n public let surfaceMuted: Color\n public let surfaceStrong: Color\n public let line: Color\n public let ink: Color\n public let inkSoft: Color\n public let inkMuted: Color\n public let brand: Color\n public let brandSolid: Color\n public let brandSoft: Color\n public let onBrand: Color\n public let accent: Color\n public let success: Color\n public let warning: Color\n public let danger: Color\n public let onDanger: Color\n}\n\npublic enum LumenColors {\n public static let light = LumenColorPalette(\n canvas: Color(red: 1.000000, green: 1.000000, blue: 1.000000),\n surface: Color(red: 1.000000, green: 1.000000, blue: 1.000000),\n surfaceMuted: Color(red: 0.952941, green: 0.956863, blue: 0.964706),\n surfaceStrong: Color(red: 0.898039, green: 0.905882, blue: 0.921569),\n line: Color(red: 0.843137, green: 0.854902, blue: 0.878431),\n ink: Color(red: 0.058824, green: 0.090196, blue: 0.160784),\n inkSoft: Color(red: 0.270588, green: 0.309804, blue: 0.372549),\n inkMuted: Color(red: 0.423529, green: 0.470588, blue: 0.537255),\n brand: Color(red: 0.141176, green: 0.388235, blue: 0.921569),\n brandSolid: Color(red: 0.078431, green: 0.321569, blue: 0.843137),\n brandSoft: Color(red: 0.941176, green: 0.960784, blue: 1.000000),\n onBrand: Color(red: 1.000000, green: 1.000000, blue: 1.000000),\n accent: Color(red: 0.086275, green: 0.635294, blue: 0.525490),\n success: Color(red: 0.105882, green: 0.615686, blue: 0.290196),\n warning: Color(red: 0.960784, green: 0.623529, blue: 0.039216),\n danger: Color(red: 0.937255, green: 0.262745, blue: 0.262745),\n onDanger: Color(red: 0.000000, green: 0.000000, blue: 0.000000)\n )\n\n public static let dark = LumenColorPalette(\n canvas: Color(red: 0.062745, green: 0.070588, blue: 0.098039),\n surface: Color(red: 0.090196, green: 0.101961, blue: 0.129412),\n surfaceMuted: Color(red: 0.125490, green: 0.141176, blue: 0.172549),\n surfaceStrong: Color(red: 0.188235, green: 0.207843, blue: 0.250980),\n line: Color(red: 0.223529, green: 0.247059, blue: 0.298039),\n ink: Color(red: 0.952941, green: 0.960784, blue: 0.968627),\n inkSoft: Color(red: 0.737255, green: 0.764706, blue: 0.803922),\n inkMuted: Color(red: 0.580392, green: 0.611765, blue: 0.658824),\n brand: Color(red: 0.160784, green: 0.705882, blue: 0.960784),\n brandSolid: Color(red: 0.031373, green: 0.478431, blue: 0.686275),\n brandSoft: Color(red: 0.027451, green: 0.207843, blue: 0.294118),\n onBrand: Color(red: 1.000000, green: 1.000000, blue: 1.000000),\n accent: Color(red: 0.101961, green: 0.756863, blue: 0.603922),\n success: Color(red: 0.141176, green: 0.760784, blue: 0.368627),\n warning: Color(red: 0.964706, green: 0.658824, blue: 0.137255),\n danger: Color(red: 0.945098, green: 0.356863, blue: 0.356863),\n onDanger: Color(red: 0.062745, green: 0.070588, blue: 0.098039)\n )\n}\n\npublic enum LumenSpacing {\n public static let zero: CGFloat = 0\n public static let xs: CGFloat = 4\n public static let sm: CGFloat = 8\n public static let md: CGFloat = 12\n public static let lg: CGFloat = 16\n public static let xl: CGFloat = 24\n public static let size2xl: CGFloat = 32\n public static let size3xl: CGFloat = 48\n}\n\npublic enum LumenRadius {\n public static let sm: CGFloat = 6\n public static let md: CGFloat = 10\n public static let lg: CGFloat = 12\n public static let full: CGFloat = 9999\n}\n\npublic enum LumenTypography {\n public static let xsSize: CGFloat = 12\n public static let smSize: CGFloat = 14\n public static let mdSize: CGFloat = 16\n public static let lgSize: CGFloat = 18\n public static let xlSize: CGFloat = 20\n public static let size2xlSize: CGFloat = 24\n public static let regularWeight: Font.Weight = .regular\n public static let mediumWeight: Font.Weight = .medium\n public static let semiboldWeight: Font.Weight = .semibold\n public static let boldWeight: Font.Weight = .bold\n}\n\npublic struct LumenCubicBezier: Equatable, Sendable {\n public let x1: Double\n public let y1: Double\n public let x2: Double\n public let y2: Double\n\n public init(x1: Double, y1: Double, x2: Double, y2: Double) {\n self.x1 = x1\n self.y1 = y1\n self.x2 = x2\n self.y2 = y2\n }\n}\n\npublic enum LumenMotion {\n public static let fastDuration: Double = 0.12\n public static let standardDuration: Double = 0.16\n public static let slowDuration: Double = 0.30\n public static let standardEasing = LumenCubicBezier(x1: 0.32, y1: 0.72, x2: 0, y2: 1)\n public static let emphasizedEasing = LumenCubicBezier(x1: 0.22, y1: 1, x2: 0.36, y2: 1)\n}\n\npublic enum LumenElevation {\n public static let resting: CGFloat = 1\n public static let raised: CGFloat = 3\n public static let overlay: CGFloat = 6\n}\n"
|
|
38801
|
+
"packages/swift/Sources/LumenUI/Tokens.generated.swift": "// This file is generated by scripts/generate-platform-tokens.mjs.\nimport SwiftUI\n\npublic struct LumenColorPalette: Sendable {\n public let canvas: Color\n public let surface: Color\n public let surfaceMuted: Color\n public let surfaceStrong: Color\n public let line: Color\n public let ink: Color\n public let inkSoft: Color\n public let inkMuted: Color\n public let brand: Color\n public let brandSolid: Color\n public let brandSoft: Color\n public let onBrand: Color\n public let accent: Color\n public let success: Color\n public let warning: Color\n public let danger: Color\n public let onDanger: Color\n\n public init(\n canvas: Color,\n surface: Color,\n surfaceMuted: Color,\n surfaceStrong: Color,\n line: Color,\n ink: Color,\n inkSoft: Color,\n inkMuted: Color,\n brand: Color,\n brandSolid: Color,\n brandSoft: Color,\n onBrand: Color,\n accent: Color,\n success: Color,\n warning: Color,\n danger: Color,\n onDanger: Color\n ) {\n self.canvas = canvas\n self.surface = surface\n self.surfaceMuted = surfaceMuted\n self.surfaceStrong = surfaceStrong\n self.line = line\n self.ink = ink\n self.inkSoft = inkSoft\n self.inkMuted = inkMuted\n self.brand = brand\n self.brandSolid = brandSolid\n self.brandSoft = brandSoft\n self.onBrand = onBrand\n self.accent = accent\n self.success = success\n self.warning = warning\n self.danger = danger\n self.onDanger = onDanger\n }\n}\n\npublic enum LumenColors {\n public static let light = LumenColorPalette(\n canvas: Color(red: 1.000000, green: 1.000000, blue: 1.000000),\n surface: Color(red: 1.000000, green: 1.000000, blue: 1.000000),\n surfaceMuted: Color(red: 0.952941, green: 0.956863, blue: 0.964706),\n surfaceStrong: Color(red: 0.898039, green: 0.905882, blue: 0.921569),\n line: Color(red: 0.843137, green: 0.854902, blue: 0.878431),\n ink: Color(red: 0.058824, green: 0.090196, blue: 0.160784),\n inkSoft: Color(red: 0.270588, green: 0.309804, blue: 0.372549),\n inkMuted: Color(red: 0.423529, green: 0.470588, blue: 0.537255),\n brand: Color(red: 0.141176, green: 0.388235, blue: 0.921569),\n brandSolid: Color(red: 0.078431, green: 0.321569, blue: 0.843137),\n brandSoft: Color(red: 0.941176, green: 0.960784, blue: 1.000000),\n onBrand: Color(red: 1.000000, green: 1.000000, blue: 1.000000),\n accent: Color(red: 0.086275, green: 0.635294, blue: 0.525490),\n success: Color(red: 0.105882, green: 0.615686, blue: 0.290196),\n warning: Color(red: 0.960784, green: 0.623529, blue: 0.039216),\n danger: Color(red: 0.937255, green: 0.262745, blue: 0.262745),\n onDanger: Color(red: 0.000000, green: 0.000000, blue: 0.000000)\n )\n\n public static let dark = LumenColorPalette(\n canvas: Color(red: 0.062745, green: 0.070588, blue: 0.098039),\n surface: Color(red: 0.090196, green: 0.101961, blue: 0.129412),\n surfaceMuted: Color(red: 0.125490, green: 0.141176, blue: 0.172549),\n surfaceStrong: Color(red: 0.188235, green: 0.207843, blue: 0.250980),\n line: Color(red: 0.223529, green: 0.247059, blue: 0.298039),\n ink: Color(red: 0.952941, green: 0.960784, blue: 0.968627),\n inkSoft: Color(red: 0.737255, green: 0.764706, blue: 0.803922),\n inkMuted: Color(red: 0.580392, green: 0.611765, blue: 0.658824),\n brand: Color(red: 0.160784, green: 0.705882, blue: 0.960784),\n brandSolid: Color(red: 0.031373, green: 0.478431, blue: 0.686275),\n brandSoft: Color(red: 0.027451, green: 0.207843, blue: 0.294118),\n onBrand: Color(red: 1.000000, green: 1.000000, blue: 1.000000),\n accent: Color(red: 0.101961, green: 0.756863, blue: 0.603922),\n success: Color(red: 0.141176, green: 0.760784, blue: 0.368627),\n warning: Color(red: 0.964706, green: 0.658824, blue: 0.137255),\n danger: Color(red: 0.945098, green: 0.356863, blue: 0.356863),\n onDanger: Color(red: 0.062745, green: 0.070588, blue: 0.098039)\n )\n}\n\npublic enum LumenSpacing {\n public static let zero: CGFloat = 0\n public static let xs: CGFloat = 4\n public static let sm: CGFloat = 8\n public static let md: CGFloat = 12\n public static let lg: CGFloat = 16\n public static let xl: CGFloat = 24\n public static let size2xl: CGFloat = 32\n public static let size3xl: CGFloat = 48\n}\n\npublic enum LumenRadius {\n public static let sm: CGFloat = 6\n public static let md: CGFloat = 10\n public static let lg: CGFloat = 12\n public static let full: CGFloat = 9999\n}\n\npublic enum LumenTypography {\n public static let xsSize: CGFloat = 12\n public static let smSize: CGFloat = 14\n public static let mdSize: CGFloat = 16\n public static let lgSize: CGFloat = 18\n public static let xlSize: CGFloat = 20\n public static let size2xlSize: CGFloat = 24\n public static let regularWeight: Font.Weight = .regular\n public static let mediumWeight: Font.Weight = .medium\n public static let semiboldWeight: Font.Weight = .semibold\n public static let boldWeight: Font.Weight = .bold\n}\n\npublic struct LumenCubicBezier: Equatable, Sendable {\n public let x1: Double\n public let y1: Double\n public let x2: Double\n public let y2: Double\n\n public init(x1: Double, y1: Double, x2: Double, y2: Double) {\n self.x1 = x1\n self.y1 = y1\n self.x2 = x2\n self.y2 = y2\n }\n}\n\npublic enum LumenMotion {\n public static let fastDuration: Double = 0.12\n public static let standardDuration: Double = 0.16\n public static let slowDuration: Double = 0.30\n public static let standardEasing = LumenCubicBezier(x1: 0.32, y1: 0.72, x2: 0, y2: 1)\n public static let emphasizedEasing = LumenCubicBezier(x1: 0.22, y1: 1, x2: 0.36, y2: 1)\n}\n\npublic enum LumenElevation {\n public static let resting: CGFloat = 1\n public static let raised: CGFloat = 3\n public static let overlay: CGFloat = 6\n}\n"
|
|
36855
38802
|
}
|
|
36856
38803
|
},
|
|
36857
38804
|
"recipes": [
|