@nu-art/floating-windows 0.400.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.
@@ -0,0 +1,5 @@
1
+ import { ThunderDispatcher } from '@nu-art/thunderstorm-frontend/index';
2
+ export interface FloatingWindows_FocusWindow {
3
+ __onFocusFloatingWindow: (windowKey: string) => void;
4
+ }
5
+ export declare const dispatcher_FloatingWindows_FocusWindow: ThunderDispatcher<FloatingWindows_FocusWindow, "__onFocusFloatingWindow", [windowKey: string], void>;
@@ -0,0 +1,2 @@
1
+ import { ThunderDispatcher } from '@nu-art/thunderstorm-frontend/index';
2
+ export const dispatcher_FloatingWindows_FocusWindow = new ThunderDispatcher('__onFocusFloatingWindow');
@@ -0,0 +1,5 @@
1
+ import { ThunderDispatcher } from '@nu-art/thunderstorm-frontend/index';
2
+ export interface FloatingWindows_WindowsUpdated {
3
+ __onFloatingWindowsUpdated: VoidFunction;
4
+ }
5
+ export declare const dispatch_FloatingWindows_WindowsUpdated: ThunderDispatcher<FloatingWindows_WindowsUpdated, "__onFloatingWindowsUpdated", [], void>;
@@ -0,0 +1,2 @@
1
+ import { ThunderDispatcher } from '@nu-art/thunderstorm-frontend/index';
2
+ export const dispatch_FloatingWindows_WindowsUpdated = new ThunderDispatcher('__onFloatingWindowsUpdated');
@@ -0,0 +1,11 @@
1
+ import { Module } from '@nu-art/ts-common';
2
+ import { Model_FloatingWindow } from '../types.js';
3
+ declare class ModuleFE_FloatingWindow_Class extends Module {
4
+ readonly windowModels: Model_FloatingWindow[];
5
+ window: {
6
+ add: (model: Model_FloatingWindow) => void;
7
+ remove: (key: string) => void;
8
+ };
9
+ }
10
+ export declare const ModuleFE_FloatingWindows: ModuleFE_FloatingWindow_Class;
11
+ export {};
@@ -0,0 +1,22 @@
1
+ import { Module, removeFromArrayByIndex } from '@nu-art/ts-common';
2
+ import { dispatcher_FloatingWindows_FocusWindow } from '../_dispatchers/focus-window.js';
3
+ import { dispatch_FloatingWindows_WindowsUpdated } from '../_dispatchers/models-updated.js';
4
+ class ModuleFE_FloatingWindow_Class extends Module {
5
+ windowModels = [];
6
+ window = {
7
+ add: (model) => {
8
+ if (this.windowModels.find(item => item.key === model.key))
9
+ return void dispatcher_FloatingWindows_FocusWindow.dispatchUI(model.key);
10
+ this.windowModels.push({ ...model });
11
+ dispatch_FloatingWindows_WindowsUpdated.dispatchUI();
12
+ },
13
+ remove: (key) => {
14
+ const index = this.windowModels.findIndex(model => model.key === key);
15
+ if (index === -1)
16
+ return;
17
+ removeFromArrayByIndex(this.windowModels, index);
18
+ dispatch_FloatingWindows_WindowsUpdated.dispatchUI();
19
+ }
20
+ };
21
+ }
22
+ export const ModuleFE_FloatingWindows = new ModuleFE_FloatingWindow_Class();
@@ -0,0 +1,2 @@
1
+ import { AppToolsScreen } from '@nu-art/thunderstorm-frontend/index';
2
+ export declare const ATS_FloatingWindows: AppToolsScreen;
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, ComponentSync, LL_H_C, LL_V_L } from '@nu-art/thunderstorm-frontend/index';
3
+ import { thunderstormCapabilitiesGroup } from '@nu-art/thunderstorm-frontend/consts';
4
+ import { ModuleFE_FloatingWindows } from '../../_modules/ModuleFE_FloatingWindows.js';
5
+ import { windowRect } from '../../_utils/get-window-rect.js';
6
+ class ATS_FloatingWindows_Class extends ComponentSync {
7
+ openHalfLeft = () => {
8
+ const model = {
9
+ key: 'ats-window-1',
10
+ content: (cb) => this.renderWindowContent('Test Half Left', cb),
11
+ resizable: true,
12
+ moveable: true,
13
+ rect: windowRect.halfScreen_Left()
14
+ };
15
+ ModuleFE_FloatingWindows.window.add(model);
16
+ };
17
+ openHalfRight = () => {
18
+ const model = {
19
+ key: 'ats-window-2',
20
+ content: (cb) => this.renderWindowContent('Test Half Right', cb),
21
+ resizable: true,
22
+ moveable: true,
23
+ rect: windowRect.halfScreen_Right(),
24
+ };
25
+ ModuleFE_FloatingWindows.window.add(model);
26
+ };
27
+ openThirdLeft = () => {
28
+ const model = {
29
+ key: 'ats-window-3',
30
+ content: (cb) => this.renderWindowContent('Test Third Left', cb),
31
+ resizable: true,
32
+ moveable: true,
33
+ rect: windowRect.thirdScreen_Left(),
34
+ };
35
+ ModuleFE_FloatingWindows.window.add(model);
36
+ };
37
+ openThirdMiddle = () => {
38
+ const model = {
39
+ key: 'ats-window-4',
40
+ content: (cb) => this.renderWindowContent('Test Third Middle', cb),
41
+ resizable: true,
42
+ moveable: true,
43
+ rect: windowRect.thirdScreen_Middle(),
44
+ };
45
+ ModuleFE_FloatingWindows.window.add(model);
46
+ };
47
+ openThirdRight = () => {
48
+ const model = {
49
+ key: 'ats-window-5',
50
+ content: (cb) => this.renderWindowContent('Test Third Right', cb),
51
+ resizable: true,
52
+ moveable: true,
53
+ rect: windowRect.thirdScreen_Right(),
54
+ };
55
+ ModuleFE_FloatingWindows.window.add(model);
56
+ };
57
+ render() {
58
+ return _jsxs(LL_V_L, { style: { gap: 10 }, children: [_jsx(Button, { variant: 'primary', onClick: this.openHalfLeft, children: "Open Half Left" }), _jsx(Button, { variant: 'primary', onClick: this.openHalfRight, children: "Open Half Right" }), _jsx(Button, { variant: 'primary', onClick: this.openThirdLeft, children: "Open Third Left" }), _jsx(Button, { variant: 'primary', onClick: this.openThirdMiddle, children: "Open Third Middle" }), _jsx(Button, { variant: 'primary', onClick: this.openThirdRight, children: "Open Third Right" })] });
59
+ }
60
+ renderWindowContent = (content, cb) => {
61
+ return _jsxs(LL_H_C, { style: { gap: 10 }, children: [content, _jsx(Button, { variant: 'secondary', onClick: cb, children: "Close" })] });
62
+ };
63
+ }
64
+ export const ATS_FloatingWindows = {
65
+ key: 'ats__floating-windows',
66
+ name: 'Floating Windows',
67
+ renderer: ATS_FloatingWindows_Class,
68
+ group: thunderstormCapabilitiesGroup
69
+ };
@@ -0,0 +1,32 @@
1
+ import { ComponentSync } from '@nu-art/thunderstorm-frontend';
2
+ import { Model_FloatingWindow } from '../../types.js';
3
+ import { FloatingWindows_FocusWindow } from '../../_dispatchers/focus-window.js';
4
+ import './TS_FloatingWindow.scss';
5
+ type Props = {
6
+ model: Model_FloatingWindow;
7
+ };
8
+ export declare class TS_FloatingWindow extends ComponentSync<Props> implements FloatingWindows_FocusWindow {
9
+ static runningZIndex: number;
10
+ private wrapperRef;
11
+ private anchorRef;
12
+ private focusTimeout?;
13
+ private resizeDebouncer;
14
+ private resizeObserver;
15
+ private dragOffset?;
16
+ __onFocusFloatingWindow: (windowKey: string) => void;
17
+ componentDidMount(): void;
18
+ componentWillUnmount(): void;
19
+ private closeWindow;
20
+ private getClassName;
21
+ private getStyle;
22
+ private getWrapperElement;
23
+ private getAnchorElement;
24
+ private onResize;
25
+ private onFocus;
26
+ private onDragStart;
27
+ private onDragMove;
28
+ private onDragEnd;
29
+ render(): import("react/jsx-runtime").JSX.Element;
30
+ private render_DragAnchor;
31
+ }
32
+ export {};
@@ -0,0 +1,130 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { _className, ComponentSync } from '@nu-art/thunderstorm-frontend';
4
+ import { BadImplementationException, Debounce, Second } from '@nu-art/ts-common';
5
+ import { ModuleFE_FloatingWindows } from '../../_modules/ModuleFE_FloatingWindows.js';
6
+ import './TS_FloatingWindow.scss';
7
+ export class TS_FloatingWindow extends ComponentSync {
8
+ static runningZIndex = 0;
9
+ wrapperRef = React.createRef();
10
+ anchorRef = React.createRef();
11
+ focusTimeout;
12
+ resizeDebouncer = new Debounce(() => this.onResize(), 100, 250);
13
+ resizeObserver = new ResizeObserver(() => this.resizeDebouncer.trigger());
14
+ dragOffset;
15
+ //######################### Life Cycle #########################
16
+ //Logic for programmatic focus
17
+ __onFocusFloatingWindow = (windowKey) => {
18
+ if (windowKey !== this.props.model.key)
19
+ return;
20
+ const wrapper = this.wrapperRef.current;
21
+ if (!wrapper)
22
+ return;
23
+ wrapper.focus();
24
+ wrapper.classList.add('focus');
25
+ //Bring to foreground
26
+ if (Number(wrapper.style.zIndex) !== TS_FloatingWindow.runningZIndex)
27
+ wrapper.style.zIndex = `${++TS_FloatingWindow.runningZIndex}`;
28
+ this.focusTimeout = setTimeout(() => {
29
+ wrapper.classList.remove('focus');
30
+ delete this.focusTimeout;
31
+ }, Second);
32
+ };
33
+ componentDidMount() {
34
+ const wrapper = this.getWrapperElement();
35
+ //Focus the window when it first mounts
36
+ wrapper.focus();
37
+ this.resizeObserver.observe(wrapper);
38
+ }
39
+ componentWillUnmount() {
40
+ clearTimeout(this.focusTimeout);
41
+ this.resizeObserver.disconnect();
42
+ }
43
+ //######################### Logic #########################
44
+ closeWindow = () => {
45
+ ModuleFE_FloatingWindows.window.remove(this.props.model.key);
46
+ };
47
+ getClassName = () => {
48
+ const model = this.props.model;
49
+ return _className('ts-floating-window', model.resizable && 'resizable', model.className);
50
+ };
51
+ getStyle = () => {
52
+ const model = this.props.model;
53
+ return {
54
+ left: model.rect.x,
55
+ top: model.rect.y,
56
+ width: model.rect.width,
57
+ height: model.rect.height,
58
+ zIndex: ++TS_FloatingWindow.runningZIndex,
59
+ };
60
+ };
61
+ getWrapperElement = () => {
62
+ const wrapper = this.wrapperRef.current;
63
+ if (!wrapper)
64
+ throw new BadImplementationException('Wrapper ref disconnected!');
65
+ return wrapper;
66
+ };
67
+ getAnchorElement = () => {
68
+ const anchorElement = this.anchorRef.current;
69
+ if (!anchorElement)
70
+ throw new BadImplementationException('Anchor ref disconnected!');
71
+ return anchorElement;
72
+ };
73
+ //######################### Event Callbacks #########################
74
+ onResize = () => {
75
+ const wrapper = this.getWrapperElement();
76
+ const model = this.props.model;
77
+ const rect = wrapper.getBoundingClientRect();
78
+ model.rect.x = rect.x;
79
+ model.rect.y = rect.y;
80
+ model.rect.height = rect.height;
81
+ model.rect.width = rect.width;
82
+ };
83
+ //Logic for UI focus
84
+ onFocus = () => {
85
+ const wrapper = this.getWrapperElement();
86
+ //Bring to foreground
87
+ if (Number(wrapper.style.zIndex) !== TS_FloatingWindow.runningZIndex)
88
+ wrapper.style.zIndex = `${++TS_FloatingWindow.runningZIndex}`;
89
+ };
90
+ onDragStart = (e) => {
91
+ const anchor = this.getAnchorElement();
92
+ const wrapper = this.getWrapperElement();
93
+ anchor.addEventListener('pointermove', this.onDragMove);
94
+ anchor.addEventListener('pointerup', this.onDragEnd);
95
+ wrapper.classList.add('dragging');
96
+ //Calc x & y offset from wrapper top-left corner
97
+ this.dragOffset = {
98
+ x: e.clientX - this.props.model.rect.x,
99
+ y: e.clientY - this.props.model.rect.y,
100
+ };
101
+ };
102
+ onDragMove = (e) => {
103
+ const newX = e.clientX - (this.dragOffset?.x ?? 0);
104
+ const newY = e.clientY - (this.dragOffset?.y ?? 0);
105
+ this.props.model.rect.x = newX;
106
+ this.props.model.rect.y = newY;
107
+ const wrapper = this.getWrapperElement();
108
+ wrapper.style.left = `${newX}px`;
109
+ wrapper.style.top = `${newY}px`;
110
+ };
111
+ onDragEnd = () => {
112
+ const anchor = this.getAnchorElement();
113
+ const wrapper = this.getWrapperElement();
114
+ anchor.removeEventListener('pointermove', this.onDragMove);
115
+ anchor.removeEventListener('pointerup', this.onDragEnd);
116
+ wrapper.classList.remove('dragging');
117
+ delete this.dragOffset;
118
+ };
119
+ //######################### Render #########################
120
+ render() {
121
+ const model = this.props.model;
122
+ return _jsxs("div", { ref: this.wrapperRef, className: this.getClassName(), style: this.getStyle(), tabIndex: 0, onFocus: this.onFocus, children: [this.render_DragAnchor(), model.content(this.closeWindow)] });
123
+ }
124
+ render_DragAnchor = () => {
125
+ const model = this.props.model;
126
+ if (!model.moveable)
127
+ return;
128
+ return _jsx("div", { ref: this.anchorRef, className: 'ts-floating-window__drag-anchor', onPointerDown: this.onDragStart });
129
+ };
130
+ }
@@ -0,0 +1,38 @@
1
+ .ts-floating-window {
2
+ position: fixed;
3
+ background: #fff;
4
+ padding: 16px;
5
+ min-width: 20px;
6
+ min-height: 20px;
7
+ max-width: calc(100vw - 32px);
8
+ max-height: calc(100vh - 32px);
9
+ border: 1px solid black;
10
+
11
+ .ts-floating-window__drag-anchor {
12
+ position: absolute;
13
+ top: 0;
14
+ left: 0;
15
+ width: 20px;
16
+ height: 20px;
17
+ cursor: move;
18
+ }
19
+
20
+ &.dragging {
21
+ .ts-floating-window__drag-anchor {
22
+ position: fixed;
23
+ width: 100vw;
24
+ height: 100vh;
25
+ }
26
+ }
27
+
28
+ &.resizable {
29
+ resize: both;
30
+ overflow: auto;
31
+ }
32
+
33
+ @supports selector(::-webkit-resizer) {
34
+ &::-webkit-resizer {
35
+ appearance: none;
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,14 @@
1
+ import { ComponentSync } from '@nu-art/thunderstorm-frontend/index';
2
+ import { FloatingWindows_WindowsUpdated } from '../../_dispatchers/models-updated.js';
3
+ import './TS_FloatingWindows.scss';
4
+ import { Model_FloatingWindow } from '../../types.js';
5
+ type State = {
6
+ windowModels: Model_FloatingWindow[];
7
+ };
8
+ export declare class TS_FloatingWindows extends ComponentSync<unknown, State> implements FloatingWindows_WindowsUpdated {
9
+ __onFloatingWindowsUpdated: () => void;
10
+ protected deriveStateFromProps(nextProps: unknown, state: State): State;
11
+ componentDidUpdate(): void;
12
+ render(): import("react/jsx-runtime").JSX.Element | undefined;
13
+ }
14
+ export {};
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ComponentSync } from '@nu-art/thunderstorm-frontend/index';
3
+ import { ModuleFE_FloatingWindows } from '../../_modules/ModuleFE_FloatingWindows.js';
4
+ import './TS_FloatingWindows.scss';
5
+ import { TS_FloatingWindow } from '../TS_FloatingWindow/TS_FloatingWindow.js';
6
+ export class TS_FloatingWindows extends ComponentSync {
7
+ //######################### Life Cycle #########################
8
+ __onFloatingWindowsUpdated = () => {
9
+ this.forceUpdate();
10
+ };
11
+ deriveStateFromProps(nextProps, state) {
12
+ state.windowModels ??= ModuleFE_FloatingWindows.windowModels;
13
+ return state;
14
+ }
15
+ componentDidUpdate() {
16
+ if (!this.state.windowModels.length)
17
+ TS_FloatingWindow.runningZIndex = 0;
18
+ }
19
+ //######################### Logic #########################
20
+ //######################### Render #########################
21
+ render() {
22
+ if (!this.state.windowModels?.length)
23
+ return;
24
+ return _jsx("div", { id: 'ts-floating-windows', children: this.state.windowModels.map(model => {
25
+ return _jsx(TS_FloatingWindow, { model: model }, model.key);
26
+ }) });
27
+ }
28
+ }
@@ -0,0 +1,5 @@
1
+ #ts-floating-windows {
2
+ width: 0;
3
+ height: 0;
4
+ isolation: isolate;
5
+ }
@@ -0,0 +1,32 @@
1
+ export declare const windowRect: {
2
+ halfScreen_Left: () => {
3
+ width: number;
4
+ height: number;
5
+ x: number;
6
+ y: number;
7
+ };
8
+ halfScreen_Right: () => {
9
+ width: number;
10
+ height: number;
11
+ x: number;
12
+ y: number;
13
+ };
14
+ thirdScreen_Left: () => {
15
+ width: number;
16
+ height: number;
17
+ x: number;
18
+ y: number;
19
+ };
20
+ thirdScreen_Middle: () => {
21
+ width: number;
22
+ height: number;
23
+ x: number;
24
+ y: number;
25
+ };
26
+ thirdScreen_Right: () => {
27
+ width: number;
28
+ height: number;
29
+ x: number;
30
+ y: number;
31
+ };
32
+ };
@@ -0,0 +1,22 @@
1
+ export const windowRect = {
2
+ halfScreen_Left: () => {
3
+ const width = (window.innerWidth - 32) / 2;
4
+ return { width, height: window.innerHeight - 32, x: 16, y: 16 };
5
+ },
6
+ halfScreen_Right: () => {
7
+ const width = (window.innerWidth - 32) / 2;
8
+ return { width, height: window.innerHeight - 32, x: width + 16, y: 16 };
9
+ },
10
+ thirdScreen_Left: () => {
11
+ const width = (window.innerWidth - 32) / 3;
12
+ return { width, height: window.innerHeight - 32, x: 16, y: 16 };
13
+ },
14
+ thirdScreen_Middle: () => {
15
+ const width = (window.innerWidth - 32) / 3;
16
+ return { width, height: window.innerHeight - 32, x: width + 16, y: 16 };
17
+ },
18
+ thirdScreen_Right: () => {
19
+ const width = (window.innerWidth - 32) / 3;
20
+ return { width, height: window.innerHeight - 32, x: (2 * width) + 16, y: 16 };
21
+ }
22
+ };
package/ats.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './_ui/ATS_FloatingWindows/ATS_FloatingWindows.js';
package/ats.js ADDED
@@ -0,0 +1 @@
1
+ export * from './_ui/ATS_FloatingWindows/ATS_FloatingWindows.js';
package/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './types.js';
2
+ export * from './_modules/ModuleFE_FloatingWindows.js';
3
+ export * from './_dispatchers/models-updated.js';
4
+ export * from './_dispatchers/focus-window.js';
5
+ export * from './_utils/get-window-rect.js';
6
+ export * from './_ui/TS_FloatingWindows/TS_FloatingWindows.js';
7
+ export * from './_ui/TS_FloatingWindow/TS_FloatingWindow.js';
package/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from './types.js';
2
+ export * from './_modules/ModuleFE_FloatingWindows.js';
3
+ export * from './_dispatchers/models-updated.js';
4
+ export * from './_dispatchers/focus-window.js';
5
+ export * from './_utils/get-window-rect.js';
6
+ export * from './_ui/TS_FloatingWindows/TS_FloatingWindows.js';
7
+ export * from './_ui/TS_FloatingWindow/TS_FloatingWindow.js';
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@nu-art/floating-windows",
3
+ "version": "0.400.7",
4
+ "description": "Floating Windows",
5
+ "keywords": [
6
+ "TacB0sS",
7
+ "express",
8
+ "infra",
9
+ "nu-art",
10
+ "thunderstorm",
11
+ "typescript"
12
+ ],
13
+ "homepage": "https://github.com/nu-art-js/thunderstorm",
14
+ "bugs": {
15
+ "url": "https://github.com/nu-art-js/thunderstorm/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+ssh://git@github.com:nu-art-js/thunderstorm.git"
20
+ },
21
+ "license": "Apache-2.0",
22
+ "author": "TacB0sS",
23
+ "main": "index.js",
24
+ "types": "index.d.ts",
25
+ "scripts": {
26
+ "build": "tsc"
27
+ },
28
+ "contributors": [
29
+ {
30
+ "name": "TacB0sS"
31
+ },
32
+ {
33
+ "name": "Cipher",
34
+ "url": "https://www.linkedin.com/in/itay-leybovich-470b87229/"
35
+ }
36
+ ],
37
+ "publishConfig": {
38
+ "directory": "dist",
39
+ "linkDirectory": true
40
+ },
41
+ "dependencies": {
42
+ "@nu-art/ts-common": "0.400.7",
43
+ "@nu-art/ts-styles": "0.400.7",
44
+ "@nu-art/thunderstorm-frontend": "0.400.7",
45
+ "@nu-art/thunderstorm-shared": "0.400.7",
46
+ "react": "^18.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/react": "^18.0.0"
50
+ },
51
+ "unitConfig": {
52
+ "type": "typescript-lib"
53
+ },
54
+ "type": "module",
55
+ "exports": {
56
+ ".": {
57
+ "types": "./index.d.ts",
58
+ "import": "./index.js"
59
+ },
60
+ "./*": {
61
+ "types": "./*.d.ts",
62
+ "import": "./*.js"
63
+ }
64
+ }
65
+ }
package/types.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from 'react';
2
+ export type FloatingWindowRect = {
3
+ width?: number;
4
+ height?: number;
5
+ x: number;
6
+ y: number;
7
+ };
8
+ export type Model_FloatingWindow = {
9
+ key: string;
10
+ content: (closeWindowCB: VoidFunction) => ReactNode;
11
+ rect: FloatingWindowRect;
12
+ resizable?: boolean;
13
+ moveable?: boolean;
14
+ className?: string;
15
+ };
package/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};