@sprucelabs/spruce-heartwood-utils 38.17.0 → 38.17.2
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 +31 -37
- package/build/components/animation/SimpleEmitter.d.ts +7 -0
- package/build/components/animation/SimpleEmitter.js +22 -0
- package/build/components/animation/index.d.ts +1 -0
- package/build/components/animation/index.js +3 -1
- package/build/esm/components/animation/SimpleEmitter.d.ts +7 -0
- package/build/esm/components/animation/SimpleEmitter.js +21 -0
- package/build/esm/components/animation/index.d.ts +1 -0
- package/build/esm/components/animation/index.js +1 -0
- package/docs/animation/animation-emitter.md +7 -22
- package/docs/animation/sizer.md +5 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -576,26 +576,43 @@ import {
|
|
|
576
576
|
|
|
577
577
|
### Required CSS
|
|
578
578
|
|
|
579
|
+
> **⚠ Silent failure warning:** If CSS transitions are missing, animations will not play and no error will be thrown. Elements will simply snap instantly between states with no visual feedback. Every class listed below requires its own `transition` declaration — the system provides none.
|
|
580
|
+
|
|
579
581
|
**Inside a Heartwood skill view:** all required CSS is already provided by Heartwood's stylesheet. No extra setup needed.
|
|
580
582
|
|
|
581
|
-
**Outside Heartwood (standalone use):** supply
|
|
583
|
+
**Outside Heartwood (standalone use):** supply every rule below yourself. Missing any one of them causes that animation to silently skip.
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
#### `queueShow` / `queueHide` — fade + slide transitions
|
|
582
588
|
|
|
583
|
-
|
|
589
|
+
`queueShow` removes the `hidden` class; `queueHide` adds it back. The `hidden` class sets the hidden state (opacity, position offset, pointer-events). **The transition must be declared on the element itself, not on `.hidden`** — if the transition is on `.hidden` it is removed at the same moment the class is removed and the browser never interpolates.
|
|
584
590
|
|
|
585
591
|
```css
|
|
592
|
+
/* Hidden state — sets opacity, nudge, and pointer-events */
|
|
586
593
|
.hidden {
|
|
587
594
|
opacity: 0;
|
|
588
595
|
transform: translateY(4px);
|
|
589
596
|
pointer-events: none;
|
|
590
597
|
}
|
|
591
598
|
|
|
592
|
-
/* Transition
|
|
599
|
+
/* Transition on the element — this is what the browser animates */
|
|
593
600
|
.your-element {
|
|
594
601
|
transition: opacity 200ms ease, transform 200ms ease;
|
|
595
602
|
}
|
|
596
603
|
```
|
|
597
604
|
|
|
598
|
-
`
|
|
605
|
+
Elements must also start with `hidden` in their `className` so they are invisible until `queueShow` fires:
|
|
606
|
+
|
|
607
|
+
```tsx
|
|
608
|
+
<div className="your-element hidden" ref={(ref) => { ref && queueShow(ref) }} />
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
#### `Sizer` — animated height
|
|
614
|
+
|
|
615
|
+
`Sizer` measures its content and writes `style.height` directly. The CSS `transition` on `.sizer` is what makes the height change animate smoothly. Without it the height jumps instantly.
|
|
599
616
|
|
|
600
617
|
```css
|
|
601
618
|
.sizer {
|
|
@@ -604,7 +621,11 @@ The `queueShow` system toggles a `hidden` CSS class. Elements must start with `h
|
|
|
604
621
|
}
|
|
605
622
|
```
|
|
606
623
|
|
|
607
|
-
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
#### `DelayedPlacer` — absolute positioning
|
|
627
|
+
|
|
628
|
+
`DelayedPlacer` writes `style.left` / `style.top` on the child. The `.placer` wrapper must be `position: relative` so the child's absolute coordinates are relative to it.
|
|
608
629
|
|
|
609
630
|
```css
|
|
610
631
|
.placer {
|
|
@@ -615,6 +636,8 @@ The `queueShow` system toggles a `hidden` CSS class. Elements must start with `h
|
|
|
615
636
|
}
|
|
616
637
|
```
|
|
617
638
|
|
|
639
|
+
No transition is needed on `.placer` itself — placement jumps immediately to the measured position.
|
|
640
|
+
|
|
618
641
|
---
|
|
619
642
|
|
|
620
643
|
### System Architecture
|
|
@@ -659,28 +682,6 @@ interface AnimationEmitter {
|
|
|
659
682
|
| `did-change-orientation` | DelayedPlacer | your code | Portrait ↔ landscape switch |
|
|
660
683
|
| `did-place-cards` | your listeners | DelayedPlacer | Placement calculation finished |
|
|
661
684
|
|
|
662
|
-
**Minimal implementation:**
|
|
663
|
-
|
|
664
|
-
```ts
|
|
665
|
-
class SimpleEmitter implements AnimationEmitter {
|
|
666
|
-
private listeners: Record<string, Array<() => void>> = {}
|
|
667
|
-
|
|
668
|
-
on(event: string, handler: () => void): void {
|
|
669
|
-
;(this.listeners[event] ??= []).push(handler)
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
off(event: string, handler: () => void): void {
|
|
673
|
-
this.listeners[event] = (this.listeners[event] ?? []).filter(
|
|
674
|
-
(h) => h !== handler
|
|
675
|
-
)
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
emit(event: string): void {
|
|
679
|
-
for (const h of this.listeners[event] ?? []) h()
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
```
|
|
683
|
-
|
|
684
685
|
> Pass the **same function reference** to `on` and `off`. Inline arrow functions cannot be removed with `off`.
|
|
685
686
|
|
|
686
687
|
---
|
|
@@ -928,6 +929,8 @@ sizer.current?.resize(): boolean // measure and apply new height; returns tru
|
|
|
928
929
|
#### Usage
|
|
929
930
|
|
|
930
931
|
```tsx
|
|
932
|
+
import { Sizer, SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
933
|
+
|
|
931
934
|
// Simplest — just clip overflow during height transition:
|
|
932
935
|
<Sizer shouldHideOverflow>
|
|
933
936
|
{isExpanded && <YourExpandableContent />}
|
|
@@ -1096,18 +1099,9 @@ A complete example showing all three systems working together in a card componen
|
|
|
1096
1099
|
|
|
1097
1100
|
```tsx
|
|
1098
1101
|
import {
|
|
1099
|
-
Sizer, DelayedPlacer, queueShow, Settings,
|
|
1102
|
+
Sizer, DelayedPlacer, queueShow, Settings, SimpleEmitter,
|
|
1100
1103
|
} from '@sprucelabs/spruce-heartwood-utils'
|
|
1101
1104
|
|
|
1102
|
-
class SimpleEmitter implements AnimationEmitter {
|
|
1103
|
-
private listeners: Record<string, Array<() => void>> = {}
|
|
1104
|
-
on(event: string, handler: () => void) { (this.listeners[event] ??= []).push(handler) }
|
|
1105
|
-
off(event: string, handler: () => void) {
|
|
1106
|
-
this.listeners[event] = (this.listeners[event] ?? []).filter(h => h !== handler)
|
|
1107
|
-
}
|
|
1108
|
-
emit(event: string) { for (const h of this.listeners[event] ?? []) h() }
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
1105
|
function YourCard({ isPlaced, isFocused }: { isPlaced: boolean; isFocused: () => boolean }) {
|
|
1112
1106
|
const emitter = useMemo(() => new SimpleEmitter(), [])
|
|
1113
1107
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class SimpleEmitter {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.listeners = {};
|
|
6
|
+
}
|
|
7
|
+
on(event, handler) {
|
|
8
|
+
var _a;
|
|
9
|
+
;
|
|
10
|
+
((_a = this.listeners)[event] ?? (_a[event] = [])).push(handler);
|
|
11
|
+
}
|
|
12
|
+
off(event, handler) {
|
|
13
|
+
this.listeners[event] = (this.listeners[event] ?? []).filter((h) => h !== handler);
|
|
14
|
+
}
|
|
15
|
+
emit(event) {
|
|
16
|
+
for (const h of this.listeners[event] ?? []) {
|
|
17
|
+
h();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.default = SimpleEmitter;
|
|
22
|
+
//# sourceMappingURL=SimpleEmitter.js.map
|
|
@@ -6,3 +6,4 @@ 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.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;
|
|
6
|
+
exports.SimpleEmitter = 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,4 +27,6 @@ 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; } });
|
|
30
32
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export default class SimpleEmitter {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.listeners = {};
|
|
4
|
+
}
|
|
5
|
+
on(event, handler) {
|
|
6
|
+
var _a;
|
|
7
|
+
var _b;
|
|
8
|
+
;
|
|
9
|
+
((_a = (_b = this.listeners)[event]) !== null && _a !== void 0 ? _a : (_b[event] = [])).push(handler);
|
|
10
|
+
}
|
|
11
|
+
off(event, handler) {
|
|
12
|
+
var _a;
|
|
13
|
+
this.listeners[event] = ((_a = this.listeners[event]) !== null && _a !== void 0 ? _a : []).filter((h) => h !== handler);
|
|
14
|
+
}
|
|
15
|
+
emit(event) {
|
|
16
|
+
var _a;
|
|
17
|
+
for (const h of (_a = this.listeners[event]) !== null && _a !== void 0 ? _a : []) {
|
|
18
|
+
h();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -6,3 +6,4 @@ 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,3 +6,4 @@ 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';
|
|
@@ -29,33 +29,18 @@ All three methods accept a plain string event name and return either `void` or `
|
|
|
29
29
|
| `did-change-orientation` | DelayedPlacer | Device orientation handler | Portrait ↔ landscape switch |
|
|
30
30
|
| `did-place-cards` | (external listeners) | DelayedPlacer | Placement calculation complete |
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## Using SimpleEmitter
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
`SimpleEmitter` is exported from the package — import it directly instead of building your own:
|
|
35
35
|
|
|
36
36
|
```ts
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
on(event: string, handler: () => void): void {
|
|
42
|
-
;(this.listeners[event] ??= []).push(handler)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
off(event: string, handler: () => void): void {
|
|
46
|
-
this.listeners[event] = (this.listeners[event] ?? []).filter(
|
|
47
|
-
(h) => h !== handler
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
emit(event: string): void {
|
|
52
|
-
for (const h of this.listeners[event] ?? []) {
|
|
53
|
-
h()
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
37
|
+
import { SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
38
|
+
|
|
39
|
+
const emitter = new SimpleEmitter()
|
|
57
40
|
```
|
|
58
41
|
|
|
42
|
+
It implements `AnimationEmitter` with a plain in-memory listener map. Use it for standalone components, isolated tests, or any context where the full `SkillViewEmitter` is unnecessary.
|
|
43
|
+
|
|
59
44
|
## Noop Emitter
|
|
60
45
|
|
|
61
46
|
`SizerExport.tsx` defines and uses a noop emitter as the default when no emitter is passed:
|
package/docs/animation/sizer.md
CHANGED
|
@@ -104,7 +104,9 @@ handleSizing() (debounced at sizerDelayMs, default 250ms)
|
|
|
104
104
|
### With emitter — responds to external layout events
|
|
105
105
|
|
|
106
106
|
```tsx
|
|
107
|
-
|
|
107
|
+
import { SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
108
|
+
|
|
109
|
+
const [emitter] = useState(() => new SimpleEmitter())
|
|
108
110
|
|
|
109
111
|
<Sizer emitter={emitter} shouldStartAtZero>
|
|
110
112
|
<MyContent />
|
|
@@ -114,6 +116,8 @@ const [emitter] = useState(() => createMyEmitter())
|
|
|
114
116
|
### With ref — manually drive resize on content change
|
|
115
117
|
|
|
116
118
|
```tsx
|
|
119
|
+
import { SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
120
|
+
|
|
117
121
|
const emitter = useMemo(() => new SimpleEmitter(), [])
|
|
118
122
|
const sizer = useRef<React.ElementRef<typeof Sizer>>(null)
|
|
119
123
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sprucelabs/spruce-heartwood-utils",
|
|
3
3
|
"description": "Heartwood Utilities",
|
|
4
|
-
"version": "38.17.
|
|
4
|
+
"version": "38.17.2",
|
|
5
5
|
"skill": {
|
|
6
6
|
"namespace": "heartwood"
|
|
7
7
|
},
|
|
@@ -86,6 +86,10 @@
|
|
|
86
86
|
"build/components/animation/types.d.ts",
|
|
87
87
|
"build/esm/components/animation/types.js",
|
|
88
88
|
"build/esm/components/animation/types.d.ts",
|
|
89
|
+
"build/components/animation/SimpleEmitter.js",
|
|
90
|
+
"build/components/animation/SimpleEmitter.d.ts",
|
|
91
|
+
"build/esm/components/animation/SimpleEmitter.js",
|
|
92
|
+
"build/esm/components/animation/SimpleEmitter.d.ts",
|
|
89
93
|
"build/hooks/queueShow.js",
|
|
90
94
|
"build/hooks/queueShow.d.ts",
|
|
91
95
|
"build/esm/hooks/queueShow.js",
|