cellery 1.2.0 → 1.3.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
@@ -10,6 +12,7 @@ class Cell {
10
12
  this.decoration = opts.decoration
11
13
  this.size = opts.size
12
14
  this.onclick = opts.onclick
15
+ this.style = opts.style
13
16
  }
14
17
 
15
18
  static Styled(styledOpts = {}) {
@@ -64,6 +67,83 @@ class Cell {
64
67
  }
65
68
  }
66
69
 
70
+ class Style {
71
+ constructor(opts = {}) {
72
+ this.content = parse(opts.children?.join('\n'))
73
+ }
74
+
75
+ findPropertyOfCell(name, property) {
76
+ return this.findProperty(`[data-cellery-cell="${name}"]`, property)
77
+ }
78
+
79
+ findProperty(selector, property) {
80
+ let value = null
81
+
82
+ walk(this.content, {
83
+ visit: 'Rule',
84
+ enter(rule) {
85
+ const prelude = generate(rule.prelude)
86
+ if (prelude !== selector) return
87
+
88
+ rule.block.children.forEach((decl) => {
89
+ if (decl.type === 'Declaration' && decl.property === property) {
90
+ value = generate(decl.value)
91
+ }
92
+ })
93
+ }
94
+ })
95
+
96
+ return value
97
+ }
98
+
99
+ addScope(target, parent) {
100
+ walk(this.content, {
101
+ visit: 'Rule',
102
+ enter(rule) {
103
+ rule.prelude.children.forEach((selector) => {
104
+ const str = generate(selector)
105
+ if (!str.startsWith(target)) return
106
+
107
+ const items = [...selector.children]
108
+ let inserted = false
109
+ for (let i = 0; i < items.length; i++) {
110
+ const node = items[i]
111
+ if (node.type === 'PseudoClassSelector' || node.type === 'PseudoElementSelector') {
112
+ selector.children.insertData({ type: 'IdSelector', name: parent }, items[i])
113
+ inserted = true
114
+ break
115
+ }
116
+ }
117
+ if (!inserted) {
118
+ selector.children.appendData({ type: 'IdSelector', name: parent })
119
+ }
120
+ })
121
+ }
122
+ })
123
+ }
124
+ }
125
+
126
+ class StyleHTML extends Style {
127
+ constructor(opts = {}) {
128
+ super(opts)
129
+
130
+ const cellNames = new Set(Object.keys(opts.cells))
131
+
132
+ walk(this.content, {
133
+ visit: 'TypeSelector',
134
+ enter(node) {
135
+ if (cellNames.has(node.name)) {
136
+ node.name = `[data-cellery-cell="${node.name}"]`
137
+ }
138
+ }
139
+ })
140
+ }
141
+
142
+ toCSS() {
143
+ return generate(this.content)
144
+ }
145
+ }
146
+
67
147
  class Container extends Cell {
68
148
  // TODO: replace with classes
69
149
  static ScrollAll = 'all'
@@ -89,7 +169,7 @@ class App extends Cell {
89
169
  class Text extends Cell {
90
170
  constructor(opts = {}) {
91
171
  super(opts)
92
- this.value = opts.value || ''
172
+ this.value = opts.value || this.children.filter((c) => typeof c === 'string').join('') || ''
93
173
  }
94
174
  }
95
175
 
@@ -114,5 +194,8 @@ module.exports = {
114
194
  App,
115
195
  Text,
116
196
  Paragraph,
117
- Input
197
+ Input,
198
+
199
+ Style,
200
+ StyleHTML
118
201
  }
package/lib/template.js CHANGED
@@ -1,8 +1,8 @@
1
- const { Cell, Container, App, Text, Paragraph, Input } = require('./cells')
1
+ const { Cell, Container, App, Text, Paragraph, Input, StyleHTML, Style } = require('./cells')
2
2
 
3
3
  const SENTINEL = '\x00'
4
4
 
5
- const registry = { Cell, Container, App, Text, Paragraph, Input }
5
+ const registry = { Cell, Container, App, Text, Paragraph, Input, 'Style.HTML': StyleHTML }
6
6
 
7
7
  function register(cells) {
8
8
  Object.assign(registry, cells)
@@ -44,10 +44,18 @@ function parse(input, slots) {
44
44
  while (!eof() && /\s/.test(peek())) advance()
45
45
  }
46
46
 
47
+ const SLOT_RE = new RegExp(SENTINEL + '(\\d+)' + SENTINEL, 'g')
48
+ const SLOT_PURE = new RegExp('^' + SENTINEL + '(\\d+)' + SENTINEL + '$')
49
+
47
50
  function resolveValue(str) {
48
51
  str = str.trim()
49
- const m = str.match(new RegExp('^' + SENTINEL + '(\\d+)' + SENTINEL + '$'))
52
+ // Pure placeholder preserve type
53
+ const m = str.match(SLOT_PURE)
50
54
  if (m) return slots[parseInt(m[1])]
55
+ // Mixed content — inline replace as strings
56
+ if (str.includes(SENTINEL)) {
57
+ return str.replace(SLOT_RE, (_, id) => String(slots[parseInt(id)]))
58
+ }
51
59
  if (str === 'true') return true
52
60
  if (str === 'false') return false
53
61
  if (str !== '' && !isNaN(str)) return Number(str)
@@ -82,7 +90,7 @@ function parse(input, slots) {
82
90
  advance() // <
83
91
 
84
92
  let name = ''
85
- while (!eof() && /[a-zA-Z0-9_]/.test(peek())) name += advance()
93
+ while (!eof() && /[a-zA-Z0-9_.]/.test(peek())) name += advance()
86
94
 
87
95
  const attrs = parseAttrs()
88
96
 
@@ -158,9 +166,23 @@ function build(node, slots) {
158
166
  const Ctor = registry[node.tag]
159
167
  if (!Ctor) throw new Error('Unknown cell: ' + node.tag)
160
168
 
161
- const children = node.children.map((c) => build(c, slots)).filter((c) => c != null)
162
-
163
- return new Ctor({ ...node.attrs, children })
169
+ let style = null
170
+ const children = node.children
171
+ .map((c) => build(c, slots))
172
+ .filter((c) => {
173
+ if (c instanceof Style) {
174
+ style = c
175
+ return false
176
+ }
177
+ return c !== null
178
+ })
179
+
180
+ return new Ctor({
181
+ ...node.attrs,
182
+ children,
183
+ style,
184
+ cells: node.tag.startsWith('Style') ? registry : undefined
185
+ })
164
186
  }
165
187
 
166
188
  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.3.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
  }