@surdeddd/wmkit 0.1.1 → 0.3.0
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 +58 -12
- package/README.ru.md +21 -12
- package/dist/angular.cjs +52 -0
- package/dist/angular.cjs.map +1 -0
- package/dist/angular.d.cts +15 -0
- package/dist/angular.d.ts +15 -0
- package/dist/angular.js +47 -0
- package/dist/angular.js.map +1 -0
- package/dist/{controller-1uedRvQw.d.ts → binder-0Oku22Nc.d.ts} +21 -3
- package/dist/{controller-BfUF1k04.d.cts → binder-Dq_AZRbo.d.cts} +21 -3
- package/dist/{chunk-TXINVEK3.cjs → chunk-B35DANTX.cjs} +596 -241
- package/dist/chunk-B35DANTX.cjs.map +1 -0
- package/dist/{chunk-N5KNWRKU.js → chunk-YCHJXSTC.js} +596 -242
- package/dist/chunk-YCHJXSTC.js.map +1 -0
- package/dist/index.cjs +19 -15
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +1 -1
- package/dist/persist.cjs +1 -1
- package/dist/persist.cjs.map +1 -1
- package/dist/persist.d.cts +1 -1
- package/dist/persist.d.ts +1 -1
- package/dist/persist.js +1 -1
- package/dist/persist.js.map +1 -1
- package/dist/popout.cjs +1 -1
- package/dist/popout.cjs.map +1 -1
- package/dist/popout.d.cts +1 -1
- package/dist/popout.d.ts +1 -1
- package/dist/popout.js +1 -1
- package/dist/popout.js.map +1 -1
- package/dist/react.cjs +3 -3
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/solid.cjs +3 -3
- package/dist/solid.cjs.map +1 -1
- package/dist/solid.d.cts +2 -2
- package/dist/solid.d.ts +2 -2
- package/dist/solid.js +1 -1
- package/dist/solid.js.map +1 -1
- package/dist/svelte.cjs +4 -4
- package/dist/svelte.cjs.map +1 -1
- package/dist/svelte.d.cts +2 -2
- package/dist/svelte.d.ts +2 -2
- package/dist/svelte.js +2 -2
- package/dist/svelte.js.map +1 -1
- package/dist/themes/glass.css +0 -1
- package/dist/themes/light.css +202 -0
- package/dist/themes/retro.css +152 -0
- package/dist/{types-BYx7gCuT.d.cts → types-DdCtDHgt.d.cts} +23 -1
- package/dist/{types-BYx7gCuT.d.ts → types-DdCtDHgt.d.ts} +23 -1
- package/dist/vue.cjs +3 -3
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.cts +2 -2
- package/dist/vue.d.ts +2 -2
- package/dist/vue.js +1 -1
- package/dist/vue.js.map +1 -1
- package/package.json +34 -7
- package/dist/chunk-N5KNWRKU.js.map +0 -1
- package/dist/chunk-TXINVEK3.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -14,13 +14,17 @@
|
|
|
14
14
|
|
|
15
15
|
- 🪟 **Full window lifecycle** — open, close, focus, minimize, maximize, restore, drag, 8-direction resize
|
|
16
16
|
- 🧠 **Headless core** — a serializable state machine plus a DOM controller; bring your own markup or use the glass theme
|
|
17
|
-
- ⚛️ **Official adapters** — `@surdeddd/wmkit/react`,
|
|
17
|
+
- ⚛️ **Official adapters** — `@surdeddd/wmkit/react`, `/vue`, `/svelte`, `/solid`, `/angular`, all thin sugar over one core
|
|
18
18
|
- ⊞ **Snap zones** — halves, quarters and drag-to-top maximize with a live preview
|
|
19
|
+
- 🧲 **Magnetism** — window edges align to neighbours and the viewport while dragging
|
|
20
|
+
- ↩️ **Undo/redo** — every mutation is one step; a whole drag collapses into a single history entry
|
|
21
|
+
- 🗂️ **Named layouts & arrange** — save/load desktop snapshots, `cascade`/`tile` in one call
|
|
19
22
|
- ⌨️ **Accessible** — keyboard move/resize, F6 window cycling, focus-trapped modals, `aria-live` announcements
|
|
20
23
|
- ⚡ **Fast** — `transform`-only positioning, rAF-batched pointer input, structural sharing; 50 windows drag at 60fps
|
|
21
24
|
- 💾 **Persistence** — one call to serialize the desktop, one call to restore it
|
|
25
|
+
- 🎨 **Three themes** — dark glass, light glass and Win98 retro, or bring your own CSS
|
|
22
26
|
- 🖼️ **Popout** *(experimental)* — send a window into Document Picture-in-Picture
|
|
23
|
-
- 📦 **Zero dependencies**, strict TypeScript, ESM + CJS, ~9 kB
|
|
27
|
+
- 📦 **Zero dependencies**, strict TypeScript, ESM + CJS, ~9.3 kB brotli core
|
|
24
28
|
|
|
25
29
|
## Install
|
|
26
30
|
|
|
@@ -54,13 +58,7 @@ el.innerHTML = `
|
|
|
54
58
|
<div data-wm-content>Anything you want.</div>
|
|
55
59
|
`
|
|
56
60
|
document.querySelector('#desktop').append(el)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
wm.on('close', ({ window: closed }) => {
|
|
60
|
-
if (closed.id !== win.id) return
|
|
61
|
-
detach()
|
|
62
|
-
el.remove()
|
|
63
|
-
})
|
|
61
|
+
desktop.attachWindow(win.id, el, { removeOnClose: true })
|
|
64
62
|
```
|
|
65
63
|
|
|
66
64
|
The desktop element becomes the coordinate space. Your markup stays yours — wmkit wires behavior onto `data-wm-*` attributes:
|
|
@@ -72,6 +70,8 @@ The desktop element becomes the coordinate space. Your markup stays yours — wm
|
|
|
72
70
|
| `data-wm-close` / `data-wm-minimize` / `data-wm-maximize` | control buttons, wired by delegation |
|
|
73
71
|
| `data-wm-content` | scrollable content area (styled by themes) |
|
|
74
72
|
|
|
73
|
+
`removeOnClose` detaches the controller and removes the element when the window closes. Touch devices get larger resize hit areas and snap thresholds automatically (`pointer: coarse`); tune via `attachDesktop(wm, el, { hitAreas: { edge, corner } })`.
|
|
74
|
+
|
|
75
75
|
The controller adds resize handles (`[data-wm-resize]`), a snap preview (`[data-wm-snap-preview]`) and a visually hidden live region for screen readers.
|
|
76
76
|
|
|
77
77
|
## React
|
|
@@ -194,6 +194,41 @@ function Desktop() {
|
|
|
194
194
|
}
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
+
## Angular
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'
|
|
201
|
+
import { useWindowManager, createDesktop, useWmState } from '@surdeddd/wmkit/angular'
|
|
202
|
+
|
|
203
|
+
@Component({
|
|
204
|
+
selector: 'app-desktop',
|
|
205
|
+
standalone: true,
|
|
206
|
+
template: `
|
|
207
|
+
<div #desktop style="position: relative; height: 100vh">
|
|
208
|
+
<section #hello>
|
|
209
|
+
<header data-wm-drag><span data-wm-title>Hello</span></header>
|
|
210
|
+
<div data-wm-content>signals inside</div>
|
|
211
|
+
</section>
|
|
212
|
+
</div>
|
|
213
|
+
`,
|
|
214
|
+
})
|
|
215
|
+
export class DesktopComponent implements AfterViewInit {
|
|
216
|
+
wm = useWindowManager()
|
|
217
|
+
dk = createDesktop(this.wm)
|
|
218
|
+
state = useWmState(this.wm)
|
|
219
|
+
@ViewChild('desktop') desktopRef!: ElementRef<HTMLElement>
|
|
220
|
+
@ViewChild('hello') helloRef!: ElementRef<HTMLElement>
|
|
221
|
+
|
|
222
|
+
ngAfterViewInit(): void {
|
|
223
|
+
this.wm.open({ id: 'hello', title: 'Hello' })
|
|
224
|
+
this.dk.desktop(this.desktopRef.nativeElement)
|
|
225
|
+
this.dk.window('hello')(this.helloRef.nativeElement)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`useWmState` returns a read-only `Signal<ManagerState>` and `useWmWindow(wm, id)` a computed per-window signal, so templates track updates fine-grained. Hooks called in an injection context clean up through `DestroyRef` automatically; outside one they simply skip auto-cleanup.
|
|
231
|
+
|
|
197
232
|
## Snap zones in action
|
|
198
233
|
|
|
199
234
|
[](https://surdeddd.github.io/wmkit/)
|
|
@@ -215,6 +250,7 @@ interface ManagerOptions {
|
|
|
215
250
|
cascadeOffset?: number // auto-position step for new windows (default 32)
|
|
216
251
|
cascadeOrigin?: { x: number; y: number }
|
|
217
252
|
idPrefix?: string
|
|
253
|
+
historyLimit?: number // undo/redo depth (default 50, 0 disables history)
|
|
218
254
|
}
|
|
219
255
|
```
|
|
220
256
|
|
|
@@ -232,6 +268,10 @@ Manager methods:
|
|
|
232
268
|
| `update(id, patch)` | title, layer, min/max size, per-window flags, `meta` |
|
|
233
269
|
| `setViewport(size)` | re-derives maximized/snapped bounds, clamps the rest |
|
|
234
270
|
| `serialize()` / `hydrate(data)` | JSON-safe snapshot of the whole desktop |
|
|
271
|
+
| `undo()` / `redo()` / `canUndo()` / `canRedo()` / `clearHistory()` | every mutation is one step; a whole drag or resize collapses into a single entry |
|
|
272
|
+
| `saveLayout(name)` / `loadLayout(name)` / `deleteLayout(name)` / `layoutNames()` | named desktop snapshots; `getLayout`/`setLayout` for external storage |
|
|
273
|
+
| `arrange('cascade' \| 'tile')` | cascade staggers restored sizes, tile fills the viewport in a grid |
|
|
274
|
+
| `minimizeAll()` / `restoreAll()` | bulk stage switches in one history step |
|
|
235
275
|
| `subscribe(fn)` / `on(event, fn)` | granular events: `open, close, focus, move, resize, stage, update, order, modalblocked` |
|
|
236
276
|
| `batch(fn)` | coalesce many operations into one `change` notification |
|
|
237
277
|
|
|
@@ -247,7 +287,10 @@ interface DesktopOptions {
|
|
|
247
287
|
keyboard?: boolean | { moveStep?: number; cycle?: boolean }
|
|
248
288
|
announce?: boolean | Partial<AnnouncerMessages> // localize screen-reader strings here
|
|
249
289
|
autoViewport?: boolean // ResizeObserver → wm.setViewport (default true)
|
|
290
|
+
magnetism?: boolean | { threshold?: number } // edge-align to neighbours + viewport while dragging (default on, 8 px / 12 px coarse)
|
|
291
|
+
hitAreas?: { edge?: number; corner?: number } // resize handle thickness (auto-doubles on touch)
|
|
250
292
|
minimizeTarget?: (win: WindowState) => Element | null // FLIP ghost target on minimize
|
|
293
|
+
onTitlebarContextMenu?: (win: WindowState, event: MouseEvent) => void // right-click / long-press hook for your own menu
|
|
251
294
|
}
|
|
252
295
|
```
|
|
253
296
|
|
|
@@ -281,6 +324,8 @@ Moves a window's content into a [Document Picture-in-Picture](https://developer.
|
|
|
281
324
|
}
|
|
282
325
|
```
|
|
283
326
|
|
|
327
|
+
Two more ready-made themes ship alongside: `themes/light.css` (light glass) and `themes/retro.css` (pixel-perfect Win98 nostalgia). All three style the same `data-wm-*` attributes, so switching is a one-line import swap.
|
|
328
|
+
|
|
284
329
|
Skip the import entirely and the library stays headless: state attributes (`data-wm-stage`, `data-wm-focused`, `data-wm-dragging`, `data-wm-flash`, `[hidden]`) are yours to style.
|
|
285
330
|
|
|
286
331
|
## SSR
|
|
@@ -293,7 +338,7 @@ The core never touches `window`/`document` — create managers and even `hydrate
|
|
|
293
338
|
| --- | --- | --- | --- | --- | --- |
|
|
294
339
|
| Maintained | ✓ 2026 | ✗ since 2023 | ✗ since 2022 | ✓ | ✓ |
|
|
295
340
|
| Headless core | ✓ | ✗ | ✗ | ~ own UI | ✓ |
|
|
296
|
-
| Official adapters | React·Vue·Svelte·Solid | community | ✗ | React·Vue·Angular | via Ark UI |
|
|
341
|
+
| Official adapters | React·Vue·Svelte·Solid·Angular | community | ✗ | React·Vue·Angular | via Ark UI |
|
|
297
342
|
| Multi-window (z-order, taskbar, modals) | ✓ | partial | partial | dock groups | ✗ single panel |
|
|
298
343
|
| Snap zones + preview | ✓ | ✗ | ✗ | — | ✗ |
|
|
299
344
|
| Keyboard + screen reader | ✓ | ✗ | ✗ | partial | partial |
|
|
@@ -305,8 +350,9 @@ The core never touches `window`/`document` — create managers and even `hydrate
|
|
|
305
350
|
|
|
306
351
|
## Quality
|
|
307
352
|
|
|
308
|
-
-
|
|
309
|
-
-
|
|
353
|
+
- 172 unit tests, **100%** line/branch/function/statement coverage on the core state machine and persistence
|
|
354
|
+
- 178+ Playwright scenarios on Chromium, WebKit and mobile emulation: drag, 8-way resize, snap, magnetism, undo after drag, keyboard, touch, persistence across reloads, 50-window stress, modal traps, axe accessibility scans, visual regression screenshots
|
|
355
|
+
- performance benchmarks run in CI on every push (`vitest bench`): 1 000 windows open in ~150 ms, a move among 50 windows costs ~1.2 µs, a full 100-step undo/redo sweep ~52 µs
|
|
310
356
|
- `publint` + `@arethetypeswrong/cli` validate the published package, `size-limit` guards bundle budgets
|
|
311
357
|
|
|
312
358
|
## Development
|
package/README.ru.md
CHANGED
|
@@ -10,13 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
- 🪟 **Полный жизненный цикл окна** — открытие, закрытие, фокус, сворачивание, разворачивание, восстановление, drag, ресайз в 8 направлениях
|
|
12
12
|
- 🧠 **Headless-ядро** — сериализуемая стейт-машина плюс DOM-контроллер; своя разметка или готовая стеклянная тема
|
|
13
|
-
- ⚛️ **Родные адаптеры** — `@surdeddd/wmkit/react`,
|
|
13
|
+
- ⚛️ **Родные адаптеры** — `@surdeddd/wmkit/react`, `/vue`, `/svelte`, `/solid`, `/angular`, тонкий сахар над одним ядром
|
|
14
14
|
- ⊞ **Snap-зоны** — половины, четверти и максимизация от верхнего края с живым превью
|
|
15
|
+
- 🧲 **Магнетизм** — края окна прилипают к соседям и вьюпорту при перетаскивании
|
|
16
|
+
- ↩️ **Undo/redo** — каждая мутация = один шаг; целый drag схлопывается в одну запись истории
|
|
17
|
+
- 🗂️ **Именованные layout'ы и arrange** — снапшоты рабочего стола, `cascade`/`tile` одним вызовом
|
|
15
18
|
- ⌨️ **Доступность** — move/resize с клавиатуры, цикл окон по F6, focus-trap в модалках, `aria-live`-анонсы
|
|
16
19
|
- ⚡ **Производительность** — позиционирование только через `transform`, rAF-батчинг ввода, structural sharing; 50 окон таскаются на 60fps
|
|
17
20
|
- 💾 **Персист** — один вызов сериализует рабочий стол, один — восстанавливает
|
|
21
|
+
- 🎨 **Три темы** — тёмное стекло, светлое стекло и Win98-ретро, либо полностью свой CSS
|
|
18
22
|
- 🖼️ **Popout** *(experimental)* — вынос окна в Document Picture-in-Picture
|
|
19
|
-
- 📦 **Ноль зависимостей**, строгий TypeScript, ESM + CJS, ~9 kB
|
|
23
|
+
- 📦 **Ноль зависимостей**, строгий TypeScript, ESM + CJS, ~9.3 kB brotli
|
|
20
24
|
|
|
21
25
|
## Установка
|
|
22
26
|
|
|
@@ -50,13 +54,7 @@ el.innerHTML = `
|
|
|
50
54
|
<div data-wm-content>Что угодно.</div>
|
|
51
55
|
`
|
|
52
56
|
document.querySelector('#desktop').append(el)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
wm.on('close', ({ window: closed }) => {
|
|
56
|
-
if (closed.id !== win.id) return
|
|
57
|
-
detach()
|
|
58
|
-
el.remove()
|
|
59
|
-
})
|
|
57
|
+
desktop.attachWindow(win.id, el, { removeOnClose: true })
|
|
60
58
|
```
|
|
61
59
|
|
|
62
60
|
Элемент рабочего стола становится системой координат. Разметка остаётся вашей — wmkit вешает поведение на `data-wm-*` атрибуты:
|
|
@@ -68,11 +66,13 @@ wm.on('close', ({ window: closed }) => {
|
|
|
68
66
|
| `data-wm-close` / `data-wm-minimize` / `data-wm-maximize` | кнопки управления, работают через делегирование |
|
|
69
67
|
| `data-wm-content` | скроллируемая область контента |
|
|
70
68
|
|
|
69
|
+
`removeOnClose` сам отвязывает контроллер и удаляет элемент при закрытии окна. На тач-устройствах хит-зоны ресайза и порог снэпа автоматически крупнее (`pointer: coarse`); настраиваются через `attachDesktop(wm, el, { hitAreas: { edge, corner } })`.
|
|
70
|
+
|
|
71
71
|
Контроллер добавляет ресайз-хендлы (`[data-wm-resize]`), превью снэпа (`[data-wm-snap-preview]`) и скрытый live-регион для скринридеров.
|
|
72
72
|
|
|
73
73
|
## Адаптеры
|
|
74
74
|
|
|
75
|
-
Примеры для React, Vue, Svelte и
|
|
75
|
+
Примеры для React, Vue, Svelte, Solid и Angular — в [английском README](./README.md#react) и на [лендинге](https://surdeddd.github.io/wmkit/) (табы «Фреймворки»). Принцип один: контент окна живёт в дереве вашего фреймворка, никакого innerHTML.
|
|
76
76
|
|
|
77
77
|
## API ядра — кратко
|
|
78
78
|
|
|
@@ -89,12 +89,18 @@ wm.hydrate(json)
|
|
|
89
89
|
|
|
90
90
|
wm.on('stage', ({ window, previous }) => console.log(previous, '→', window.stage))
|
|
91
91
|
wm.batch(() => { /* много операций — одно уведомление */ })
|
|
92
|
+
|
|
93
|
+
wm.undo(); wm.redo() // история изменений, drag = одна запись (historyLimit, default 50)
|
|
94
|
+
wm.saveLayout('работа'); wm.loadLayout('работа') // именованные снапшоты рабочего стола
|
|
95
|
+
wm.arrange('tile') // или 'cascade'; плюс minimizeAll() / restoreAll()
|
|
92
96
|
```
|
|
93
97
|
|
|
94
98
|
Слои: `normal` < `floating` (always-on-top) < `modal`. Модалка блокирует фокус нижних окон (попытка — событие `modalblocked` и flash-анимация), Tab заперт внутри.
|
|
95
99
|
|
|
96
100
|
Клавиатура по умолчанию: стрелки двигают сфокусированное окно (16 px), `Alt` — шаг 1 px, `Shift+стрелки` — ресайз, `F6`/`Shift+F6` — цикл по окнам, `Escape` отменяет активный drag/resize.
|
|
97
101
|
|
|
102
|
+
Магнетизм включён из коробки (порог 8 px, на тач-устройствах 12 px): `attachDesktop(wm, el, { magnetism: { threshold: 16 } })` или `magnetism: false`. Свой контекст-меню тайтлбара — через `onTitlebarContextMenu(win, event)` (правый клик и long-press на таче).
|
|
103
|
+
|
|
98
104
|
### Персист
|
|
99
105
|
|
|
100
106
|
```js
|
|
@@ -115,14 +121,17 @@ if (isPopoutSupported()) await popout(wm, 'docs', contentElement)
|
|
|
115
121
|
|
|
116
122
|
Подключите `@surdeddd/wmkit/themes/glass.css` и переопределяйте CSS-переменные (`--wm-radius`, `--wm-bg`, `--wm-accent`, …) — или не подключайте ничего и стилизуйте `data-wm-stage`, `data-wm-focused`, `data-wm-dragging`, `[hidden]` сами.
|
|
117
123
|
|
|
124
|
+
Ещё две готовые темы: `themes/light.css` (светлое стекло) и `themes/retro.css` (Win98-ностальгия). Все три стилизуют одни и те же `data-wm-*` атрибуты — переключение = замена одного импорта.
|
|
125
|
+
|
|
118
126
|
## SSR
|
|
119
127
|
|
|
120
128
|
Ядро не трогает `window`/`document`: менеджер можно создавать и гидрейтить на сервере, `attachDesktop` вызывается после маунта. `persist` тихо выключается без доступного storage.
|
|
121
129
|
|
|
122
130
|
## Качество
|
|
123
131
|
|
|
124
|
-
-
|
|
125
|
-
-
|
|
132
|
+
- 172 юнит-теста, **100%** покрытие стейт-машины и persist по строкам/веткам/функциям
|
|
133
|
+
- 178+ Playwright-сценариев на Chromium, WebKit и мобильной эмуляции: drag, ресайз во все стороны, снэп, магнетизм, undo после drag, клавиатура, touch, персист через перезагрузку, стресс на 50 окон, модальные ловушки, axe-аудиты доступности, визуальная регрессия по скриншотам
|
|
134
|
+
- перф-бенчмарки в CI на каждый push (`vitest bench`): 1 000 окон открываются за ~150 мс, move среди 50 окон ~1.2 мкс, полный undo/redo-проход на 100 шагов ~52 мкс
|
|
126
135
|
- `publint` + `@arethetypeswrong/cli` проверяют валидность пакета, `size-limit` следит за бюджетами
|
|
127
136
|
|
|
128
137
|
## Лицензия
|
package/dist/angular.cjs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkB35DANTX_cjs = require('./chunk-B35DANTX.cjs');
|
|
4
|
+
var core = require('@angular/core');
|
|
5
|
+
|
|
6
|
+
function onDestroy(dispose) {
|
|
7
|
+
let destroyRef = null;
|
|
8
|
+
try {
|
|
9
|
+
destroyRef = core.inject(core.DestroyRef);
|
|
10
|
+
} catch {
|
|
11
|
+
destroyRef = null;
|
|
12
|
+
}
|
|
13
|
+
destroyRef?.onDestroy(dispose);
|
|
14
|
+
}
|
|
15
|
+
function useWindowManager(options) {
|
|
16
|
+
const wm = chunkB35DANTX_cjs.createWindowManager(options);
|
|
17
|
+
onDestroy(() => wm.destroy());
|
|
18
|
+
return wm;
|
|
19
|
+
}
|
|
20
|
+
function useWmState(wm) {
|
|
21
|
+
const state = core.signal(wm.getState());
|
|
22
|
+
const stop = wm.subscribe((next) => state.set(next));
|
|
23
|
+
onDestroy(stop);
|
|
24
|
+
return state.asReadonly();
|
|
25
|
+
}
|
|
26
|
+
function useWmWindow(wm, id) {
|
|
27
|
+
const state = useWmState(wm);
|
|
28
|
+
return core.computed(() => state().windows[id]);
|
|
29
|
+
}
|
|
30
|
+
function createDesktop(wm, options) {
|
|
31
|
+
const binder = chunkB35DANTX_cjs.createDesktopBinder(wm, options);
|
|
32
|
+
return {
|
|
33
|
+
binder,
|
|
34
|
+
desktop(element) {
|
|
35
|
+
const unbind = binder.bindDesktop(element);
|
|
36
|
+
onDestroy(unbind);
|
|
37
|
+
},
|
|
38
|
+
window(id, windowOptions) {
|
|
39
|
+
return (element) => {
|
|
40
|
+
const unbind = binder.bindWindow(id, element, windowOptions);
|
|
41
|
+
onDestroy(unbind);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
exports.createDesktop = createDesktop;
|
|
48
|
+
exports.useWindowManager = useWindowManager;
|
|
49
|
+
exports.useWmState = useWmState;
|
|
50
|
+
exports.useWmWindow = useWmWindow;
|
|
51
|
+
//# sourceMappingURL=angular.cjs.map
|
|
52
|
+
//# sourceMappingURL=angular.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/angular.ts"],"names":["inject","DestroyRef","createWindowManager","signal","computed","createDesktopBinder"],"mappings":";;;;;AAMA,SAAS,UAAU,OAAA,EAA2B;AAC5C,EAAA,IAAI,UAAA,GAAgC,IAAA;AACpC,EAAA,IAAI;AACF,IAAA,UAAA,GAAaA,YAAOC,eAAU,CAAA;AAAA,EAChC,CAAA,CAAA,MAAQ;AACN,IAAA,UAAA,GAAa,IAAA;AAAA,EACf;AACA,EAAA,UAAA,EAAY,UAAU,OAAO,CAAA;AAC/B;AAEO,SAAS,iBAAiB,OAAA,EAAyC;AACxE,EAAA,MAAM,EAAA,GAAKC,sCAAoB,OAAO,CAAA;AACtC,EAAA,SAAA,CAAU,MAAM,EAAA,CAAG,OAAA,EAAS,CAAA;AAC5B,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,WAAW,EAAA,EAAyC;AAClE,EAAA,MAAM,KAAA,GAAQC,WAAA,CAAO,EAAA,CAAG,QAAA,EAAU,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,GAAG,SAAA,CAAU,CAAC,SAAS,KAAA,CAAM,GAAA,CAAI,IAAI,CAAC,CAAA;AACnD,EAAA,SAAA,CAAU,IAAI,CAAA;AACd,EAAA,OAAO,MAAM,UAAA,EAAW;AAC1B;AAEO,SAAS,WAAA,CAAY,IAAmB,EAAA,EAA6C;AAC1F,EAAA,MAAM,KAAA,GAAQ,WAAW,EAAE,CAAA;AAC3B,EAAA,OAAOC,cAAS,MAAM,KAAA,EAAM,CAAE,OAAA,CAAQ,EAAE,CAAC,CAAA;AAC3C;AAQO,SAAS,aAAA,CAAc,IAAmB,OAAA,EAA0C;AACzF,EAAA,MAAM,MAAA,GAASC,qCAAA,CAAoB,EAAA,EAAI,OAAO,CAAA;AAC9C,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,QAAQ,OAAA,EAAS;AACf,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,WAAA,CAAY,OAAO,CAAA;AACzC,MAAA,SAAA,CAAU,MAAM,CAAA;AAAA,IAClB,CAAA;AAAA,IACA,MAAA,CAAO,IAAI,aAAA,EAAe;AACxB,MAAA,OAAO,CAAC,OAAA,KAAY;AAClB,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,EAAA,EAAI,SAAS,aAAa,CAAA;AAC3D,QAAA,SAAA,CAAU,MAAM,CAAA;AAAA,MAClB,CAAA;AAAA,IACF;AAAA,GACF;AACF","file":"angular.cjs","sourcesContent":["import { computed, DestroyRef, inject, type Signal, signal } from '@angular/core'\nimport { createWindowManager } from '../core/manager'\nimport type { ManagerOptions, ManagerState, WindowManager, WindowState } from '../core/types'\nimport { createDesktopBinder, type DesktopBinder } from '../dom/binder'\nimport type { DesktopOptions, WindowAttachOptions } from '../dom/shared'\n\nfunction onDestroy(dispose: () => void): void {\n let destroyRef: DestroyRef | null = null\n try {\n destroyRef = inject(DestroyRef)\n } catch {\n destroyRef = null\n }\n destroyRef?.onDestroy(dispose)\n}\n\nexport function useWindowManager(options?: ManagerOptions): WindowManager {\n const wm = createWindowManager(options)\n onDestroy(() => wm.destroy())\n return wm\n}\n\nexport function useWmState(wm: WindowManager): Signal<ManagerState> {\n const state = signal(wm.getState())\n const stop = wm.subscribe((next) => state.set(next))\n onDestroy(stop)\n return state.asReadonly()\n}\n\nexport function useWmWindow(wm: WindowManager, id: string): Signal<WindowState | undefined> {\n const state = useWmState(wm)\n return computed(() => state().windows[id])\n}\n\nexport interface AngularDesktop {\n binder: DesktopBinder\n desktop(element: HTMLElement): void\n window(id: string, options?: WindowAttachOptions): (element: HTMLElement) => void\n}\n\nexport function createDesktop(wm: WindowManager, options?: DesktopOptions): AngularDesktop {\n const binder = createDesktopBinder(wm, options)\n return {\n binder,\n desktop(element) {\n const unbind = binder.bindDesktop(element)\n onDestroy(unbind)\n },\n window(id, windowOptions) {\n return (element) => {\n const unbind = binder.bindWindow(id, element, windowOptions)\n onDestroy(unbind)\n }\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Signal } from '@angular/core';
|
|
2
|
+
import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-DdCtDHgt.cjs';
|
|
3
|
+
import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-Dq_AZRbo.cjs';
|
|
4
|
+
|
|
5
|
+
declare function useWindowManager(options?: ManagerOptions): WindowManager;
|
|
6
|
+
declare function useWmState(wm: WindowManager): Signal<ManagerState>;
|
|
7
|
+
declare function useWmWindow(wm: WindowManager, id: string): Signal<WindowState | undefined>;
|
|
8
|
+
interface AngularDesktop {
|
|
9
|
+
binder: DesktopBinder;
|
|
10
|
+
desktop(element: HTMLElement): void;
|
|
11
|
+
window(id: string, options?: WindowAttachOptions): (element: HTMLElement) => void;
|
|
12
|
+
}
|
|
13
|
+
declare function createDesktop(wm: WindowManager, options?: DesktopOptions): AngularDesktop;
|
|
14
|
+
|
|
15
|
+
export { type AngularDesktop, createDesktop, useWindowManager, useWmState, useWmWindow };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Signal } from '@angular/core';
|
|
2
|
+
import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-DdCtDHgt.js';
|
|
3
|
+
import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-0Oku22Nc.js';
|
|
4
|
+
|
|
5
|
+
declare function useWindowManager(options?: ManagerOptions): WindowManager;
|
|
6
|
+
declare function useWmState(wm: WindowManager): Signal<ManagerState>;
|
|
7
|
+
declare function useWmWindow(wm: WindowManager, id: string): Signal<WindowState | undefined>;
|
|
8
|
+
interface AngularDesktop {
|
|
9
|
+
binder: DesktopBinder;
|
|
10
|
+
desktop(element: HTMLElement): void;
|
|
11
|
+
window(id: string, options?: WindowAttachOptions): (element: HTMLElement) => void;
|
|
12
|
+
}
|
|
13
|
+
declare function createDesktop(wm: WindowManager, options?: DesktopOptions): AngularDesktop;
|
|
14
|
+
|
|
15
|
+
export { type AngularDesktop, createDesktop, useWindowManager, useWmState, useWmWindow };
|
package/dist/angular.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createWindowManager, createDesktopBinder } from './chunk-YCHJXSTC.js';
|
|
2
|
+
import { signal, computed, inject, DestroyRef } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
function onDestroy(dispose) {
|
|
5
|
+
let destroyRef = null;
|
|
6
|
+
try {
|
|
7
|
+
destroyRef = inject(DestroyRef);
|
|
8
|
+
} catch {
|
|
9
|
+
destroyRef = null;
|
|
10
|
+
}
|
|
11
|
+
destroyRef?.onDestroy(dispose);
|
|
12
|
+
}
|
|
13
|
+
function useWindowManager(options) {
|
|
14
|
+
const wm = createWindowManager(options);
|
|
15
|
+
onDestroy(() => wm.destroy());
|
|
16
|
+
return wm;
|
|
17
|
+
}
|
|
18
|
+
function useWmState(wm) {
|
|
19
|
+
const state = signal(wm.getState());
|
|
20
|
+
const stop = wm.subscribe((next) => state.set(next));
|
|
21
|
+
onDestroy(stop);
|
|
22
|
+
return state.asReadonly();
|
|
23
|
+
}
|
|
24
|
+
function useWmWindow(wm, id) {
|
|
25
|
+
const state = useWmState(wm);
|
|
26
|
+
return computed(() => state().windows[id]);
|
|
27
|
+
}
|
|
28
|
+
function createDesktop(wm, options) {
|
|
29
|
+
const binder = createDesktopBinder(wm, options);
|
|
30
|
+
return {
|
|
31
|
+
binder,
|
|
32
|
+
desktop(element) {
|
|
33
|
+
const unbind = binder.bindDesktop(element);
|
|
34
|
+
onDestroy(unbind);
|
|
35
|
+
},
|
|
36
|
+
window(id, windowOptions) {
|
|
37
|
+
return (element) => {
|
|
38
|
+
const unbind = binder.bindWindow(id, element, windowOptions);
|
|
39
|
+
onDestroy(unbind);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { createDesktop, useWindowManager, useWmState, useWmWindow };
|
|
46
|
+
//# sourceMappingURL=angular.js.map
|
|
47
|
+
//# sourceMappingURL=angular.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/angular.ts"],"names":[],"mappings":";;;AAMA,SAAS,UAAU,OAAA,EAA2B;AAC5C,EAAA,IAAI,UAAA,GAAgC,IAAA;AACpC,EAAA,IAAI;AACF,IAAA,UAAA,GAAa,OAAO,UAAU,CAAA;AAAA,EAChC,CAAA,CAAA,MAAQ;AACN,IAAA,UAAA,GAAa,IAAA;AAAA,EACf;AACA,EAAA,UAAA,EAAY,UAAU,OAAO,CAAA;AAC/B;AAEO,SAAS,iBAAiB,OAAA,EAAyC;AACxE,EAAA,MAAM,EAAA,GAAK,oBAAoB,OAAO,CAAA;AACtC,EAAA,SAAA,CAAU,MAAM,EAAA,CAAG,OAAA,EAAS,CAAA;AAC5B,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,WAAW,EAAA,EAAyC;AAClE,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,GAAG,SAAA,CAAU,CAAC,SAAS,KAAA,CAAM,GAAA,CAAI,IAAI,CAAC,CAAA;AACnD,EAAA,SAAA,CAAU,IAAI,CAAA;AACd,EAAA,OAAO,MAAM,UAAA,EAAW;AAC1B;AAEO,SAAS,WAAA,CAAY,IAAmB,EAAA,EAA6C;AAC1F,EAAA,MAAM,KAAA,GAAQ,WAAW,EAAE,CAAA;AAC3B,EAAA,OAAO,SAAS,MAAM,KAAA,EAAM,CAAE,OAAA,CAAQ,EAAE,CAAC,CAAA;AAC3C;AAQO,SAAS,aAAA,CAAc,IAAmB,OAAA,EAA0C;AACzF,EAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,EAAA,EAAI,OAAO,CAAA;AAC9C,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,QAAQ,OAAA,EAAS;AACf,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,WAAA,CAAY,OAAO,CAAA;AACzC,MAAA,SAAA,CAAU,MAAM,CAAA;AAAA,IAClB,CAAA;AAAA,IACA,MAAA,CAAO,IAAI,aAAA,EAAe;AACxB,MAAA,OAAO,CAAC,OAAA,KAAY;AAClB,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,EAAA,EAAI,SAAS,aAAa,CAAA;AAC3D,QAAA,SAAA,CAAU,MAAM,CAAA;AAAA,MAClB,CAAA;AAAA,IACF;AAAA,GACF;AACF","file":"angular.js","sourcesContent":["import { computed, DestroyRef, inject, type Signal, signal } from '@angular/core'\nimport { createWindowManager } from '../core/manager'\nimport type { ManagerOptions, ManagerState, WindowManager, WindowState } from '../core/types'\nimport { createDesktopBinder, type DesktopBinder } from '../dom/binder'\nimport type { DesktopOptions, WindowAttachOptions } from '../dom/shared'\n\nfunction onDestroy(dispose: () => void): void {\n let destroyRef: DestroyRef | null = null\n try {\n destroyRef = inject(DestroyRef)\n } catch {\n destroyRef = null\n }\n destroyRef?.onDestroy(dispose)\n}\n\nexport function useWindowManager(options?: ManagerOptions): WindowManager {\n const wm = createWindowManager(options)\n onDestroy(() => wm.destroy())\n return wm\n}\n\nexport function useWmState(wm: WindowManager): Signal<ManagerState> {\n const state = signal(wm.getState())\n const stop = wm.subscribe((next) => state.set(next))\n onDestroy(stop)\n return state.asReadonly()\n}\n\nexport function useWmWindow(wm: WindowManager, id: string): Signal<WindowState | undefined> {\n const state = useWmState(wm)\n return computed(() => state().windows[id])\n}\n\nexport interface AngularDesktop {\n binder: DesktopBinder\n desktop(element: HTMLElement): void\n window(id: string, options?: WindowAttachOptions): (element: HTMLElement) => void\n}\n\nexport function createDesktop(wm: WindowManager, options?: DesktopOptions): AngularDesktop {\n const binder = createDesktopBinder(wm, options)\n return {\n binder,\n desktop(element) {\n const unbind = binder.bindDesktop(element)\n onDestroy(unbind)\n },\n window(id, windowOptions) {\n return (element) => {\n const unbind = binder.bindWindow(id, element, windowOptions)\n onDestroy(unbind)\n }\n },\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as Bounds,
|
|
1
|
+
import { B as Bounds, f as Size, g as SnapZone, W as WindowManager, b as WindowState } from './types-DdCtDHgt.js';
|
|
2
2
|
|
|
3
3
|
declare function clamp(value: number, min: number, max: number): number;
|
|
4
4
|
declare function clampSize(size: Size, min: Size, max: Size | null): Size;
|
|
@@ -10,6 +10,13 @@ interface SnapDetectOptions {
|
|
|
10
10
|
cornerSize?: number;
|
|
11
11
|
}
|
|
12
12
|
declare function detectSnapZone(x: number, y: number, viewport: Size, options?: SnapDetectOptions): SnapZone | null;
|
|
13
|
+
interface MagnetResult {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
snappedX: boolean;
|
|
17
|
+
snappedY: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare function magnetize(bounds: Bounds, targets: readonly Bounds[], threshold: number): MagnetResult;
|
|
13
20
|
|
|
14
21
|
interface AnnouncerMessages {
|
|
15
22
|
opened(title: string): string;
|
|
@@ -36,16 +43,27 @@ interface DesktopKeyboardOptions {
|
|
|
36
43
|
moveStep?: number;
|
|
37
44
|
cycle?: boolean;
|
|
38
45
|
}
|
|
46
|
+
interface HitAreaOptions {
|
|
47
|
+
edge?: number;
|
|
48
|
+
corner?: number;
|
|
49
|
+
}
|
|
50
|
+
interface MagnetismOptions {
|
|
51
|
+
threshold?: number;
|
|
52
|
+
}
|
|
39
53
|
interface DesktopOptions {
|
|
40
54
|
snap?: boolean | DesktopSnapOptions;
|
|
41
55
|
keyboard?: boolean | DesktopKeyboardOptions;
|
|
42
56
|
announce?: boolean | Partial<AnnouncerMessages>;
|
|
43
57
|
autoViewport?: boolean;
|
|
58
|
+
hitAreas?: HitAreaOptions;
|
|
59
|
+
magnetism?: boolean | MagnetismOptions;
|
|
44
60
|
minimizeTarget?: (window: WindowState) => Element | null;
|
|
61
|
+
onTitlebarContextMenu?: (window: WindowState, event: MouseEvent) => void;
|
|
45
62
|
}
|
|
46
63
|
interface WindowAttachOptions {
|
|
47
64
|
handle?: HTMLElement | string;
|
|
48
65
|
resizeHandles?: boolean;
|
|
66
|
+
removeOnClose?: boolean;
|
|
49
67
|
}
|
|
50
68
|
interface DesktopController {
|
|
51
69
|
element: HTMLElement;
|
|
@@ -53,7 +71,7 @@ interface DesktopController {
|
|
|
53
71
|
attachWindow(id: string, element: HTMLElement, options?: WindowAttachOptions): () => void;
|
|
54
72
|
destroy(): void;
|
|
55
73
|
}
|
|
56
|
-
|
|
74
|
+
|
|
57
75
|
interface DesktopBinder {
|
|
58
76
|
wm: WindowManager;
|
|
59
77
|
controller(): DesktopController | null;
|
|
@@ -62,4 +80,4 @@ interface DesktopBinder {
|
|
|
62
80
|
}
|
|
63
81
|
declare function createDesktopBinder(wm: WindowManager, options?: DesktopOptions): DesktopBinder;
|
|
64
82
|
|
|
65
|
-
export { type Announcer as A, type DesktopBinder as D, type SnapDetectOptions as S, type WindowAttachOptions as W, type
|
|
83
|
+
export { type Announcer as A, type DesktopBinder as D, type HitAreaOptions as H, type MagnetResult as M, type SnapDetectOptions as S, type WindowAttachOptions as W, type DesktopOptions as a, type DesktopController as b, type AnnouncerMessages as c, type DesktopKeyboardOptions as d, type DesktopSnapOptions as e, type MagnetismOptions as f, boundsEqual as g, clamp as h, clampSize as i, clampToViewport as j, createAnnouncer as k, createDesktopBinder as l, defaultMessages as m, detectSnapZone as n, magnetize as o, zoneBounds as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as Bounds,
|
|
1
|
+
import { B as Bounds, f as Size, g as SnapZone, W as WindowManager, b as WindowState } from './types-DdCtDHgt.cjs';
|
|
2
2
|
|
|
3
3
|
declare function clamp(value: number, min: number, max: number): number;
|
|
4
4
|
declare function clampSize(size: Size, min: Size, max: Size | null): Size;
|
|
@@ -10,6 +10,13 @@ interface SnapDetectOptions {
|
|
|
10
10
|
cornerSize?: number;
|
|
11
11
|
}
|
|
12
12
|
declare function detectSnapZone(x: number, y: number, viewport: Size, options?: SnapDetectOptions): SnapZone | null;
|
|
13
|
+
interface MagnetResult {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
snappedX: boolean;
|
|
17
|
+
snappedY: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare function magnetize(bounds: Bounds, targets: readonly Bounds[], threshold: number): MagnetResult;
|
|
13
20
|
|
|
14
21
|
interface AnnouncerMessages {
|
|
15
22
|
opened(title: string): string;
|
|
@@ -36,16 +43,27 @@ interface DesktopKeyboardOptions {
|
|
|
36
43
|
moveStep?: number;
|
|
37
44
|
cycle?: boolean;
|
|
38
45
|
}
|
|
46
|
+
interface HitAreaOptions {
|
|
47
|
+
edge?: number;
|
|
48
|
+
corner?: number;
|
|
49
|
+
}
|
|
50
|
+
interface MagnetismOptions {
|
|
51
|
+
threshold?: number;
|
|
52
|
+
}
|
|
39
53
|
interface DesktopOptions {
|
|
40
54
|
snap?: boolean | DesktopSnapOptions;
|
|
41
55
|
keyboard?: boolean | DesktopKeyboardOptions;
|
|
42
56
|
announce?: boolean | Partial<AnnouncerMessages>;
|
|
43
57
|
autoViewport?: boolean;
|
|
58
|
+
hitAreas?: HitAreaOptions;
|
|
59
|
+
magnetism?: boolean | MagnetismOptions;
|
|
44
60
|
minimizeTarget?: (window: WindowState) => Element | null;
|
|
61
|
+
onTitlebarContextMenu?: (window: WindowState, event: MouseEvent) => void;
|
|
45
62
|
}
|
|
46
63
|
interface WindowAttachOptions {
|
|
47
64
|
handle?: HTMLElement | string;
|
|
48
65
|
resizeHandles?: boolean;
|
|
66
|
+
removeOnClose?: boolean;
|
|
49
67
|
}
|
|
50
68
|
interface DesktopController {
|
|
51
69
|
element: HTMLElement;
|
|
@@ -53,7 +71,7 @@ interface DesktopController {
|
|
|
53
71
|
attachWindow(id: string, element: HTMLElement, options?: WindowAttachOptions): () => void;
|
|
54
72
|
destroy(): void;
|
|
55
73
|
}
|
|
56
|
-
|
|
74
|
+
|
|
57
75
|
interface DesktopBinder {
|
|
58
76
|
wm: WindowManager;
|
|
59
77
|
controller(): DesktopController | null;
|
|
@@ -62,4 +80,4 @@ interface DesktopBinder {
|
|
|
62
80
|
}
|
|
63
81
|
declare function createDesktopBinder(wm: WindowManager, options?: DesktopOptions): DesktopBinder;
|
|
64
82
|
|
|
65
|
-
export { type Announcer as A, type DesktopBinder as D, type SnapDetectOptions as S, type WindowAttachOptions as W, type
|
|
83
|
+
export { type Announcer as A, type DesktopBinder as D, type HitAreaOptions as H, type MagnetResult as M, type SnapDetectOptions as S, type WindowAttachOptions as W, type DesktopOptions as a, type DesktopController as b, type AnnouncerMessages as c, type DesktopKeyboardOptions as d, type DesktopSnapOptions as e, type MagnetismOptions as f, boundsEqual as g, clamp as h, clampSize as i, clampToViewport as j, createAnnouncer as k, createDesktopBinder as l, defaultMessages as m, detectSnapZone as n, magnetize as o, zoneBounds as z };
|