@threlte/flex 0.0.6 → 0.0.7

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.
@@ -1,12 +1,18 @@
1
- <script>import { HierarchicalObject, T } from '@threlte/core';
1
+ <script>import { HierarchicalObject, T, createRawEventDispatcher } from '@threlte/core';
2
2
  import { onDestroy } from 'svelte';
3
3
  import { Group } from 'three';
4
4
  import { useFlex } from '../Flex/context';
5
5
  import { createNodeContext } from '../nodes/context';
6
+ import { createUseDimensionsContext } from '../hooks/useDimensions';
6
7
  export let order = undefined;
7
8
  let _class = '';
8
9
  export { _class as class };
9
- const { scaleFactor, onEvent, addNode, removeNode, updateNodeProps, mainAxis, crossAxis, depthAxis, classParser } = useFlex();
10
+ const dispatch = createRawEventDispatcher();
11
+ /**
12
+ * Create the context for `useDimensions`
13
+ */
14
+ const dimensionsContext = createUseDimensionsContext();
15
+ const { scaleFactor, onEvent, addNode, removeNode, updateNodeProps, mainAxis, crossAxis, depthAxis, classParser, reflow } = useFlex();
10
16
  export const group = new Group();
11
17
  export const contentGroup = new Group();
12
18
  group.userData.isNode = true;
@@ -38,6 +44,12 @@ onEvent('reflow:after', () => {
38
44
  getContentGroup().position[$mainAxis] = computedWidth / 2;
39
45
  getContentGroup().position[$crossAxis] = -computedHeight / 2;
40
46
  getContentGroup().position[$depthAxis] = 0;
47
+ dimensionsContext.width.set(computedWidth);
48
+ dimensionsContext.height.set(computedHeight);
49
+ dispatch('reflow', {
50
+ width: computedWidth,
51
+ height: computedHeight
52
+ });
41
53
  });
42
54
  </script>
43
55
 
@@ -62,6 +74,7 @@ onEvent('reflow:after', () => {
62
74
  }}
63
75
  >
64
76
  <slot
77
+ {reflow}
65
78
  width={computedWidth}
66
79
  height={computedHeight}
67
80
  />
@@ -6,10 +6,16 @@ type BoxProps = NodeProps & {
6
6
  class?: string
7
7
  }
8
8
 
9
- type BoxEvents = {}
9
+ type BoxEvents = {
10
+ reflow: {
11
+ width: number
12
+ height: number
13
+ }
14
+ }
10
15
 
11
16
  type BoxSlots = {
12
17
  default: {
18
+ reflow: () => void
13
19
  width: number
14
20
  height: number
15
21
  }
@@ -15,7 +15,13 @@ const component = forwardEventHandlers();
15
15
  {...$$restProps}
16
16
  bind:this={$component}
17
17
  let:reflow
18
+ let:width
19
+ let:height
18
20
  >
19
- <slot {reflow} />
21
+ <slot
22
+ {reflow}
23
+ {width}
24
+ {height}
25
+ />
20
26
  </InnerFlex>
21
27
  {/if}
@@ -10,6 +10,8 @@ type FlexEvents = ComponentEvents<InnerFlex>
10
10
  type FlexSlots = {
11
11
  default: {
12
12
  reflow: () => void
13
+ width: number
14
+ height: number
13
15
  }
14
16
  }
15
17
 
@@ -8,6 +8,7 @@ import { getRootShift } from '../lib/getRootShift';
8
8
  import { applyNodeProps } from '../lib/props';
9
9
  import { createNodeContext } from '../nodes/context';
10
10
  import { createFlexContext } from './context';
11
+ import { createUseDimensionsContext } from '../hooks/useDimensions';
11
12
  export let yoga;
12
13
  export let width = 1;
13
14
  export let height = 1;
@@ -22,6 +23,10 @@ const rootGroup = new Group();
22
23
  rootGroup.userData.isNode = true;
23
24
  const boundingBox = new Box3();
24
25
  const vec3 = new Vector3();
26
+ /**
27
+ * Create the context for `useDimensions`
28
+ */
29
+ const { width: computedWidth, height: computedHeight } = createUseDimensionsContext();
25
30
  /**
26
31
  * Reflowing inside useFrame automatically batches reflows to 1 per frame.
27
32
  */
@@ -68,9 +73,11 @@ const { start: reflow, stop } = useFrame(() => {
68
73
  maxY = Math.max(maxY, top + height);
69
74
  }
70
75
  flexContext.emit('reflow:after');
76
+ computedWidth.set((maxX - minX) / scaleFactor);
77
+ computedHeight.set((maxY - minY) / scaleFactor);
71
78
  dispatch('reflow', {
72
- width: (maxX - minX) / scaleFactor,
73
- height: (maxY - minY) / scaleFactor
79
+ width: computedWidth.current,
80
+ height: computedHeight.current
74
81
  });
75
82
  stop();
76
83
  }, { autostart: false });
