@rip-lang/ui 0.3.67 → 0.4.2
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/AGENTS.md +93 -0
- package/README.md +22 -625
- package/browser/AGENTS.md +213 -0
- package/browser/CONTRIBUTING.md +375 -0
- package/browser/README.md +11 -0
- package/browser/TESTING.md +59 -0
- package/browser/browser.rip +56 -0
- package/{components → browser/components}/accordion.rip +1 -1
- package/{components → browser/components}/alert-dialog.rip +6 -3
- package/{components → browser/components}/autocomplete.rip +27 -21
- package/{components → browser/components}/avatar.rip +3 -3
- package/{components → browser/components}/badge.rip +1 -1
- package/{components → browser/components}/breadcrumb.rip +2 -2
- package/{components → browser/components}/button-group.rip +3 -3
- package/{components → browser/components}/button.rip +2 -2
- package/{components → browser/components}/card.rip +1 -1
- package/{components → browser/components}/carousel.rip +5 -5
- package/{components → browser/components}/checkbox-group.rip +40 -11
- package/{components → browser/components}/checkbox.rip +4 -4
- package/{components → browser/components}/collapsible.rip +2 -2
- package/{components → browser/components}/combobox.rip +36 -23
- package/{components → browser/components}/context-menu.rip +1 -1
- package/{components → browser/components}/date-picker.rip +5 -5
- package/{components → browser/components}/dialog.rip +8 -4
- package/{components → browser/components}/drawer.rip +8 -4
- package/{components → browser/components}/editable-value.rip +7 -1
- package/{components → browser/components}/field.rip +5 -5
- package/{components → browser/components}/fieldset.rip +2 -2
- package/{components → browser/components}/form.rip +1 -1
- package/{components → browser/components}/grid.rip +8 -8
- package/{components → browser/components}/input-group.rip +1 -1
- package/{components → browser/components}/input.rip +6 -6
- package/{components → browser/components}/label.rip +2 -2
- package/{components → browser/components}/menu.rip +17 -10
- package/{components → browser/components}/menubar.rip +1 -1
- package/{components → browser/components}/meter.rip +7 -7
- package/{components → browser/components}/multi-select.rip +76 -33
- package/{components → browser/components}/native-select.rip +3 -3
- package/{components → browser/components}/nav-menu.rip +3 -3
- package/{components → browser/components}/number-field.rip +11 -11
- package/{components → browser/components}/otp-field.rip +4 -4
- package/{components → browser/components}/pagination.rip +4 -4
- package/{components → browser/components}/popover.rip +11 -24
- package/{components → browser/components}/preview-card.rip +7 -11
- package/{components → browser/components}/progress.rip +3 -3
- package/{components → browser/components}/radio-group.rip +4 -4
- package/{components → browser/components}/resizable.rip +3 -3
- package/{components → browser/components}/scroll-area.rip +1 -1
- package/{components → browser/components}/select.rip +55 -27
- package/{components → browser/components}/separator.rip +2 -2
- package/{components → browser/components}/skeleton.rip +4 -4
- package/{components → browser/components}/slider.rip +15 -10
- package/{components → browser/components}/spinner.rip +2 -2
- package/{components → browser/components}/table.rip +2 -2
- package/{components → browser/components}/tabs.rip +12 -7
- package/{components → browser/components}/textarea.rip +8 -8
- package/{components → browser/components}/toast.rip +3 -3
- package/{components → browser/components}/toggle-group.rip +42 -11
- package/{components → browser/components}/toggle.rip +2 -2
- package/{components → browser/components}/toolbar.rip +2 -2
- package/{components → browser/components}/tooltip.rip +19 -23
- package/browser/hljs-rip.js +209 -0
- package/browser/playwright.config.mjs +31 -0
- package/browser/tests/overlays.js +352 -0
- package/email/AGENTS.md +16 -0
- package/email/README.md +55 -0
- package/email/benchmarks/benchmark.rip +94 -0
- package/email/benchmarks/samples.rip +104 -0
- package/email/compat.rip +129 -0
- package/email/components.rip +371 -0
- package/email/dom.rip +330 -0
- package/email/email.rip +10 -0
- package/email/render.rip +82 -0
- package/package.json +29 -39
- package/shared/README.md +3 -0
- package/shared/styles.rip +17 -0
- package/tailwind/AGENTS.md +3 -0
- package/tailwind/README.md +27 -0
- package/tailwind/engine.js +107 -0
- package/tailwind/inline.js +215 -0
- package/tailwind/serve.js +6 -0
- package/tailwind/tailwind.rip +13 -0
- 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,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