@threlte/flex 0.0.7 → 0.0.8

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.
@@ -2,8 +2,8 @@
2
2
  import { onDestroy } from 'svelte';
3
3
  import { Group } from 'three';
4
4
  import { useFlex } from '../Flex/context';
5
- import { createNodeContext } from '../nodes/context';
6
5
  import { createUseDimensionsContext } from '../hooks/useDimensions';
6
+ import { createNodeContext } from '../nodes/context';
7
7
  export let order = undefined;
8
8
  let _class = '';
9
9
  export { _class as class };
@@ -13,10 +13,16 @@ const dispatch = createRawEventDispatcher();
13
13
  */
14
14
  const dimensionsContext = createUseDimensionsContext();
15
15
  const { scaleFactor, onEvent, addNode, removeNode, updateNodeProps, mainAxis, crossAxis, depthAxis, classParser, reflow } = useFlex();
16
- export const group = new Group();
17
- export const contentGroup = new Group();
16
+ const group = new Group();
18
17
  group.userData.isNode = true;
19
- export const { node } = createNodeContext(order);
18
+ const contentGroup = new Group();
19
+ const { yoga } = useFlex();
20
+ const node = yoga.Node.create();
21
+ const parentNodeContext = createNodeContext(node);
22
+ parentNodeContext?.insertNode(node, order);
23
+ onDestroy(() => {
24
+ parentNodeContext?.removeNode(node);
25
+ });
20
26
  addNode(node, group, $$restProps);
21
27
  updateNodeProps(node, { ...classParser?.(_class, {}), ...$$restProps }, true);
22
28
  $: updateNodeProps(node, { ...classParser?.(_class, {}), ...$$restProps });
@@ -2,7 +2,7 @@ import { SvelteComponent } from 'svelte'
2
2
  import type { NodeProps } from '../lib/props'
3
3
 
4
4
  type BoxProps = NodeProps & {
5
- order?: number
5
+ order?: number | undefined
6
6
  class?: string
7
7
  }
8
8
 
@@ -2,13 +2,14 @@
2
2
  import { onDestroy } from 'svelte';
3
3
  import { Box3, Group, Vector3 } from 'three';
4
4
  import { Direction } from 'yoga-layout';
5
+ import { createUseDimensionsContext } from '../hooks/useDimensions';
5
6
  import { getDepthAxis } from '../lib/getDepthAxis';
6
7
  import { getOrientedBoundingBoxSize } from '../lib/getOrientedBoundingBoxSize';
7
8
  import { getRootShift } from '../lib/getRootShift';
8
9
  import { applyNodeProps } from '../lib/props';
10
+ import { propsChanged } from '../lib/propsChanged';
9
11
  import { createNodeContext } from '../nodes/context';
10
12
  import { createFlexContext } from './context';
11
- import { createUseDimensionsContext } from '../hooks/useDimensions';
12
13
  export let yoga;
13
14
  export let width = 1;
14
15
  export let height = 1;
@@ -21,6 +22,8 @@ export { _class as class };
21
22
  const dispatch = createRawEventDispatcher();
22
23
  const rootGroup = new Group();
23
24
  rootGroup.userData.isNode = true;
25
+ const rootNode = yoga.Node.create();
26
+ createNodeContext(rootNode);
24
27
  const boundingBox = new Box3();
25
28
  const vec3 = new Vector3();