@@ -120,19 +127,25 @@ const flexContext = createFlexContext({
120
127
  const { mainAxis, crossAxis, depthAxis } = flexContext;
121
128
  const { node: rootNode } = createNodeContext();
122
129
  $: rootNode.setWidth(width * scaleFactor), rootNode.setHeight(height * scaleFactor);
123
- $: applyNodeProps(rootNode, { ...classParser?.(_class, {}), ...$$restProps }, scaleFactor),
130
+ $: {
131
+ applyNodeProps(rootNode, { ...classParser?.(_class, {}), ...$$restProps }, scaleFactor);
124
132
  reflow();
125
- $: flexContext.rootWidth.set(width), flexContext.reflow('Updated root width');
126
- $: flexContext.rootHeight.set(height), flexContext.reflow('Updated root height');
127
- $: flexContext.mainAxis.set(plane[0]), flexContext.reflow('Updated main axis');
128
- $: flexContext.crossAxis.set(plane[1]), flexContext.reflow('Updated cross axis');
129
- $: flexContext.depthAxis.set(getDepthAxis(plane)), flexContext.reflow('Updated depth axis');
130
- $: flexContext.scaleFactor.set(scaleFactor), flexContext.reflow('Updated scale factor');
133
+ }
134
+ $: flexContext.rootWidth.set(width), flexContext.reflow();
135
+ $: flexContext.rootHeight.set(height), flexContext.reflow();
136
+ $: flexContext.mainAxis.set(plane[0]), flexContext.reflow();
137
+ $: flexContext.crossAxis.set(plane[1]), flexContext.reflow();
138
+ $: flexContext.depthAxis.set(getDepthAxis(plane)), flexContext.reflow();
139
+ $: flexContext.scaleFactor.set(scaleFactor), flexContext.reflow();
131
140
  onDestroy(() => {
132
141
  rootNode.free();
133
142
  });
134
143
  </script>
135
144
 
136
145
  <T is={rootGroup}>
137
- <slot {reflow} />
146
+ <slot
147
+ {reflow}
148
+ width={$computedWidth}
149
+ height={$computedHeight}
150
+ />
138
151
  </T>
@@ -24,6 +24,8 @@ type InnerFlexEvents = {
24
24
  type InnerFlexSlots = {
25
25
  default: {
26
26
  reflow: () => void
27
+ width: number
28
+ height: number
27
29
  }
28
30
  }
29
31
 
@@ -25,7 +25,7 @@ export type FlexContextData = {
25
25
  rootGroup: Group;
26
26
  rootWidth: CurrentWritable<number>;
27
27
  rootHeight: CurrentWritable<number>;
28
- reflow: (msg?: string) => void;
28
+ reflow: () => void;
29
29
  classParser?: ClassParser;
30
30
  };
31
31
  export type FlexContext = FlexContextData & Emitter<FlexContextEvents> & {
@@ -0,0 +1,50 @@
1
+ import { type CurrentWritable } from '@threlte/core';
2
+ type UseDimensionsContext = {
3
+ width: CurrentWritable<number>;
4
+ height: CurrentWritable<number>;
5
+ };
6
+ export declare const flexContextName = "__threlte-flex-dimensions";
7
+ /**
8
+ * Creates a context for useDimensions.
9
+ */
10
+ export declare const createUseDimensionsContext: () => UseDimensionsContext;
11
+ /**
12
+ * The hook `useDimensions` can be used to retrieve the computed width and
13
+ * height of a `<Flex>` or `<Box>` component as
14
+ * [CurrentWritable](https://threlte.xyz/docs/reference/core/utilities#currentwritable)
15
+ * stores.
16
+ *
17
+ * ## Usage
18
+ *
19
+ * ### In a `<Flex>` component
20
+ *
21
+ * Because there's no viewport to measure, the width and height of a `<Flex>`
22
+ * component need to be set manually. Nevertheless, the dimensions of the
23
+ * contents of the `<Flex>` component will be measured after a layout reflow and
24
+ * can be retrieved using `useDimensions` in a direct child component to
25
+ * `<Flex>`.
26
+ *
27
+ * ### In a `<Box>` component
28
+ *
29
+ * By default `@threlte/flex` controls elements position only. In some cases you
30
+ * may want to control element sizing too. Since `@threlte/flex` has no
31
+ * information about how the inner content size works, you need to set your
32
+ * content size manually. You can do this by using `useDimensions` hook in a
33
+ * direct child component to `<Box>`.
34
+ *
35
+ * @example
36
+ * ```svelte
37
+ * <script>
38
+ * import { useDimensions } from '@threlte/flex'
39
+ *
40
+ * const { width, height } = useDimensions()
41
+ * </script>
42
+ *
43
+ * <T.Mesh scale.x={$width} scale.y={$height}>
44
+ * <T.BoxGeometry />
45
+ * <T.MeshBasicMaterial color="red" />
46
+ * </T.Mesh>
47
+ * ```
48
+ */
49
+ export declare const useDimensions: () => UseDimensionsContext;
50
+ export {};
@@ -0,0 +1,59 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ import { currentWritable } from '@threlte/core';
3
+ export const flexContextName = '__threlte-flex-dimensions';
4
+ /**
5
+ * Creates a context for useDimensions.
6
+ */
7
+ export const createUseDimensionsContext = () => {
8
+ const context = {
9
+ width: currentWritable(0),
10
+ height: currentWritable(0)
11
+ };
12
+ setContext(flexContextName, context);
13
+ return context;
14
+ };
15
+ /**
16
+ * The hook `useDimensions` can be used to retrieve the computed width and
17
+ * height of a `<Flex>` or `<Box>` component as
18
+ * [CurrentWritable](https://threlte.xyz/docs/reference/core/utilities#currentwritable)
19
+ * stores.
20
+ *
21
+ * ## Usage
22
+ *
23
+ * ### In a `<Flex>` component
24
+ *
25
+ * Because there's no viewport to measure, the width and height of a `<Flex>`
26
+ * component need to be set manually. Nevertheless, the dimensions of the
27
+ * contents of the `<Flex>` component will be measured after a layout reflow and
28
+ * can be retrieved using `useDimensions` in a direct child component to
29
+ * `<Flex>`.
30
+ *
31
+ * ### In a `<Box>` component
32
+ *
33
+ * By default `@threlte/flex` controls elements position only. In some cases you
34
+ * may want to control element sizing too. Since `@threlte/flex` has no
35
+ * information about how the inner content size works, you need to set your
36
+ * content size manually. You can do this by using `useDimensions` hook in a
37
+ * direct child component to `<Box>`.
38
+ *
39
+ * @example
40
+ * ```svelte
41
+ * <script>
42
+ * import { useDimensions } from '@threlte/flex'
43
+ *
44
+ * const { width, height } = useDimensions()
45
+ * </script>
46
+ *
47
+ * <T.Mesh scale.x={$width} scale.y={$height}>
48
+ * <T.BoxGeometry />
49
+ * <T.MeshBasicMaterial color="red" />
50
+ * </T.Mesh>
51
+ * ```
52
+ */
53
+ export const useDimensions = () => {
54
+ const context = getContext(flexContextName);
55
+ if (!context) {
56
+ throw new Error('useDimensions must be used within a <Flex> component');
57
+ }
58
+ return context;
59
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The hook useReflow allows you to manually request a [layout
3
+ * reflow](https://threlte.xyz/docs/reference/flex/flex#layout-reflow), for
4
+ * instance when a [`<Text>`](https://threlte.xyz/docs/reference/extras/text)
5
+ * component finished synchronizing or when a model has loaded into view and
6
+ * there’s no easy access to the reflow slot prop of the components `<Flex>` or
7
+ * `<Box>` because of component composition. Calls to`reflow` will be limited to
8
+ * once per frame, so it’s safe to call it from multiple components at a time.
9
+ */
10
+ export declare const useReflow: () => () => void;
@@ -0,0 +1,18 @@
1
+ import { getContext } from 'svelte';
2
+ import { flexContextName } from '../Flex/context';
3
+ /**
4
+ * The hook useReflow allows you to manually request a [layout
5
+ * reflow](https://threlte.xyz/docs/reference/flex/flex#layout-reflow), for
6
+ * instance when a [`<Text>`](https://threlte.xyz/docs/reference/extras/text)
7
+ * component finished synchronizing or when a model has loaded into view and
8
+ * there’s no easy access to the reflow slot prop of the components `<Flex>` or
9
+ * `<Box>` because of component composition. Calls to`reflow` will be limited to
10
+ * once per frame, so it’s safe to call it from multiple components at a time.
11
+ */
12
+ export const useReflow = () => {
13
+ const flexContext = getContext(flexContextName);
14
+ if (!flexContext) {
15
+ throw new Error('useReflow must be used within a <Flex> component');
16
+ }
17
+ return flexContext.reflow;
18
+ };
package/dist/index.d.ts CHANGED
@@ -3,3 +3,5 @@ export { default as Flex } from './Flex/Flex.svelte';
3
3
  export type { NodeProps } from './lib/props';
4
4
  export { createClassParser } from './parsers/createClassParser';
5
5
  export { tailwindParser } from './parsers/tailwindParser';
6
+ export { useReflow } from './hooks/useReflow';
7
+ export { useDimensions } from './hooks/useDimensions';
package/dist/index.js CHANGED
@@ -3,3 +3,6 @@ export { default as Flex } from './Flex/Flex.svelte';
3
3
  // parsers
4
4
  export { createClassParser } from './parsers/createClassParser';
5
5
  export { tailwindParser } from './parsers/tailwindParser';
6
+ // hooks
7
+ export { useReflow } from './hooks/useReflow';
8
+ export { useDimensions } from './hooks/useDimensions';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threlte/flex",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
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.8",
31
- "@threlte/extras": "5.7.0"
30
+ "@threlte/core": "6.0.9",
31
+ "@threlte/extras": "6.0.0"
32
32
  },
33
33
  "dependencies": {
34
34
  "mitt": "^3.0.1",