@rip-lang/ui 0.3.67 → 0.4.1

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.
Files changed (83) hide show
  1. package/AGENTS.md +93 -0
  2. package/README.md +22 -625
  3. package/browser/AGENTS.md +213 -0
  4. package/browser/CONTRIBUTING.md +375 -0
  5. package/browser/README.md +11 -0
  6. package/browser/TESTING.md +59 -0
  7. package/browser/browser.rip +56 -0
  8. package/{components → browser/components}/accordion.rip +1 -1
  9. package/{components → browser/components}/alert-dialog.rip +6 -3
  10. package/{components → browser/components}/autocomplete.rip +27 -21
  11. package/{components → browser/components}/avatar.rip +3 -3
  12. package/{components → browser/components}/badge.rip +1 -1
  13. package/{components → browser/components}/breadcrumb.rip +2 -2
  14. package/{components → browser/components}/button-group.rip +3 -3
  15. package/{components → browser/components}/button.rip +2 -2
  16. package/{components → browser/components}/card.rip +1 -1
  17. package/{components → browser/components}/carousel.rip +5 -5
  18. package/{components → browser/components}/checkbox-group.rip +40 -11
  19. package/{components → browser/components}/checkbox.rip +4 -4
  20. package/{components → browser/components}/collapsible.rip +2 -2
  21. package/{components → browser/components}/combobox.rip +36 -23
  22. package/{components → browser/components}/context-menu.rip +1 -1
  23. package/{components → browser/components}/date-picker.rip +5 -5
  24. package/{components → browser/components}/dialog.rip +8 -4
  25. package/{components → browser/components}/drawer.rip +8 -4
  26. package/{components → browser/components}/editable-value.rip +7 -1
  27. package/{components → browser/components}/field.rip +5 -5
  28. package/{components → browser/components}/fieldset.rip +2 -2
  29. package/{components → browser/components}/form.rip +1 -1
  30. package/{components → browser/components}/grid.rip +8 -8
  31. package/{components → browser/components}/input-group.rip +1 -1
  32. package/{components → browser/components}/input.rip +6 -6
  33. package/{components → browser/components}/label.rip +2 -2
  34. package/{components → browser/components}/menu.rip +17 -10
  35. package/{components → browser/components}/menubar.rip +1 -1
  36. package/{components → browser/components}/meter.rip +7 -7
  37. package/{components → browser/components}/multi-select.rip +76 -33
  38. package/{components → browser/components}/native-select.rip +3 -3
  39. package/{components → browser/components}/nav-menu.rip +3 -3
  40. package/{components → browser/components}/number-field.rip +11 -11
  41. package/{components → browser/components}/otp-field.rip +4 -4
  42. package/{components → browser/components}/pagination.rip +4 -4
  43. package/{components → browser/components}/popover.rip +11 -24
  44. package/{components → browser/components}/preview-card.rip +7 -11
  45. package/{components → browser/components}/progress.rip +3 -3
  46. package/{components → browser/components}/radio-group.rip +4 -4
  47. package/{components → browser/components}/resizable.rip +3 -3
  48. package/{components → browser/components}/scroll-area.rip +1 -1
  49. package/{components → browser/components}/select.rip +55 -27
  50. package/{components → browser/components}/separator.rip +2 -2
  51. package/{components → browser/components}/skeleton.rip +4 -4
  52. package/{components → browser/components}/slider.rip +15 -10
  53. package/{components → browser/components}/spinner.rip +2 -2
  54. package/{components → browser/components}/table.rip +2 -2
  55. package/{components → browser/components}/tabs.rip +12 -7
  56. package/{components → browser/components}/textarea.rip +8 -8
  57. package/{components → browser/components}/toast.rip +3 -3
  58. package/{components → browser/components}/toggle-group.rip +42 -11
  59. package/{components → browser/components}/toggle.rip +2 -2
  60. package/{components → browser/components}/toolbar.rip +2 -2
  61. package/{components → browser/components}/tooltip.rip +19 -23
  62. package/browser/hljs-rip.js +209 -0
  63. package/browser/playwright.config.mjs +31 -0
  64. package/browser/tests/overlays.js +349 -0
  65. package/email/AGENTS.md +16 -0
  66. package/email/README.md +55 -0
  67. package/email/benchmarks/benchmark.rip +94 -0
  68. package/email/benchmarks/samples.rip +104 -0
  69. package/email/compat.rip +129 -0
  70. package/email/components.rip +371 -0
  71. package/email/dom.rip +330 -0
  72. package/email/email.rip +10 -0
  73. package/email/render.rip +82 -0
  74. package/package.json +29 -39
  75. package/shared/README.md +3 -0
  76. package/shared/styles.rip +17 -0
  77. package/tailwind/AGENTS.md +3 -0
  78. package/tailwind/README.md +27 -0
  79. package/tailwind/engine.js +107 -0
  80. package/tailwind/inline.js +215 -0
  81. package/tailwind/serve.js +6 -0
  82. package/tailwind/tailwind.rip +13 -0
  83. package/ui.rip +3 -0
