cellery 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/lib/cells.js +86 -21
- package/lib/compat.js +18 -0
- package/lib/template.js +40 -8
- package/package.json +4 -3
package/lib/cells.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { parse, walk, generate } = require('css-tree/dist/csstree.esm')
|
|
2
|
+
|
|
1
3
|
class Cell {
|
|
2
4
|
constructor(opts = {}) {
|
|
3
5
|
this.id = opts.id
|
|
@@ -9,7 +11,10 @@ class Cell {
|
|
|
9
11
|
this.alignment = opts.alignment
|
|
10
12
|
this.decoration = opts.decoration
|
|
11
13
|
this.size = opts.size
|
|
12
|
-
this.
|
|
14
|
+
this.events = opts.events
|
|
15
|
+
this.style = opts.style
|
|
16
|
+
|
|
17
|
+
this._eventsRegistered = false
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
static Styled(styledOpts = {}) {
|
|
@@ -36,14 +41,21 @@ class Cell {
|
|
|
36
41
|
|
|
37
42
|
render(opts = {}) {
|
|
38
43
|
const cell = this._render()
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
|
|
45
|
+
if (!this.cellery) {
|
|
46
|
+
this.cellery = Cell.cellery
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// @todo explore - 'ready' style first time setup?
|
|
50
|
+
if (this.events?.length && this.id && !this._eventsRegistered) {
|
|
51
|
+
this._eventsRegistered = true
|
|
52
|
+
this.cellery.pub({ event: 'register', id: this.id, targets: this.events })
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
this.cellery.pub({
|
|
44
56
|
event: 'render',
|
|
45
57
|
id: cell.id,
|
|
46
|
-
content: this.cellery.adapter
|
|
58
|
+
content: this.cellery.adapter?.render(cell),
|
|
47
59
|
...opts
|
|
48
60
|
})
|
|
49
61
|
}
|
|
@@ -56,11 +68,68 @@ class Cell {
|
|
|
56
68
|
})
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
// @deprecated
|
|
72
|
+
register(cellery) {}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class Style {
|
|
76
|
+
constructor(opts = {}) {
|
|
77
|
+
this.content = parse(opts.children?.join('\n'))
|
|
78
|
+
|
|
79
|
+
// setup data-cellery-cell for namespaced matching
|
|
80
|
+
const cellNames = new Set(Object.keys(opts.cells))
|
|
81
|
+
|
|
82
|
+
walk(this.content, {
|
|
83
|
+
visit: 'TypeSelector',
|
|
84
|
+
enter(node) {
|
|
85
|
+
if (cellNames.has(node.name)) {
|
|
86
|
+
node.name = `[data-cellery-cell="${node.name}"]`
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
findPropertyOfCell(name, property) {
|
|
93
|
+
return this.findProperty(`[data-cellery-cell="${name}"]`, property)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
findProperty(selector, property) {
|
|
97
|
+
let value = null
|
|
98
|
+
|
|
99
|
+
walk(this.content, {
|
|
100
|
+
visit: 'Rule',
|
|
101
|
+
enter(rule) {
|
|
102
|
+
const prelude = generate(rule.prelude)
|
|
103
|
+
if (prelude !== selector) return
|
|
104
|
+
|
|
105
|
+
rule.block.children.forEach((decl) => {
|
|
106
|
+
if (decl.type === 'Declaration' && decl.property === property) {
|
|
107
|
+
value = generate(decl.value)
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
return value
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
addScope(target, parent) {
|
|
117
|
+
walk(this.content, {
|
|
118
|
+
visit: 'Rule',
|
|
119
|
+
enter(rule) {
|
|
120
|
+
rule.prelude.children.forEach((selector) => {
|
|
121
|
+
const str = generate(selector)
|
|
122
|
+
if (!str.startsWith(target)) return
|
|
123
|
+
|
|
124
|
+
selector.children.prependData({ type: 'Combinator', name: ' ' })
|
|
125
|
+
selector.children.prependData({ type: 'IdSelector', name: parent })
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
toCSS() {
|
|
132
|
+
return generate(this.content)
|
|
64
133
|
}
|
|
65
134
|
}
|
|
66
135
|
|
|
@@ -80,22 +149,18 @@ class Container extends Cell {
|
|
|
80
149
|
}
|
|
81
150
|
}
|
|
82
151
|
|
|
83
|
-
class
|
|
152
|
+
class Fragment extends Cell {
|
|
84
153
|
constructor(opts = {}) {
|
|
85
|
-
super({ ...opts
|
|
154
|
+
super({ ...opts })
|
|
86
155
|
}
|
|
87
156
|
}
|
|
88
157
|
|
|
89
158
|
class Text extends Cell {
|
|
90
159
|
constructor(opts = {}) {
|
|
91
160
|
super(opts)
|
|
92
|
-
this.value = opts.value || ''
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
class Paragraph extends Cell {
|
|
97
|
-
constructor(opts = {}) {
|
|
98
|
-
super(opts)
|
|
161
|
+
this.value = opts.value || this.children.filter((c) => typeof c === 'string').join('') || ''
|
|
162
|
+
this.paragraph = !!opts.paragraph
|
|
163
|
+
this.heading = opts.heading
|
|
99
164
|
}
|
|
100
165
|
}
|
|
101
166
|
|
|
@@ -111,8 +176,8 @@ class Input extends Cell {
|
|
|
111
176
|
module.exports = {
|
|
112
177
|
Cell,
|
|
113
178
|
Container,
|
|
114
|
-
|
|
179
|
+
Fragment,
|
|
115
180
|
Text,
|
|
116
|
-
|
|
117
|
-
|
|
181
|
+
Input,
|
|
182
|
+
Style
|
|
118
183
|
}
|
package/lib/compat.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { Text, Input, Container, Fragment, Style } = require('./cells')
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
'': Fragment,
|
|
5
|
+
h1: Text.Styled({ heading: 1 }),
|
|
6
|
+
h2: Text.Styled({ heading: 2 }),
|
|
7
|
+
h3: Text.Styled({ heading: 3 }),
|
|
8
|
+
h4: Text.Styled({ heading: 4 }),
|
|
9
|
+
h5: Text.Styled({ heading: 5 }),
|
|
10
|
+
h6: Text.Styled({ heading: 6 }),
|
|
11
|
+
div: Container,
|
|
12
|
+
input: Input.Styled({ multiline: false }),
|
|
13
|
+
textbox: Input.Styled({ multiline: true }),
|
|
14
|
+
span: Text,
|
|
15
|
+
button: Text.Styled({ events: ['click'] }),
|
|
16
|
+
p: Text.Styled({ paragraph: true }),
|
|
17
|
+
style: Style
|
|
18
|
+
}
|
package/lib/template.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
const { Cell, Container,
|
|
1
|
+
const { Cell, Container, Fragment, Text, Paragraph, Input, Style } = require('./cells')
|
|
2
|
+
const compat = require('./compat')
|
|
2
3
|
|
|
3
4
|
const SENTINEL = '\x00'
|
|
4
5
|
|
|
5
|
-
const registry = {
|
|
6
|
+
const registry = {
|
|
7
|
+
...compat,
|
|
8
|
+
Cell,
|
|
9
|
+
Container,
|
|
10
|
+
Fragment,
|
|
11
|
+
Text,
|
|
12
|
+
Paragraph,
|
|
13
|
+
Input,
|
|
14
|
+
Style
|
|
15
|
+
}
|
|
6
16
|
|
|
7
17
|
function register(cells) {
|
|
8
18
|
Object.assign(registry, cells)
|
|
@@ -44,10 +54,18 @@ function parse(input, slots) {
|
|
|
44
54
|
while (!eof() && /\s/.test(peek())) advance()
|
|
45
55
|
}
|
|
46
56
|
|
|
57
|
+
const SLOT_RE = new RegExp(SENTINEL + '(\\d+)' + SENTINEL, 'g')
|
|
58
|
+
const SLOT_PURE = new RegExp('^' + SENTINEL + '(\\d+)' + SENTINEL + '$')
|
|
59
|
+
|
|
47
60
|
function resolveValue(str) {
|
|
48
61
|
str = str.trim()
|
|
49
|
-
|
|
62
|
+
// Pure placeholder — preserve type
|
|
63
|
+
const m = str.match(SLOT_PURE)
|
|
50
64
|
if (m) return slots[parseInt(m[1])]
|
|
65
|
+
// Mixed content — inline replace as strings
|
|
66
|
+
if (str.includes(SENTINEL)) {
|
|
67
|
+
return str.replace(SLOT_RE, (_, id) => String(slots[parseInt(id)]))
|
|
68
|
+
}
|
|
51
69
|
if (str === 'true') return true
|
|
52
70
|
if (str === 'false') return false
|
|
53
71
|
if (str !== '' && !isNaN(str)) return Number(str)
|
|
@@ -82,7 +100,7 @@ function parse(input, slots) {
|
|
|
82
100
|
advance() // <
|
|
83
101
|
|
|
84
102
|
let name = ''
|
|
85
|
-
while (!eof() && /[a-zA-Z0-9_]/.test(peek())) name += advance()
|
|
103
|
+
while (!eof() && /[a-zA-Z0-9_.]/.test(peek())) name += advance()
|
|
86
104
|
|
|
87
105
|
const attrs = parseAttrs()
|
|
88
106
|
|
|
@@ -138,7 +156,7 @@ function parse(input, slots) {
|
|
|
138
156
|
while (!eof() && !/[\s>\/]/.test(peek())) val += advance()
|
|
139
157
|
}
|
|
140
158
|
|
|
141
|
-
attrs[name] = resolveValue(val)
|
|
159
|
+
attrs[name] = name === 'events' ? resolveValue(val).split(',') : resolveValue(val)
|
|
142
160
|
} else {
|
|
143
161
|
attrs[name] = true
|
|
144
162
|
}
|
|
@@ -158,9 +176,23 @@ function build(node, slots) {
|
|
|
158
176
|
const Ctor = registry[node.tag]
|
|
159
177
|
if (!Ctor) throw new Error('Unknown cell: ' + node.tag)
|
|
160
178
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
179
|
+
let style = null
|
|
180
|
+
const children = node.children
|
|
181
|
+
.map((c) => build(c, slots))
|
|
182
|
+
.filter((c) => {
|
|
183
|
+
if (c instanceof Style) {
|
|
184
|
+
style = c
|
|
185
|
+
return false
|
|
186
|
+
}
|
|
187
|
+
return c !== null
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
return new Ctor({
|
|
191
|
+
...node.attrs,
|
|
192
|
+
children,
|
|
193
|
+
style,
|
|
194
|
+
cells: node.tag.toLowerCase() === 'style' ? registry : undefined
|
|
195
|
+
})
|
|
164
196
|
}
|
|
165
197
|
|
|
166
198
|
module.exports = { cellery, register }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cellery",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "cellery",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./package": "./package.json",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"format": "prettier . --write",
|
|
27
|
-
"test": "
|
|
28
|
-
"lint": "lunte"
|
|
27
|
+
"test": "brittle-bare test/all.js",
|
|
28
|
+
"lint": "prettier . --check && lunte"
|
|
29
29
|
},
|
|
30
30
|
"repository": {
|
|
31
31
|
"type": "git",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/holepunchto/cellery",
|
|
40
40
|
"dependencies": {
|
|
41
|
+
"css-tree": "^3.2.1",
|
|
41
42
|
"iambus": "^2.0.6"
|
|
42
43
|
}
|
|
43
44
|
}
|