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 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.onclick = opts.onclick
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
- if (!cell.cellery) {
40
- cell.register(this.cellery)
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.render(cell),
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
- register(cellery) {
60
- this.cellery = cellery
61
- for (const c of this.children) {
62
- c.register(cellery)
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 App extends Cell {
152
+ class Fragment extends Cell {
84
153
  constructor(opts = {}) {
85
- super({ ...opts, id: 'app' })
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
- App,
179
+ Fragment,
115
180
  Text,
116
- Paragraph,
117
- Input
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, App, Text, Paragraph, Input } = require('./cells')
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 = { Cell, Container, App, Text, Paragraph, Input }
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
- const m = str.match(new RegExp('^' + SENTINEL + '(\\d+)' + SENTINEL + '$'))
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
- const children = node.children.map((c) => build(c, slots)).filter((c) => c != null)
162
-
163
- return new Ctor({ ...node.attrs, children })
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.2.0",
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": "prettier . --check && lunte && brittle-bare test/all.js",
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
  }