@threlte/flex 0.0.8 → 0.1.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.
@@ -23,6 +23,8 @@ parentNodeContext?.insertNode(node, order);
23
23
  onDestroy(() => {
24
24
  parentNodeContext?.removeNode(node);
25
25
  });
26
+ // update the order of the node
27
+ $: parentNodeContext?.updateNodeOrder(node, order);
26
28
  addNode(node, group, $$restProps);
27
29
  updateNodeProps(node, { ...classParser?.(_class, {}), ...$$restProps }, true);
28
30
  $: updateNodeProps(node, { ...classParser?.(_class, {}), ...$$restProps });
@@ -22,8 +22,6 @@ export { _class as class };
22
22
  const dispatch = createRawEventDispatcher();
23
23
  const rootGroup = new Group();
24
24
  rootGroup.userData.isNode = true;
25
- const rootNode = yoga.Node.create();
26
- createNodeContext(rootNode);
27
25
  const boundingBox = new Box3();
28
26
  const vec3 = new Vector3();
29
27
  /**
@@ -115,6 +113,8 @@ const flexContext = createFlexContext({
115
113
  reflow,
116
114
  classParser
117
115
  });
116
+ const rootNode = yoga.Node.create();
117
+ createNodeContext(rootNode);
118
118
  const { mainAxis, crossAxis, depthAxis } = flexContext;
119
119
  $: rootNode.setWidth(width * scaleFactor), rootNode.setHeight(height * scaleFactor);
120
120
  // prettier-ignore
@@ -2,6 +2,7 @@ import type { Node } from 'yoga-layout';
2
2
  export type NodeContext = {
3
3
  insertNode: (childNode: Node, order?: number) => void;
4
4
  removeNode: (childNode: Node) => void;
5
+ updateNodeOrder: (childNode: Node, order?: number) => void;
5
6
  };
6
7
  export declare const nodeContextName = "__threlte-node";
7
8
  export declare const createNodeContext: (node: Node) => NodeContext | undefined;
@@ -1,21 +1,32 @@
1
1
  import { getContext, setContext } from 'svelte';
2
+ import { useReflow } from '..';
2
3
  export const nodeContextName = '__threlte-node';
3
4
  export const createNodeContext = (node) => {
5
+ const reflow = useReflow();
6
+ /** Set to keep track of all child nodes */
4
7
  const childNodes = new Set();
8
+ /** Map to keep track of the requested order of nodes */
5
9
  const childNodesOrderMap = new Map();
6
10
  const parentNodeContext = getContext(nodeContextName);
