@shelamkoff/rector 1.0.2 → 1.0.4

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.
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @typedef {Object} GalleryMasonryOptions
3
+ * @property {AbortSignal} [signal]
4
+ * @property {number} [columnWidth]
5
+ * @property {number} [transitionDuration]
6
+ * @property {number} [fadeInDuration]
7
+ * @property {number} [contentLoadTimeout]
8
+ * @property {(error: unknown) => void} [onError]
9
+ */
10
+ /**
11
+ * Mount a Masonry instance once its container is connected and measurable.
12
+ *
13
+ * Editor plugins and renderers both create their DOM before the host inserts
14
+ * it into the document. Waiting for a non-zero box avoids a failed first
15
+ * layout and keeps the two rendering paths on the same implementation.
16
+ *
17
+ * @param {HTMLElement} container
18
+ * @param {HTMLElement[]} elements
19
+ * @param {GalleryMasonryOptions} [options]
20
+ */
21
+ export function mountGalleryMasonry(container: HTMLElement, elements: HTMLElement[], options?: GalleryMasonryOptions): {
22
+ ready: Promise<any>;
23
+ destroy: () => void;
24
+ readonly instance: Masonry;
25
+ };
26
+ export type GalleryMasonryOptions = {
27
+ signal?: AbortSignal;
28
+ columnWidth?: number;
29
+ transitionDuration?: number;
30
+ fadeInDuration?: number;
31
+ contentLoadTimeout?: number;
32
+ onError?: (error: unknown) => void;
33
+ };
34
+ import { Masonry } from '@shelamkoff/masonry';
@@ -0,0 +1,152 @@
1
+ // @ts-check
2
+ import { Masonry } from '@shelamkoff/masonry'
3
+
4
+ /**
5
+ * @typedef {Object} GalleryMasonryOptions
6
+ * @property {AbortSignal} [signal]
7
+ * @property {number} [columnWidth]
8
+ * @property {number} [transitionDuration]
9
+ * @property {number} [fadeInDuration]
10
+ * @property {number} [contentLoadTimeout]
11
+ * @property {(error: unknown) => void} [onError]
12
+ */
13
+
14
+ /**
15
+ * Mount a Masonry instance once its container is connected and measurable.
16
+ *
17
+ * Editor plugins and renderers both create their DOM before the host inserts
18
+ * it into the document. Waiting for a non-zero box avoids a failed first
19
+ * layout and keeps the two rendering paths on the same implementation.
20
+ *
21
+ * @param {HTMLElement} container
22
+ * @param {HTMLElement[]} elements
23
+ * @param {GalleryMasonryOptions} [options]
24
+ */
25
+ export function mountGalleryMasonry(container, elements, options = {}) {
26
+ const {
27
+ signal,
28
+ columnWidth = 240,
29
+ transitionDuration = 200,
30
+ fadeInDuration = 180,
31
+ contentLoadTimeout = 10_000,
32
+ onError,
33
+ } = options
34
+
35
+ /** @type {Masonry | null} */
36
+ let instance = null
37
+ /** @type {ResizeObserver | null} */
38
+ let waitingObserver = null
39
+ /** @type {number | null} */
40
+ let frameId = null
41
+ let destroyed = false
42
+ let starting = false
43
+ let settled = false
44
+ /** @type {(value: Masonry | null) => void} */
45
+ let resolveReady = () => {}
46
+
47
+ const ready = new Promise((resolve) => {
48
+ resolveReady = resolve
49
+ })
50
+
51
+ const settle = (/** @type {Masonry | null} */ value) => {
52
+ if (settled) return
53
+ settled = true
54
+ resolveReady(value)
55
+ }
56
+
57
+ const stopWaiting = () => {
58
+ waitingObserver?.disconnect()
59
+ waitingObserver = null
60
+ if (frameId !== null && typeof cancelAnimationFrame === 'function') {
61
+ cancelAnimationFrame(frameId)
62
+ }
63
+ frameId = null
64
+ }
65
+
66
+ const readGap = () => {
67
+ const style = getComputedStyle(container)
68
+ const value = Number.parseFloat(style.columnGap || style.gap)
69
+ return Number.isFinite(value) && value >= 0 ? value : 4
70
+ }
71
+
72
+ const start = async () => {
73
+ if (destroyed || starting || settled) return
74
+ if (!container.isConnected || container.getBoundingClientRect().width <= 0) return
75
+
76
+ starting = true
77
+ stopWaiting()
78
+
79
+ try {
80
+ instance = new Masonry(container, {
81
+ columnWidth,
82
+ layoutMode: 'masonry',
83
+ autoColSpan: false,
84
+ gap: readGap(),
85
+ transitionDuration,
86
+ fadeInDuration,
87
+ contentLoadTimeout,
88
+ })
89
+ await instance.append(elements.map((element, index) => ({
90
+ id: `gallery-item-${index}`,
91
+ element,
92
+ colSpan: 1,
93
+ meta: { index },
94
+ })))
95
+
96
+ if (destroyed) {
97
+ instance.destroy()
98
+ instance = null
99
+ settle(null)
100
+ return
101
+ }
102
+ settle(instance)
103
+ } catch (error) {
104
+ instance?.destroy()
105
+ instance = null
106
+ if (!destroyed) {
107
+ container.classList.add('eg--masonry-fallback')
108
+ onError?.(error)
109
+ }
110
+ settle(null)
111
+ }
112
+ }
113
+
114
+ const destroy = () => {
115
+ if (destroyed) return
116
+ destroyed = true
117
+ stopWaiting()
118
+ instance?.destroy()
119
+ instance = null
120
+ signal?.removeEventListener('abort', destroy)
121
+ settle(null)
122
+ }
123
+
124
+ if (signal?.aborted) {
125
+ destroy()
126
+ } else {
127
+ signal?.addEventListener('abort', destroy, { once: true })
128
+ if (typeof ResizeObserver === 'function') {
129
+ waitingObserver = new ResizeObserver(() => {
130
+ void start()
131
+ })
132
+ waitingObserver.observe(container)
133
+ }
134
+ if (typeof requestAnimationFrame === 'function') {
135
+ frameId = requestAnimationFrame(() => {
136
+ frameId = null
137
+ void start()
138
+ })
139
+ }
140
+ queueMicrotask(() => {
141
+ void start()
142
+ })
143
+ }
144
+
145
+ return {
146
+ ready,
147
+ destroy,
148
+ get instance() {
149
+ return instance
150
+ },
151
+ }
152
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shelamkoff/rector",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Extensible browser-native block editor with atomic history, versioned JSON, and document rendering",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -107,11 +107,12 @@
107
107
  },
108
108
  "dependencies": {
109
109
  "@shelamkoff/carousel": "^1.0.0",
110
- "@shelamkoff/color-picker": "^1.0.0",
111
- "@shelamkoff/event-bus": "^1.0.0",
112
- "@shelamkoff/cropper": "^1.0.0",
113
- "@shelamkoff/expose": "^1.0.0"
114
- },
110
+ "@shelamkoff/color-picker": "^1.0.0",
111
+ "@shelamkoff/event-bus": "^1.0.0",
112
+ "@shelamkoff/cropper": "^1.0.0",
113
+ "@shelamkoff/expose": "^1.0.0",
114
+ "@shelamkoff/masonry": "^1.0.0"
115
+ },
115
116
  "devDependencies": {
116
117
  "@tabler/icons-vue": "^3.41.1",
117
118
  "typescript": "~5.9.3",