@sprucelabs/spruce-heartwood-utils 38.17.7 → 38.17.9
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/README.md
CHANGED
|
@@ -10,16 +10,20 @@ Heartwood integration toolkit for Spruce skills. Use this package to load remote
|
|
|
10
10
|
npm install @sprucelabs/spruce-heartwood-utils
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
```
|
|
14
|
+
yarn add @sprucelabs/spruce-heartwood-utils
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This package is designed for use inside a Spruce skill or a React application.
|
|
14
18
|
|
|
15
19
|
---
|
|
16
20
|
|
|
17
21
|
## Project Pitch
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
+
This module supports development in 2 ways.
|
|
24
|
+
|
|
25
|
+
1. It provides first-party utilities for common tasks in Skill development — remote view controllers, card registration, theming, plugins, and test helpers.
|
|
26
|
+
2. It offers animation and layout primitives for use in React.
|
|
23
27
|
|
|
24
28
|
`@sprucelabs/spruce-heartwood-utils` packages those into one module with explicit entrypoints so you can keep browser bundles clean while still getting first-class testing helpers.
|
|
25
29
|
|
|
@@ -59,7 +63,6 @@ import {
|
|
|
59
63
|
AutoLogoutPlugin,
|
|
60
64
|
loadActiveThemeForOrg,
|
|
61
65
|
Sizer,
|
|
62
|
-
SimpleEmitter,
|
|
63
66
|
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
64
67
|
```
|
|
65
68
|
|
|
@@ -84,8 +87,6 @@ Use split imports so runtime code stays browser-safe while tests get all mocks/h
|
|
|
84
87
|
import {
|
|
85
88
|
CardRegistrar,
|
|
86
89
|
RemoteViewControllerFactoryImpl,
|
|
87
|
-
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
88
|
-
import {
|
|
89
90
|
remoteVcAssert,
|
|
90
91
|
MockRemoteViewControllerFactory,
|
|
91
92
|
} from '@sprucelabs/spruce-heartwood-utils/testing'
|
|
@@ -93,33 +94,6 @@ import {
|
|
|
93
94
|
|
|
94
95
|
---
|
|
95
96
|
|
|
96
|
-
## Table of Contents
|
|
97
|
-
|
|
98
|
-
1. [Project Pitch](#project-pitch)
|
|
99
|
-
2. [What You Get](#what-you-get)
|
|
100
|
-
3. [Import Paths](#import-paths)
|
|
101
|
-
4. [Get Started](#get-started)
|
|
102
|
-
5. [Remote View Controllers](#remote-view-controllers)
|
|
103
|
-
6. [CardRegistrar](#cardregistrar)
|
|
104
|
-
7. [Types](#types)
|
|
105
|
-
8. [Theming](#theming)
|
|
106
|
-
9. [Plugins](#plugins)
|
|
107
|
-
10. [Test Utilities](#test-utilities)
|
|
108
|
-
11. [Device & Layout Utilities](#device--layout-utilities)
|
|
109
|
-
12. [Animation](#animation)
|
|
110
|
-
- [Quick Start](#quick-start)
|
|
111
|
-
- [Required CSS](#required-css)
|
|
112
|
-
- [System Architecture](#system-architecture)
|
|
113
|
-
- [AnimationEmitter](#animationemitter)
|
|
114
|
-
- [Settings](#settings)
|
|
115
|
-
- [queueShow — Show/Hide Queue](#queueshow--showhide-queue)
|
|
116
|
-
- [Sizer — Animated Height Container](#sizer--animated-height-container)
|
|
117
|
-
- [DelayedPlacer — Absolute Positioning](#delayedplacer--absolute-positioning)
|
|
118
|
-
- [sizeUtil — DOM Measurement](#sizeutil--dom-measurement)
|
|
119
|
-
- [How the Three Systems Coordinate](#how-the-three-systems-coordinate)
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
97
|
## Remote View Controllers
|
|
124
98
|
|
|
125
99
|
When your skill needs to load and render cards that come from other skills at runtime, these are the types you work with. In tests, you swap in `MockRemoteViewControllerFactory` to control which cards appear without making real network calls.
|
|
@@ -675,7 +649,6 @@ import {
|
|
|
675
649
|
// Utilities
|
|
676
650
|
sizeUtil, Settings,
|
|
677
651
|
// Emitter — pass to Sizer and DelayedPlacer to coordinate layout events
|
|
678
|
-
SimpleEmitter,
|
|
679
652
|
// Types
|
|
680
653
|
AnimationEmitter,
|
|
681
654
|
SizerProps,
|
|
@@ -773,15 +746,15 @@ Pass the **same emitter instance** to `Sizer` and `DelayedPlacer` when they are
|
|
|
773
746
|
|
|
774
747
|
### AnimationEmitter
|
|
775
748
|
|
|
776
|
-
The interface `Sizer` and `DelayedPlacer` use to communicate layout-change events. Pass an instance as the `emitter` prop to coordinate components. The package exports `
|
|
749
|
+
The interface `Sizer` and `DelayedPlacer` use to communicate layout-change events. Pass an instance as the `emitter` prop to coordinate components. The package exports `SkillViewEmitter` for this:
|
|
777
750
|
|
|
778
751
|
```ts
|
|
779
|
-
import {
|
|
752
|
+
import { SkillViewEmitter } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
780
753
|
|
|
781
|
-
const emitter =
|
|
754
|
+
const emitter = SkillViewEmitter.getInstance()
|
|
782
755
|
```
|
|
783
756
|
|
|
784
|
-
`
|
|
757
|
+
`SkillViewEmitter` implements all three methods (`on`, `off`, `emit`) needed by `AnimationEmitter`.
|
|
785
758
|
|
|
786
759
|
The `AnimationEmitter` interface, for reference:
|
|
787
760
|
|
|
@@ -804,9 +777,9 @@ interface AnimationEmitter {
|
|
|
804
777
|
2. **Create an emitter** — one per component tree, stable across renders:
|
|
805
778
|
|
|
806
779
|
```ts
|
|
807
|
-
import {
|
|
780
|
+
import { SkillViewEmitter } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
808
781
|
|
|
809
|
-
const emitter = useMemo(() =>
|
|
782
|
+
const emitter = useMemo(() => SkillViewEmitter.getInstance(), [])
|
|
810
783
|
```
|
|
811
784
|
|
|
812
785
|
3. **Wire to components and emit events** — pass the emitter to `Sizer` and/or `DelayedPlacer`, then signal layout changes:
|
|
@@ -1071,14 +1044,14 @@ Wraps children in a div whose `height` is set via inline style to match the meas
|
|
|
1071
1044
|
|
|
1072
1045
|
> Inside a Heartwood skill view this CSS is already provided — skip this step.
|
|
1073
1046
|
|
|
1074
|
-
2. **Wrap your content**
|
|
1047
|
+
2. **Wrap your content**
|
|
1075
1048
|
|
|
1076
1049
|
```tsx
|
|
1077
|
-
import { Sizer
|
|
1050
|
+
import { Sizer } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1078
1051
|
|
|
1079
|
-
const emitter = useMemo(() =>
|
|
1052
|
+
const emitter = useMemo(() => SkillViewEmitter.getInstance(), [])
|
|
1080
1053
|
|
|
1081
|
-
<Sizer emitter={emitter}
|
|
1054
|
+
<Sizer emitter={emitter}>
|
|
1082
1055
|
<YourContent />
|
|
1083
1056
|
</Sizer>
|
|
1084
1057
|
```
|
|
@@ -1104,20 +1077,6 @@ interface SizerProps {
|
|
|
1104
1077
|
// CSS class on the outer .sizer div.
|
|
1105
1078
|
className?: string
|
|
1106
1079
|
|
|
1107
|
-
// When true, starts at height 0 and defers first measurement by 100ms.
|
|
1108
|
-
// Use for content that should animate in from nothing.
|
|
1109
|
-
shouldStartAtZero?: boolean
|
|
1110
|
-
|
|
1111
|
-
// When true, keeps overflow hidden throughout. When explicitly false,
|
|
1112
|
-
// overflow is always visible (adds force-show-overflow class).
|
|
1113
|
-
shouldHideOverflow?: boolean
|
|
1114
|
-
|
|
1115
|
-
// Debounce delay in ms before each resize measurement fires. Default: 250ms.
|
|
1116
|
-
// Lower values make Sizer react faster to content changes; useful when
|
|
1117
|
-
// content updates quickly and you want tighter animation timing.
|
|
1118
|
-
sizerDelayMs?: number
|
|
1119
|
-
|
|
1120
|
-
// Event emitter. Without one, Sizer only resizes on React re-renders,
|
|
1121
1080
|
// not in response to external layout events.
|
|
1122
1081
|
emitter?: AnimationEmitter
|
|
1123
1082
|
}
|
|
@@ -1142,23 +1101,23 @@ sizer.current?.hideOverflow(): void // re-apply overflow: hidden
|
|
|
1142
1101
|
#### Usage
|
|
1143
1102
|
|
|
1144
1103
|
```tsx
|
|
1145
|
-
import { Sizer
|
|
1104
|
+
import { Sizer } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1146
1105
|
|
|
1147
|
-
// Simplest
|
|
1148
|
-
<Sizer
|
|
1106
|
+
// Simplest
|
|
1107
|
+
<Sizer>
|
|
1149
1108
|
{isExpanded && <YourExpandableContent />}
|
|
1150
1109
|
</Sizer>
|
|
1151
1110
|
|
|
1152
1111
|
// Animate in from zero height:
|
|
1153
|
-
<Sizer emitter={emitter}
|
|
1112
|
+
<Sizer emitter={emitter}>
|
|
1154
1113
|
<YourCard />
|
|
1155
1114
|
</Sizer>
|
|
1156
1115
|
|
|
1157
1116
|
// Manually trigger resize when content changes outside React state:
|
|
1158
|
-
const emitter = useMemo(() =>
|
|
1117
|
+
const emitter = useMemo(() => SkillViewEmitter.getInstance(), [])
|
|
1159
1118
|
const sizer = useRef<React.ElementRef<typeof Sizer>>(null)
|
|
1160
1119
|
|
|
1161
|
-
<Sizer
|
|
1120
|
+
<Sizer ref={sizer} emitter={emitter}>
|
|
1162
1121
|
<YourDynamicContent
|
|
1163
1122
|
onContentChange={async () => {
|
|
1164
1123
|
if (sizer.current?.resize()) {
|
|
@@ -1169,7 +1128,7 @@ const sizer = useRef<React.ElementRef<typeof Sizer>>(null)
|
|
|
1169
1128
|
</Sizer>
|
|
1170
1129
|
|
|
1171
1130
|
// Staggered entrance with queueShow:
|
|
1172
|
-
<Sizer emitter={emitter}
|
|
1131
|
+
<Sizer emitter={emitter}>
|
|
1173
1132
|
<div className="your-item hidden" ref={(ref) => { ref && queueShow(ref) }}>
|
|
1174
1133
|
content
|
|
1175
1134
|
</div>
|
|
@@ -1204,9 +1163,9 @@ When `isEnabled={false}`, children render inline with no wrapper.
|
|
|
1204
1163
|
2. **Wrap your content** — `isEnabled`, `className`, and `isFocused` are all required:
|
|
1205
1164
|
|
|
1206
1165
|
```tsx
|
|
1207
|
-
import { DelayedPlacer
|
|
1166
|
+
import { DelayedPlacer } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1208
1167
|
|
|
1209
|
-
const emitter = useMemo(() =>
|
|
1168
|
+
const emitter = useMemo(() => SkillViewEmitter.getInstance(), [])
|
|
1210
1169
|
|
|
1211
1170
|
<DelayedPlacer
|
|
1212
1171
|
className="placer__card"
|
|
@@ -1224,7 +1183,7 @@ When `isEnabled={false}`, children render inline with no wrapper.
|
|
|
1224
1183
|
|
|
1225
1184
|
```tsx
|
|
1226
1185
|
<DelayedPlacer className="placer__card" isEnabled emitter={emitter} isFocused={isFocused}>
|
|
1227
|
-
<Sizer emitter={emitter}
|
|
1186
|
+
<Sizer emitter={emitter}>
|
|
1228
1187
|
<YourContent />
|
|
1229
1188
|
</Sizer>
|
|
1230
1189
|
</DelayedPlacer>
|
|
@@ -1246,11 +1205,6 @@ interface DelayedPlacerProps {
|
|
|
1246
1205
|
|
|
1247
1206
|
// Event emitter. Listens for layout changes to re-measure and re-place.
|
|
1248
1207
|
emitter?: AnimationEmitter
|
|
1249
|
-
|
|
1250
|
-
// Required. Return true when this skill view is focused and in the foreground.
|
|
1251
|
-
// Placement is skipped when false to avoid measuring off-screen content.
|
|
1252
|
-
// Pass () => true for standalone use outside Heartwood.
|
|
1253
|
-
isFocused: () => boolean
|
|
1254
1208
|
}
|
|
1255
1209
|
```
|
|
1256
1210
|
|
|
@@ -1260,6 +1214,18 @@ interface DelayedPlacerProps {
|
|
|
1260
1214
|
delayedPlacer.current?.placeRightAway(): void // re-measure and re-place immediately
|
|
1261
1215
|
```
|
|
1262
1216
|
|
|
1217
|
+
#### Placer & Sizer Together For Card Placement
|
|
1218
|
+
|
|
1219
|
+
When using `DelayedPlacer` and `Sizer` together, the typical pattern is to place `Sizer` inside `DelayedPlacer` so that height changes trigger placement updates:
|
|
1220
|
+
|
|
1221
|
+
```tsx
|
|
1222
|
+
<DelayedPlacer className="placer" isEnabled emitter={emitter} isFocused={isFocused}>
|
|
1223
|
+
<Sizer emitter={emitter}>
|
|
1224
|
+
<YourCard />
|
|
1225
|
+
</Sizer>
|
|
1226
|
+
</DelayedPlacer>
|
|
1227
|
+
```
|
|
1228
|
+
|
|
1263
1229
|
#### Emitter Events
|
|
1264
1230
|
|
|
1265
1231
|
| Event | Direction | Meaning |
|
|
@@ -1274,11 +1240,11 @@ delayedPlacer.current?.placeRightAway(): void // re-measure and re-place immedi
|
|
|
1274
1240
|
|
|
1275
1241
|
```tsx
|
|
1276
1242
|
import React, { useRef, useMemo } from 'react'
|
|
1277
|
-
import { DelayedPlacer
|
|
1243
|
+
import { DelayedPlacer } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1278
1244
|
|
|
1279
1245
|
function YourPanel({ isFocused }: { isFocused: () => boolean }) {
|
|
1280
1246
|
const placerRef = useRef<React.ElementRef<typeof DelayedPlacer>>(null)
|
|
1281
|
-
const emitter = useMemo(() =>
|
|
1247
|
+
const emitter = useMemo(() => SkillViewEmitter.getInstance(), [])
|
|
1282
1248
|
|
|
1283
1249
|
return (
|
|
1284
1250
|
<DelayedPlacer
|
|
@@ -1365,11 +1331,11 @@ A complete example showing all three systems working together in a card componen
|
|
|
1365
1331
|
```tsx
|
|
1366
1332
|
import React, { useMemo } from 'react'
|
|
1367
1333
|
import {
|
|
1368
|
-
Sizer, DelayedPlacer, queueShow, Settings,
|
|
1334
|
+
Sizer, DelayedPlacer, queueShow, Settings,
|
|
1369
1335
|
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1370
1336
|
|
|
1371
1337
|
function YourCard({ isPlaced, isFocused }: { isPlaced: boolean; isFocused: () => boolean }) {
|
|
1372
|
-
const emitter = useMemo(() =>
|
|
1338
|
+
const emitter = useMemo(() => SkillViewEmitter.getInstance(), [])
|
|
1373
1339
|
|
|
1374
1340
|
return (
|
|
1375
1341
|
<DelayedPlacer
|
|
@@ -1378,7 +1344,7 @@ function YourCard({ isPlaced, isFocused }: { isPlaced: boolean; isFocused: () =>
|
|
|
1378
1344
|
emitter={emitter}
|
|
1379
1345
|
isFocused={isFocused}
|
|
1380
1346
|
>
|
|
1381
|
-
<Sizer emitter={emitter}
|
|
1347
|
+
<Sizer emitter={emitter}>
|
|
1382
1348
|
<div
|
|
1383
1349
|
className="card hidden"
|
|
1384
1350
|
ref={(ref) => { ref && queueShow(ref) }}
|
|
@@ -6,4 +6,3 @@ export type { DelayedPlacerProps } from './DelayedPlacerExport';
|
|
|
6
6
|
export { default as sizeUtil } from '../calendars/utils/size.utility';
|
|
7
7
|
export { default as Settings } from '../Settings';
|
|
8
8
|
export type { AnimationEmitter } from './types';
|
|
9
|
-
export { default as SimpleEmitter } from './SimpleEmitter';
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.Settings = exports.sizeUtil = exports.DelayedPlacer = exports.Sizer = exports.stopQueue = exports.clearPendingShowAndQueueHide = exports.clearPendingHideAndQueueShow = exports.callbackImmediately = exports.queueCallback = exports.queueHide = exports.queueShow = exports.hideRightAway = exports.showRightAway = exports.useShowNow = exports.useQueueShow = void 0;
|
|
7
7
|
// Queue system — all functions and hooks
|
|
8
8
|
var queueShow_1 = require("../../hooks/queueShow");
|
|
9
9
|
Object.defineProperty(exports, "useQueueShow", { enumerable: true, get: function () { return queueShow_1.useQueueShow; } });
|
|
@@ -27,6 +27,4 @@ var size_utility_1 = require("../calendars/utils/size.utility");
|
|
|
27
27
|
Object.defineProperty(exports, "sizeUtil", { enumerable: true, get: function () { return __importDefault(size_utility_1).default; } });
|
|
28
28
|
var Settings_1 = require("../Settings");
|
|
29
29
|
Object.defineProperty(exports, "Settings", { enumerable: true, get: function () { return __importDefault(Settings_1).default; } });
|
|
30
|
-
var SimpleEmitter_1 = require("./SimpleEmitter");
|
|
31
|
-
Object.defineProperty(exports, "SimpleEmitter", { enumerable: true, get: function () { return __importDefault(SimpleEmitter_1).default; } });
|
|
32
30
|
//# sourceMappingURL=index.js.map
|
|
@@ -6,4 +6,3 @@ export type { DelayedPlacerProps } from './DelayedPlacerExport';
|
|
|
6
6
|
export { default as sizeUtil } from '../calendars/utils/size.utility';
|
|
7
7
|
export { default as Settings } from '../Settings';
|
|
8
8
|
export type { AnimationEmitter } from './types';
|
|
9
|
-
export { default as SimpleEmitter } from './SimpleEmitter';
|
|
@@ -6,4 +6,3 @@ export { default as DelayedPlacer } from './DelayedPlacerExport.js';
|
|
|
6
6
|
// Utilities (useful for consumers who want to measure DOM nodes)
|
|
7
7
|
export { default as sizeUtil } from '../calendars/utils/size.utility.js';
|
|
8
8
|
export { default as Settings } from '../Settings.js';
|
|
9
|
-
export { default as SimpleEmitter } from './SimpleEmitter.js';
|