26
29
  /**
@@ -90,25 +93,13 @@ const flexContext = createFlexContext({
90
93
  },
91
94
  updateNodeProps(node, props, force = false) {
92
95
  const nodeData = flexContext.nodes.get(node);
93
- if (!nodeData)
94
- return;
95
96
  // Updating the props can be forced and is done so on the initial call.
96
- if (!force) {
97
- // Because all NodeProps are primitive types, we can make a simple
98
- // comparison and only request a reflow when necessary. We do that by
99
- // checking the length of the props object and then checking if all keys
100
- // are the same and all values are the same.
101
- const previousKeys = Object.keys(nodeData.props);
102
- const currentKeys = Object.keys(props);
103
- if (previousKeys.length === currentKeys.length &&
104
- currentKeys.every((key) => previousKeys.includes(key)) &&
105
- previousKeys.every((key) => nodeData.props[key] === props[key])) {
106
- return;
107
- }
97
+ if (force || propsChanged(node, props)) {
98
+ applyNodeProps(node, props, scaleFactor);
99
+ reflow();
100
+ if (nodeData)
101
+ nodeData.props = props;
108
102
  }
109
- applyNodeProps(node, props, scaleFactor);
110
- nodeData.props = props;
111
- reflow();
112
103
  },
113
104
  removeNode(node) {
114
105
  flexContext.nodes.delete(node);
@@ -125,12 +116,11 @@ const flexContext = createFlexContext({
125
116
  classParser
126
117
  });
127
118
  const { mainAxis, crossAxis, depthAxis } = flexContext;
128
- const { node: rootNode } = createNodeContext();
129
119
  $: rootNode.setWidth(width * scaleFactor), rootNode.setHeight(height * scaleFactor);
130
- $: {
131
- applyNodeProps(rootNode, { ...classParser?.(_class, {}), ...$$restProps }, scaleFactor);
132
- reflow();
133
- }
120
+ // prettier-ignore
121
+ flexContext.updateNodeProps(rootNode, { ...classParser?.(_class, {}), ...$$restProps }, true);
122
+ // prettier-ignore
123
+ $: flexContext.updateNodeProps(rootNode, { ...classParser?.(_class, {}), ...$$restProps });
134
124
  $: flexContext.rootWidth.set(width), flexContext.reflow();
135
125
  $: flexContext.rootHeight.set(height), flexContext.reflow();
136
126
  $: flexContext.mainAxis.set(plane[0]), flexContext.reflow();
@@ -0,0 +1,8 @@
1
+ import type { NodeProps } from './props';
2
+ /**
3
+ * Because all NodeProps are primitive types, we can make a simple comparison
4
+ * and only request a reflow when necessary. We do that by checking the length
5
+ * of the props object and then checking if all keys are the same and all values
6
+ * are the same.
7
+ */
8
+ export declare const propsChanged: (node: Node, props: NodeProps) => boolean;
@@ -0,0 +1,26 @@
1
+ const nodePropsMap = new WeakMap();
2
+ /**
3
+ * Because all NodeProps are primitive types, we can make a simple comparison
4
+ * and only request a reflow when necessary. We do that by checking the length
5
+ * of the props object and then checking if all keys are the same and all values
6
+ * are the same.
7
+ */
8
+ export const propsChanged = (node, props) => {
9
+ // get a reference to the props data for this node
10
+ const propsData = nodePropsMap.get(node);
11
+ // assume that the props have changed
12
+ let changed = true;
13
+ if (propsData) {
14
+ // compare the keys and values of the previous and current props
15
+ const previousKeys = Object.keys(propsData);
16
+ const currentKeys = Object.keys(props);
17
+ if (previousKeys.length === currentKeys.length &&
18
+ currentKeys.every((key) => previousKeys.includes(key)) &&
19
+ previousKeys.every((key) => propsData[key] === props[key])) {
20
+ changed = false;
21
+ }
22
+ }
23
+ // update the props data for this node
24
+ nodePropsMap.set(node, props);
25
+ return changed;
26
+ };
@@ -1,9 +1,7 @@
1
1
  import type { Node } from 'yoga-layout';
2
2
  export type NodeContext = {
3
- node: Node;
4
- insertChild: (child: Node, order?: number) => void;
5
- removeChild: (child: Node) => void;
3
+ insertNode: (childNode: Node, order?: number) => void;
4
+ removeNode: (childNode: Node) => void;
6
5
  };
7
6
  export declare const nodeContextName = "__threlte-node";
8
- export declare const useNode: () => NodeContext;
9
- export declare const createNodeContext: (order?: number) => NodeContext;
7
+ export declare const createNodeContext: (node: Node) => NodeContext | undefined;
@@ -1,31 +1,47 @@
1
- import { getContext, onDestroy, setContext } from 'svelte';
2
- import { useFlex } from '../Flex/context';
1
+ import { getContext, setContext } from 'svelte';
3
2
  export const nodeContextName = '__threlte-node';
4
- export const useNode = () => {
5
- return getContext(nodeContextName);
6
- };
7
- export const createNodeContext = (order) => {
8
- const { yoga } = useFlex();
9
- const node = yoga.Node.create();
10
- const parentNodeContext = useNode();
11
- parentNodeContext?.insertChild(node, order);
12
- onDestroy(() => {
13
- parentNodeContext?.removeChild(node);
14
- });
15
- const data = {
16
- node,
17
- insertChild(child, order) {
3
+ export const createNodeContext = (node) => {
4
+ const childNodes = new Set();
5
+ const childNodesOrderMap = new Map();
6
+ const parentNodeContext = getContext(nodeContextName);
7
+ setContext(nodeContextName, {
8
+ insertNode(childNode, order) {
9
+ // we want to keep track of all child nodes
10
+ childNodes.add(childNode);
11
+ // Additionally, we need to keep track of child nodes that need to be inserted at a specific order
18
12
  if (order !== undefined) {
19
- data.node.insertChild(child, order);
13
+ childNodesOrderMap.set(childNode, {
14
+ requestedOrder: order
15
+ });
16
+ }
17
+ 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.
19
+ const sorted = Array.from(childNodes)
20
+ .map((node, index) => {
21
+ return {
22
+ order: childNodesOrderMap.get(node)?.requestedOrder ?? index,
23
+ node
24
+ };
25
+ })
26
+ .sort((a, b) => a.order - b.order)
27
+ .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
+ });
32
+ sorted.forEach((childNode, index) => {
33
+ node.insertChild(childNode, index);
34
+ });
20
35
  }
21
36
  else {
22
- data.node.insertChild(child, data.node.getChildCount());
37
+ node.insertChild(childNode, node.getChildCount());
23
38
  }
24
39
  },
25
- removeChild(child) {
26
- data.node.removeChild(child);
40
+ removeNode(childNode) {
41
+ node.removeChild(childNode);
42
+ childNodes.delete(childNode);
43
+ childNodesOrderMap.delete(childNode);
27
44
  }
28
- };
29
- setContext(nodeContextName, data);
30
- return data;
45
+ });
46
+ return parentNodeContext;
31
47
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threlte/flex",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "author": "Grischa Erbe <hello@legrisch.com> (https://legrisch.com)",
5
5
  "license": "MIT",
6
6
  "devDependencies": {