11
+ const removeAllChildNodes = () => {
12
+ childNodes.forEach((childNode) => {
13
+ node.removeChild(childNode);
14
+ });
15
+ };
7
16
  setContext(nodeContextName, {
8
17
  insertNode(childNode, order) {
9
18
  // we want to keep track of all child nodes
10
19
  childNodes.add(childNode);
11
- // Additionally, we need to keep track of child nodes that need to be inserted at a specific order
20
+ // Additionally, we need to keep track of child nodes that need to be
21
+ // inserted at a specific order
12
22
  if (order !== undefined) {
13
23
  childNodesOrderMap.set(childNode, {
14
24
  requestedOrder: order
15
25
  });
16
26
  }
17
27
  if (childNodesOrderMap.size) {
18
- // we need to sort the child nodes by their requested order. We leave the nodes that don't have a requested order untouched.
28
+ // we need to sort the child nodes by their requested order. We leave
29
+ // the nodes that don't have a requested order untouched.
19
30
  const sorted = Array.from(childNodes)
20
31
  .map((node, index) => {
21
32
  return {
@@ -25,10 +36,9 @@ export const createNodeContext = (node) => {
25
36
  })
26
37
  .sort((a, b) => a.order - b.order)
27
38
  .map(({ node }) => node);
28
- // Then we need to remove all child nodes from the node and insert them in the correct order.
29
- sorted.forEach((childNode) => {
30
- node.removeChild(childNode);
31
- });
39
+ // Then we need to remove all child nodes from the node and insert them
40
+ // in the correct order.
41
+ removeAllChildNodes();
32
42
  sorted.forEach((childNode, index) => {
33
43
  node.insertChild(childNode, index);
34
44
  });
@@ -36,11 +46,57 @@ export const createNodeContext = (node) => {
36
46
  else {
37
47
  node.insertChild(childNode, node.getChildCount());
38
48
  }
49
+ reflow();
39
50
  },
40
51
  removeNode(childNode) {
41
52
  node.removeChild(childNode);
42
53
  childNodes.delete(childNode);
43
54
  childNodesOrderMap.delete(childNode);
55
+ reflow();
56
+ },
57
+ updateNodeOrder(childNode, order) {
58
+ let update = false;
59
+ const oldOrder = childNodesOrderMap.get(childNode)?.requestedOrder;
60
+ if (order === undefined) {
61
+ // if the order is undefined, we remove the node from the map
62
+ if (oldOrder !== undefined) {
63
+ childNodesOrderMap.delete(childNode);
64
+ update = true;
65
+ }
66
+ }
67
+ else {
68
+ // if the order is defined, we update the node in the map
69
+ const oldOrder = childNodesOrderMap.get(childNode)?.requestedOrder;
70
+ if (oldOrder !== order) {
71
+ childNodesOrderMap.set(childNode, {
72
+ requestedOrder: order
73
+ });
74
+ update = true;
75
+ }
76
+ }
77
+ // if there's no update, return early
78
+ if (!update)
79
+ return;
80
+ // remove all child nodes from the node
81
+ removeAllChildNodes();
82
+ // we need to sort the child nodes by their requested order. We leave the
83
+ // nodes that don't have a requested order untouched.
84
+ const sorted = Array.from(childNodes)
85
+ .map((node, index) => {
86
+ return {
87
+ order: childNodesOrderMap.get(node)?.requestedOrder ?? index,
88
+ node
89
+ };
90
+ })
91
+ .sort((a, b) => a.order - b.order)
92
+ .map(({ node }) => node);
93
+ // Then we need to remove all child nodes from the node and insert them in
94
+ // the correct order.
95
+ removeAllChildNodes();
96
+ sorted.forEach((childNode, index) => {
97
+ node.insertChild(childNode, index);
98
+ });
99
+ reflow();
44
100
  }
45
101
  });
46
102
  return parentNodeContext;
@@ -1,124 +1,143 @@
1
1
  import { createClassParser } from './createClassParser';
2
+ const parseNumericOrAutoOrPercentageValue = (value) => {
3
+ if (value === 'auto') {
4
+ return value;
5
+ }
6
+ else if (value.endsWith('%')) {
7
+ return value;
8
+ }
9
+ else if (value === 'full') {
10
+ return '100%';
11
+ }
12
+ else if (value.match(/^\d+\/\d+$/)) {
13
+ const [width, height] = value.split('/');
14
+ const percentage = Math.round((Number(width) / Number(height)) * 100);
15
+ return `${percentage}%`;
16
+ }
17
+ return Number(value);
18
+ };
19
+ const parseNumericOrPercentageValue = (value) => {
20
+ if (value.endsWith('%')) {
21
+ return value;
22
+ }
23
+ else if (value === 'full') {
24
+ return '100%';
25
+ }
26
+ else if (value.match(/^\d+\/\d+$/)) {
27
+ const [width, height] = value.split('/');
28
+ const percentage = Math.round((Number(width) / Number(height)) * 100);
29
+ return `${percentage}%`;
30
+ }
31
+ return Number(value);
32
+ };
2
33
  export const tailwindParser = createClassParser((string, props) => {
3
34
  const classes = string.split(' ').map((className) => className.trim());
4
- const parseNumericOrAutoOrPercentageValue = (value) => {
5
- if (value === 'auto') {
6
- return value;
7
- }
8
- else if (value.endsWith('%')) {
9
- return value;
10
- }
11
- else if (value === 'full') {
12
- return '100%';
13
- }
14
- else if (value.match(/^\d+\/\d+$/)) {
15
- const [width, height] = value.split('/');
16
- const percentage = Math.round((Number(width) / Number(height)) * 100);
17
- return `${percentage}%`;
18
- }
19
- return Number(value);
20
- };
21
- const parseNumericOrPercentageValue = (value) => {
22
- if (value.endsWith('%')) {
23
- return value;
24
- }
25
- else if (value === 'full') {
26
- return '100%';
27
- }
28
- else if (value.match(/^\d+\/\d+$/)) {
29
- const [width, height] = value.split('/');
30
- const percentage = Math.round((Number(width) / Number(height)) * 100);
31
- return `${percentage}%`;
32
- }
33
- return Number(value);
34
- };
35
35
  classes.forEach((className) => {
36
36
  // padding
37
37
  if (className.startsWith('p-')) {
38
38
  const [, value] = className.split('-');
39
39
  props.padding = parseNumericOrPercentageValue(value);
40
+ return;
40
41
  }
41
42
  if (className.startsWith('px-')) {
42
43
  const [, value] = className.split('-');
43
44
  props.paddingLeft = parseNumericOrPercentageValue(value);
44
45
  props.paddingRight = parseNumericOrPercentageValue(value);
46
+ return;
45
47
  }
46
48
  if (className.startsWith('py-')) {
47
49
  const [, value] = className.split('-');
48
50
  props.paddingTop = parseNumericOrPercentageValue(value);
49
51
  props.paddingBottom = parseNumericOrPercentageValue(value);
52
+ return;
50
53
  }
51
54
  if (className.startsWith('pt-')) {
52
55
  const [, value] = className.split('-');
53
56
  props.paddingTop = parseNumericOrPercentageValue(value);
57
+ return;
54
58
  }
55
59
  if (className.startsWith('pr-')) {
56
60
  const [, value] = className.split('-');
57
61
  props.paddingRight = parseNumericOrPercentageValue(value);
62
+ return;
58
63
  }
59
64
  if (className.startsWith('pb-')) {
60
65
  const [, value] = className.split('-');
61
66
  props.paddingBottom = parseNumericOrPercentageValue(value);
67
+ return;
62
68
  }
63
69
  if (className.startsWith('pl-')) {
64
70
  const [, value] = className.split('-');
65
71
  props.paddingLeft = parseNumericOrPercentageValue(value);
72
+ return;
66
73
  }
67
74
  // margin
68
75
  if (className.startsWith('m-')) {
69
76
  const [, value] = className.split('-');
70
77
  props.margin = parseNumericOrAutoOrPercentageValue(value);
78
+ return;
71
79
  }
72
80
  if (className.startsWith('mx-')) {
73
81
  const [, value] = className.split('-');
74
82
  props.marginLeft = parseNumericOrAutoOrPercentageValue(value);
75
83
  props.marginRight = parseNumericOrAutoOrPercentageValue(value);
84
+ return;
76
85
  }
77
86
  if (className.startsWith('my-')) {
78
87
  const [, value] = className.split('-');
79
88
  props.marginTop = parseNumericOrAutoOrPercentageValue(value);
80
89
  props.marginBottom = parseNumericOrAutoOrPercentageValue(value);
90
+ return;
81
91
  }
82
92
  if (className.startsWith('mt-')) {
83
93
  const [, value] = className.split('-');
84
94
  props.marginTop = parseNumericOrAutoOrPercentageValue(value);
95
+ return;
85
96
  }
86
97
  if (className.startsWith('mr-')) {
87
98
  const [, value] = className.split('-');
88
99
  props.marginRight = parseNumericOrAutoOrPercentageValue(value);
100
+ return;
89
101
  }
90
102
  if (className.startsWith('mb-')) {
91
103
  const [, value] = className.split('-');
92
104
  props.marginBottom = parseNumericOrAutoOrPercentageValue(value);
105
+ return;
93
106
  }
94
107
  if (className.startsWith('ml-')) {
95
108
  const [, value] = className.split('-');
96
109
  props.marginLeft = parseNumericOrAutoOrPercentageValue(value);
110
+ return;
97
111
  }
98
112
  // width
99
113
  if (className.startsWith('w-')) {
100
114
  const [, value] = className.split('-');
101
115
  props.width = parseNumericOrAutoOrPercentageValue(value);
116
+ return;
102
117
  }
103
118
  // height
104
119
  if (className.startsWith('h-')) {
105
120
  const [, value] = className.split('-');
106
121
  props.height = parseNumericOrAutoOrPercentageValue(value);
122
+ return;
107
123
  }
108
124
  // flex-basis
109
125
  if (className.startsWith('basis-')) {
110
126
  const [, value] = className.split('-');
111
127
  props.flexBasis = parseNumericOrAutoOrPercentageValue(value);
128
+ return;
112
129
  }
113
130
  // flex-grow
114
131
  if (className.startsWith('grow-')) {
115
132
  const [, value] = className.split('-');
116
133
  props.flexGrow = Number(value);
134
+ return;
117
135
  }
118
136
  // flex-shrink
119
137
  if (className.startsWith('shrink-')) {
120
138
  const [, value] = className.split('-');
121
139
  props.flexShrink = Number(value);
140
+ return;
122
141
  }
123
142
  // flex-direction
124
143
  if (className.startsWith('flex-')) {
@@ -150,6 +169,7 @@ export const tailwindParser = createClassParser((string, props) => {
150
169
  const [, value] = className.split('-');
151
170
  props.flex = Number(value);
152
171
  }
172
+ return;
153
173
  }
154
174
  // justify-content
155
175
  if (className.startsWith('justify-')) {
@@ -173,6 +193,7 @@ export const tailwindParser = createClassParser((string, props) => {
173
193
  props.justifyContent = 'SpaceEvenly';
174
194
  break;
175
195
  }
196
+ return;
176
197
  }
177
198
  // align-items
178
199
  if (className.startsWith('items-')) {
@@ -193,6 +214,7 @@ export const tailwindParser = createClassParser((string, props) => {
193
214
  props.alignItems = 'Stretch';
194
215
  break;
195
216
  }
217
+ return;
196
218
  }
197
219
  // align-content
198
220
  if (className.startsWith('content-')) {
@@ -222,6 +244,7 @@ export const tailwindParser = createClassParser((string, props) => {
222
244
  props.alignContent = 'Baseline';
223
245
  break;
224
246
  }
247
+ return;
225
248
  }
226
249
  // align-self
227
250
  if (className.startsWith('self-')) {
@@ -245,36 +268,44 @@ export const tailwindParser = createClassParser((string, props) => {
245
268
  props.alignSelf = 'Baseline';
246
269
  break;
247
270
  }
271
+ return;
248
272
  }
249
273
  // Gaps
250
274
  if (className.startsWith('gap-x-')) {
251
275
  const [, value] = className.split('-');
252
276
  props.gapColumn = Number(value);
277
+ return;
253
278
  }
254
279
  else if (className.startsWith('gap-y-')) {
255
280
  const [, value] = className.split('-');
256
281
  props.gapRow = Number(value);
282
+ return;
257
283
  }
258
284
  else if (className.startsWith('gap-')) {
259
285
  const [, value] = className.split('-');
260
286
  props.gap = Number(value);
287
+ return;
261
288
  }
262
289
  // Position
263
290
  if (className.startsWith('top-')) {
264
291
  const [, value] = className.split('-');
265
292
  props.top = parseNumericOrPercentageValue(value);
293
+ return;
266
294
  }
267
295
  if (className.startsWith('right-')) {
268
296
  const [, value] = className.split('-');
269
297
  props.right = parseNumericOrPercentageValue(value);
298
+ return;
270
299
  }
271
300
  if (className.startsWith('bottom-')) {
272
301
  const [, value] = className.split('-');
273
302
  props.bottom = parseNumericOrPercentageValue(value);
303
+ return;
274
304
  }
275
305
  if (className.startsWith('left-')) {
276
306
  const [, value] = className.split('-');
277
307
  props.left = parseNumericOrPercentageValue(value);
308
+ return;
278
309
  }
279
310
  // aspect ratio
280
311
  if (className.startsWith('aspect-')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threlte/flex",
3
- "version": "0.0.8",
3
+ "version": "0.1.0",
4
4
  "author": "Grischa Erbe <hello@legrisch.com> (https://legrisch.com)",
5
5
  "license": "MIT",
6
6
  "devDependencies": {
@@ -27,8 +27,8 @@
27
27
  "tslib": "^2.4.1",
28
28
  "typescript": "^5.0.0",
29
29
  "vite": "^4.3.6",
30
- "@threlte/core": "6.0.9",
31
- "@threlte/extras": "6.0.0"
30
+ "@threlte/core": "6.1.0",
31
+ "@threlte/extras": "7.3.0"
32
32
  },
33
33
  "dependencies": {
34
34
  "mitt": "^3.0.1",