@@ -0,0 +1,215 @@
1
+ import { compile } from './engine.js'
2
+ import { generate, walk } from 'css-tree'
3
+
4
+ const tailwindRoots = []
5
+
6
+ function isFragmentNode(node) {
7
+ return node && Array.isArray(node.childNodes) && !node.tag && !node.attributes
8
+ }
9
+
10
+ function isElementNode(node) {
11
+ return node && node.tag && node.attributes && node.style
12
+ }
13
+
14
+ function collectClasses(node, out = new Set()) {
15
+ if (isFragmentNode(node) || isElementNode(node)) {
16
+ if (isElementNode(node) && node.className) {
17
+ for (const cls of node.className.split(/\s+/)) {
18
+ if (cls) out.add(cls)
19
+ }
20
+ }
21
+ for (const child of node.childNodes) collectClasses(child, out)
22
+ }
23
+ return out
24
+ }
25
+
26
+ function getCustomProperties(styleSheet) {
27
+ const customProperties = new Map()
28
+ walk(styleSheet, {
29
+ visit: 'Atrule',
30
+ enter(atrule) {
31
+ if (atrule.name !== 'property' || !atrule.prelude) return
32
+ const name = generate(atrule.prelude)
33
+ if (!name.startsWith('--')) return
34
+ let initialValue
35
+ walk(atrule, {
36
+ visit: 'Declaration',
37
+ enter(decl) {
38
+ if (decl.property === 'initial-value') initialValue = decl
39
+ },
40
+ })
41
+ customProperties.set(name, { initialValue })
42
+ },
43
+ })
44
+ return customProperties
45
+ }
46
+
47
+ function extractRuleMaps(styleSheet, classes) {
48
+ const classSet = new Set(classes)
49
+ const inlineable = new Map()
50
+ const supported = new Set()
51
+ const residual = []
52
+
53
+ walk(styleSheet, {
54
+ visit: 'Rule',
55
+ enter(rule, _item, list) {
56
+ const selectorClasses = []
57
+ walk(rule, {
58
+ visit: 'ClassSelector',
59
+ enter(classSelector) {
60
+ selectorClasses.push(classSelector.name.replace(/\\/g, ''))
61
+ },
62
+ })
63
+ const matched = selectorClasses.filter((cls) => classSet.has(cls))
64
+ if (matched.length === 0) return
65
+ matched.forEach((cls) => supported.add(cls))
66
+
67
+ const selectorText = generate(rule.prelude).trim()
68
+ const inlineableRule = selectorText
69
+ .split(',')
70
+ .map((s) => s.trim())
71
+ .every((s) => s.startsWith('.') && !/[\s>+~:#\[]/.test(s.slice(1)))
72
+
73
+ if (inlineableRule) {
74
+ matched.forEach((cls) => {
75
+ inlineable.set(cls, rule)
76
+ })
77
+ } else {
78
+ residual.push(rule)
79
+ }
80
+ },
81
+ })
82
+
83
+ walk(styleSheet, {
84
+ visit: 'Atrule',
85
+ enter(atrule) {
86
+ if (atrule.name === 'theme') residual.push(atrule)
87
+ if (atrule.name === 'layer' && generate(atrule.prelude).includes('base')) residual.push(atrule)
88
+ if (atrule.name === 'media') residual.push(atrule)
89
+ },
90
+ })
91
+
92
+ return { inlineable, supported, residual }
93
+ }
94
+
95
+ function makeInlineStylesFor(rules, customProperties) {
96
+ const styles = {}
97
+ const locals = new Map()
98
+
99
+ for (const rule of rules) {
100
+ walk(rule, {
101
+ visit: 'Declaration',
102
+ enter(decl) {
103
+ if (decl.property.startsWith('--')) locals.set(decl.property, decl)
104
+ },
105
+ })
106
+ }
107
+
108
+ for (const rule of rules) {
109
+ walk(rule, {
110
+ visit: 'Function',
111
+ enter(fn, item) {
112
+ if (fn.name !== 'var') return
113
+ let variableName
114
+ walk(fn, {
115
+ visit: 'Identifier',
116
+ enter(identifier) {
117
+ variableName = identifier.name
118
+ return this.break
119
+ },
120
+ })
121
+ if (!variableName) return
122
+ const local = locals.get(variableName)
123
+ if (local) {
124
+ item.data = local.value
125
+ return
126
+ }
127
+ const custom = customProperties.get(variableName)
128
+ if (custom?.initialValue) item.data = custom.initialValue.value
129
+ },
130
+ })
131
+
132
+ walk(rule, {
133
+ visit: 'Declaration',
134
+ enter(decl) {
135
+ if (decl.property.startsWith('--')) return
136
+ const camel = decl.property.replace(/-([a-z])/g, (_, c) => c.toUpperCase())
137
+ styles[camel] = generate(decl.value) + (decl.important ? '!important' : '')
138
+ },
139
+ })
140
+ }
141
+
142
+ return styles
143
+ }
144
+
145
+ function applyInlineTree(node, inlineable, customProperties, unsupported = []) {
146
+ if (isFragmentNode(node)) {
147
+ for (const child of node.childNodes) applyInlineTree(child, inlineable, customProperties, unsupported)
148
+ return unsupported
149
+ }
150
+ if (!isElementNode(node)) return unsupported
151
+
152
+ if (node.className) {
153
+ const residualClasses = []
154
+ for (const cls of node.className.split(/\s+/)) {
155
+ if (!cls) continue
156
+ const rule = inlineable.get(cls)
157
+ if (rule) {
158
+ Object.assign(node.style, makeInlineStylesFor([rule], customProperties))
159
+ } else {
160
+ residualClasses.push(cls)
161
+ if (!unsupported.includes(cls)) unsupported.push(cls)
162
+ }
163
+ }
164
+ if (residualClasses.length) node.className = residualClasses.join(' ')
165
+ else node.removeAttribute('class')
166
+ }
167
+
168
+ for (const child of node.childNodes) applyInlineTree(child, inlineable, customProperties, unsupported)
169
+ return unsupported
170
+ }
171
+
172
+ function findFirstTag(node, tag) {
173
+ if (isElementNode(node) && node.tag === tag) return node
174
+ if (isElementNode(node) || isFragmentNode(node)) {
175
+ for (const child of node.childNodes) {
176
+ const found = findFirstTag(child, tag)
177
+ if (found) return found
178
+ }
179
+ }
180
+ return null
181
+ }
182
+
183
+ export function inlineEmailTree(rootNode, config = {}) {
184
+ const classes = [...collectClasses(rootNode)]
185
+ if (classes.length === 0) return { unsupported: [], headCss: '' }
186
+
187
+ const { css, styleSheet } = compile(classes, config)
188
+ const customProperties = getCustomProperties(styleSheet)
189
+ const { inlineable, supported, residual } = extractRuleMaps(styleSheet, classes)
190
+ const unsupported = applyInlineTree(rootNode, inlineable, customProperties, [])
191
+ const headCss = residual.map((node) => generate(node)).join('\n')
192
+
193
+ if (headCss) {
194
+ const head = findFirstTag(rootNode, 'head')
195
+ if (head) {
196
+ const styleNode = new head.constructor('style')
197
+ styleNode.innerHTML = headCss
198
+ head.appendChild(styleNode)
199
+ }
200
+ }
201
+
202
+ const trulyUnsupported = unsupported.filter((cls) => !supported.has(cls))
203
+ return { unsupported: trulyUnsupported, headCss }
204
+ }
205
+
206
+ export function registerEmailTailwindRoot(component, config) {
207
+ const root = component?._root ?? component
208
+ if (root) tailwindRoots.push({ root, config })
209
+ }
210
+
211
+ export function takeEmailTailwindRoots() {
212
+ const roots = tailwindRoots.slice()
213
+ tailwindRoots.length = 0
214
+ return roots
215
+ }
@@ -0,0 +1,6 @@
1
+ import { compile } from './engine.js'
2
+
3
+ export function generateBrowserCss(classes = [], config = {}) {
4
+ const { css } = compile(classes, config)
5
+ return css
6
+ }
@@ -0,0 +1,13 @@
1
+ # Tailwind domain barrel for @rip-lang/ui/tailwind
2
+
3
+ import * as engine from './engine.js'
4
+ import * as inline from './inline.js'
5
+ import * as serve from './serve.js'
6
+
7
+ export compile = engine.compile
8
+ export prepareConfig = (config = {}) -> engine.prepareConfig(config)
9
+ export configKey = (config = {}) -> engine.configCacheKey(config)
10
+ export inlineEmailTree = inline.inlineEmailTree
11
+ export registerEmailTailwindRoot = inline.registerEmailTailwindRoot
12
+ export takeEmailTailwindRoots = inline.takeEmailTailwindRoots
13
+ export generateBrowserCss = serve.generateBrowserCss
package/ui.rip ADDED
@@ -0,0 +1,3 @@
1
+ # @rip-lang/ui umbrella barrel
2
+
3
+ export UI_VERSION =! '0.4.0'