react-graph-grid 0.0.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 ADDED
@@ -0,0 +1,16 @@
1
+ # React + Vite
2
+
3
+ This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
+
5
+ Currently, two official plugins are available:
6
+
7
+ - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
8
+ - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
+
10
+ ## React Compiler
11
+
12
+ The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
13
+
14
+ ## Expanding the ESLint configuration
15
+
16
+ If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
@@ -0,0 +1,29 @@
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import reactHooks from 'eslint-plugin-react-hooks'
4
+ import reactRefresh from 'eslint-plugin-react-refresh'
5
+ import { defineConfig, globalIgnores } from 'eslint/config'
6
+
7
+ export default defineConfig([
8
+ globalIgnores(['dist']),
9
+ {
10
+ files: ['**/*.{js,jsx}'],
11
+ extends: [
12
+ js.configs.recommended,
13
+ reactHooks.configs.flat.recommended,
14
+ reactRefresh.configs.vite,
15
+ ],
16
+ languageOptions: {
17
+ ecmaVersion: 2020,
18
+ globals: globals.browser,
19
+ parserOptions: {
20
+ ecmaVersion: 'latest',
21
+ ecmaFeatures: { jsx: true },
22
+ sourceType: 'module',
23
+ },
24
+ },
25
+ rules: {
26
+ 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
27
+ },
28
+ },
29
+ ])
package/index.html ADDED
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/IM.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>react-graph-grid</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.jsx"></script>
12
+ </body>
13
+ </html>
package/npm.aps ADDED
Binary file
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "react-graph-grid",
3
+ "author": "Mikhail Razumtsev",
4
+ "description": "a React package containing a grid that can communicate with other grids through a connection graph",
5
+ "private": false,
6
+ "version": "0.0.0",
7
+ "type": "module",
8
+ "scripts": {
9
+ "dev": "vite --port 4000",
10
+ "build": "vite build",
11
+ "lint": "eslint .",
12
+ "preview": "vite preview"
13
+ },
14
+ "dependencies": {
15
+ "react": "^19.2.0",
16
+ "react-dom": "^19.2.0"
17
+ },
18
+ "devDependencies": {
19
+ "@eslint/js": "^9.39.1",
20
+ "@types/react": "^19.2.5",
21
+ "@types/react-dom": "^19.2.3",
22
+ "@vitejs/plugin-react": "^5.1.1",
23
+ "eslint": "^9.39.1",
24
+ "eslint-plugin-react-hooks": "^7.0.1",
25
+ "eslint-plugin-react-refresh": "^0.4.24",
26
+ "globals": "^16.5.0",
27
+ "postcss-nesting": "^14.0.0",
28
+ "vite": "npm:rolldown-vite@7.2.5"
29
+ },
30
+ "overrides": {
31
+ "vite": "npm:rolldown-vite@7.2.5"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/Mish-Nukem/react-graph-grid.git"
36
+ }
37
+ }
package/public/IM.svg ADDED
@@ -0,0 +1,8 @@
1
+ <svg width="256" height="256" xmlns="http://www.w3.org/2000/svg">
2
+
3
+ <g>
4
+ <title>Layer 1</title>
5
+ <rect id="svg_3" height="256" width="253" y="2" x="2" stroke-width="0" stroke="#000" fill="#ffaa56"/>
6
+ <text transform="matrix(8.48553, 0, 0, 9.42849, -746.578, -863.864)" xml:space="preserve" text-anchor="start" font-family="Noto Sans JP" font-size="24" stroke-width="0" id="svg_2" y="113.28803" x="88.39999" stroke="#000" fill="#000000">IM</text>
7
+ </g>
8
+ </svg>
package/src/Base.jsx ADDED
@@ -0,0 +1,81 @@
1
+ import { DefaultGridTheme as Theme } from './Themes/DefaultGridTheme';
2
+ export class BaseComponent {
3
+
4
+ constructor(props) {
5
+
6
+ this.level = props.level || 0;
7
+
8
+ //window._logEnabled = true;
9
+ if (!BaseComponent.theme) {
10
+ BaseComponent.theme = new Theme();
11
+
12
+ if (BaseComponent.useBootstrap) {
13
+ BaseComponent.changeTheme(true);
14
+ }
15
+ }
16
+ }
17
+
18
+ translate(text, context) {
19
+ return BaseComponent.translate(text, context);
20
+ }
21
+
22
+ static translate(text/*, context*/) {
23
+ return text;
24
+ }
25
+
26
+ Spinner(id = -1, minW = -1, maxW = -1) {
27
+ return BaseComponent.Spinner ? BaseComponent.Spinner(id, minW, maxW) : <></>;
28
+ }
29
+
30
+ static Spinner(id = -1, minW = -1, maxW = -1) {
31
+ return (
32
+ <div key={`loader_${id}_`}
33
+ className='grid-loader'
34
+ style={{ minWidth: minW ? minW + "px" : "", maxWidth: maxW ? maxW + "px" : "" }}
35
+ >
36
+ <div>{BaseComponent.translate('Loading') + '...'}</div>
37
+ </div>
38
+ )
39
+ }
40
+
41
+ formatDate(text, dateFormat) {
42
+ return BaseComponent.formatDate(text, dateFormat);
43
+ }
44
+
45
+ static formatDate(text) {
46
+ return text;
47
+ }
48
+
49
+ static dateFormat = 'dd.MM.yyyy';
50
+ static dateTimeFormat = 'dd.MM.yyyy HH:mm:ss';
51
+
52
+ static theme = null;
53
+ static useBootstrap = false;
54
+ static changeTheme = () => {
55
+ return new Promise(function (resolve) {
56
+ BaseComponent.theme = new Theme();
57
+ resolve();
58
+ })
59
+ };
60
+ }
61
+
62
+ export function log(message) {
63
+ if (!window._logEnabled) return;
64
+
65
+ console.log(message);
66
+ }
67
+
68
+ export class NodeStatus {
69
+ static grid = 0;
70
+ static hidden = 1;
71
+ static filter = 2;
72
+ static lookup = 3;
73
+ static custom = 4;
74
+ };
75
+
76
+ export class FilterType {
77
+ static combobox = 0;
78
+ static date = 1;
79
+ static input = 2;
80
+ static custom = 3;
81
+ };
@@ -0,0 +1,339 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { BaseComponent } from './Base';
3
+ import { Images } from './Themes/Images';
4
+ import { ModalClass } from './Modal';
5
+ // ==================================================================================================================================================================
6
+ export function Dropdown(props) {
7
+ let dd = null;
8
+
9
+ const [ddState, setState] = useState({ dd: dd, ind: 0 });
10
+
11
+ if (ddState.dd) {
12
+ dd = ddState.dd;
13
+ }
14
+ else {
15
+ dd = new DropdownClass(props);
16
+ }
17
+
18
+ if (props.init && !ModalClass._isFake) {
19
+ props.init(dd);
20
+ }
21
+
22
+ dd.refreshState = function () {
23
+ setState({ dd: dd, ind: dd.stateind++ });
24
+ }
25
+
26
+ useEffect(() => {
27
+ dd.setupEvents();
28
+
29
+ return () => {
30
+ dd.clearEvents();
31
+ }
32
+ }, [dd])
33
+
34
+ return (dd.render());
35
+ }
36
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
37
+ export class DropdownClass extends ModalClass {
38
+ constructor(props) {
39
+ super(props);
40
+
41
+ const dd = this;
42
+
43
+ dd.getItems = props.getItems || function () { return new Promise(function (resolve) { resolve([]) }); };
44
+
45
+ window._dropdownSeq = window._dropdownSeq || 0;
46
+
47
+ dd.id = window._dropdownSeq++;
48
+
49
+ dd.pageNumber = 1;
50
+ dd.pageSize = props.pageSize || 20;
51
+ dd.items = props.items || [];
52
+
53
+ dd.menuItemClass = props.menuItemClass || BaseComponent.theme.menuItemClass;
54
+ dd.menuClass = props.menuClass || BaseComponent.theme.menuClass;
55
+
56
+ dd.stateind = 0;
57
+
58
+ dd.opt.onItemClick = props.onItemClick;
59
+ dd.opt.onClose = props.onClose;
60
+
61
+ dd.opt.parentRect = props.parentRect;
62
+
63
+ dd.maxW = props.maxW;
64
+
65
+ dd.opt.closeWhenEscape = true;
66
+ dd.opt.noHeader = true;
67
+ dd.opt.noFooter = true;
68
+ dd.opt.resizable = false;
69
+ dd.opt.noPadding = true;
70
+ dd.opt.hiddenOverlay = true;
71
+
72
+ dd.opt.onItemMouseEnter = props.onItemMouseEnter;
73
+ dd.opt.onItemMouseLeave = props.onItemMouseLeave;
74
+
75
+ dd.renderContent = dd.renderDropdownContent;
76
+
77
+ dd.visible = dd.items.length > 0;
78
+ }
79
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
80
+ appendItems() {
81
+ const dd = this;
82
+
83
+ dd.pageNumber++;
84
+
85
+ dd.getItems({ filter: dd.filter }).then(
86
+ items => {
87
+ dd.items = items;
88
+
89
+ dd.refreshState();
90
+ }
91
+ );
92
+ }
93
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
94
+ render() {
95
+ return super.render();
96
+ }
97
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
98
+ renderDropdownContent() {
99
+ const dd = this;
100
+
101
+ if (!dd.items || dd.waitingItems) {
102
+ return dd.Spinner(dd.id);
103
+ }
104
+
105
+ return (
106
+ <>
107
+ {
108
+ dd.allowUserFilter ? <></> : <></>
109
+ }
110
+ <ul
111
+ key={`dropdown_${dd.id}_`}
112
+ className={`dropdown-ul ${dd.menuClass || ''}`}
113
+ style={{ overflowX: 'hidden', padding: '0', margin: '0' }}
114
+ >
115
+ {
116
+ dd.items.map((item, ind) => {
117
+ return (
118
+ <li
119
+ dropdown-item={`${dd.id}_${item.id}_`}
120
+ key={`dropdownitem_${dd.id}_${item.id}_${ind}_`}
121
+ title={dd.translate(item.title || item.text)}
122
+ className={dd.menuItemClass + (dd.activeItem === item ? ' active' : '')}
123
+ style={{
124
+ listStyleType: 'none',
125
+ display: 'flex',
126
+ justifyContent: 'start',
127
+ flexWrap: 'nowrap',
128
+ alignItems: 'center',
129
+ paddingRight: item.items && item.items.length > 0 ? '' : '1em',
130
+ height: '1.5em',
131
+ }}
132
+ onClick={(e) => dd.onItemClick(e, item.id)}
133
+ onMouseEnter={(e) => {
134
+ if (!dd.opt.onItemMouseEnter) return;
135
+ dd.opt.onItemMouseEnter(e, item);
136
+ }}
137
+ onMouseLeave={(e) => {
138
+ if (!dd.opt.onItemMouseLeave) return;
139
+ dd.opt.onItemMouseLeave(e, item);
140
+ }}
141
+ >
142
+ <div style={{ width: '2em' }}>
143
+ {item.img ? item.img() : ''}
144
+ </div>
145
+ <div
146
+ style={{
147
+ display: 'flex',
148
+ justifyContent: 'space-between',
149
+ flexWrap: 'nowrap',
150
+ alignItems: 'center',
151
+ width: '100%',
152
+ }}
153
+ >
154
+ <span>{dd.translate(item.text)}</span>
155
+ {item.items && item.items.length > 0 ? Images.images.submenu(20, 10) : ''}
156
+ </div>
157
+ </li>
158
+ );
159
+ })
160
+ }
161
+ {
162
+ dd.allowUpload && dd.pageSize > 0 && dd.items.length === dd.pageSize * dd.pageNumber ?
163
+ <ul className={`dropdown-ul ${dd.menuClass || ''}`}
164
+ key={`dropdownadd_${dd.id}_`}
165
+ >
166
+ <li dropdown-item={`${dd.id}_append_`}
167
+ key={`dropdownitem_$${dd.id}_append_`}
168
+ title={dd.translate('load more records')}
169
+ className={dd.menuItemClass}
170
+ style={{ listStyleType: 'none', display: 'flex', flexWrap: 'nowrap' }}
171
+ onClick={(e) => dd.onItemClick(e, 'append')}
172
+ >
173
+ ${dd.translate('more...')}
174
+ </li>
175
+ </ul>
176
+ : <></>
177
+ }
178
+ </ul>
179
+ </>
180
+ );
181
+ }
182
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
183
+ calcPos(e) {
184
+ const dd = this;
185
+
186
+ const rect = dd.getDimensionsByContent();
187
+ const parentRect = dd.opt.parentRect ? dd.opt.parentRect : { x: e.clientX, y: e.clientY, width: e.width || 0, height: e.height || 0 };
188
+
189
+ dd.opt.pos = {
190
+ x: parentRect.x,
191
+ y: parentRect.y + parseInt(parentRect.height),
192
+ w: Math.max(rect.w, parentRect.width),
193
+ h: rect.h
194
+ };
195
+
196
+ if (dd.maxW != null) {
197
+ dd.opt.pos.w = Math.min(dd.opt.pos.w, dd.maxW);
198
+ }
199
+ }
200
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
201
+ popup(e) {
202
+ const dd = this;
203
+
204
+ function afterGetItems(newItems) {
205
+ dd.waitingItems = false;
206
+
207
+ if (newItems && newItems.length > 0) {
208
+ dd.items.push(...newItems);
209
+ }
210
+
211
+ dd.lastPageNumber = dd.pageNumber;
212
+
213
+ dd.visible = dd.items.length > 0;
214
+
215
+ if (dd.items.length <= 0 && !dd.opt.allowUserFilter) return;
216
+
217
+ dd.calcPos(e);
218
+ //log(' DropdownPos w = ' + dd.pos.w + ', h = ' + dd.pos.h);
219
+
220
+ dd.refreshState();
221
+ }
222
+
223
+ if (!dd.lastPageNumber || dd.lastPageNumber !== dd.pageNumber || dd.items.length <= 0) {
224
+ dd.waitingItems = true;
225
+ dd.visible = true;
226
+ dd.calcPos(e);
227
+
228
+ dd.refreshState();
229
+
230
+ dd.getItems({ filter: dd.filter, pageSize: dd.pageSize, pageNumber: dd.pageNumber }).then(
231
+ items => {
232
+ afterGetItems(items);
233
+ }
234
+ ).finally(() => {
235
+ dd.waitingItems = false;
236
+ dd.refreshState();
237
+ });
238
+ }
239
+ else {
240
+ afterGetItems();
241
+ }
242
+ }
243
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
244
+ close() {
245
+ const dd = this;
246
+
247
+ dd.items = [];
248
+ delete dd.activeItem;
249
+ delete dd.lastPageNumber;
250
+
251
+ super.close();
252
+ }
253
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
254
+ onItemClick(e, itemId) {
255
+ const dd = this;
256
+
257
+ if (itemId === 'append') {
258
+ dd.appendItems();
259
+ }
260
+ else {
261
+ if (dd.opt.onItemClick) {
262
+ dd.opt.onItemClick({ owner: dd.opt.owner, itemId: itemId, dropdown: dd, clientX: e.clientX, clientY: e.clientY, target: e.target });
263
+ }
264
+
265
+ const clickedItem = dd.items.find(function (item) {
266
+ return item.id === itemId;
267
+ });
268
+
269
+ if (!clickedItem || !clickedItem.noClose) {
270
+ dd.close();
271
+ }
272
+ }
273
+ }
274
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
275
+ setupEvents() {
276
+ const dd = this;
277
+
278
+ super.setupEvents();
279
+
280
+ function onKeyDown(e) {
281
+ const key = e && e.key ? e.key.toLowerCase() : '';
282
+
283
+ let ind;
284
+
285
+ switch (key) {
286
+ case 'enter':
287
+ if (!dd.activeItem) return;
288
+
289
+ dd.opt.onItemClick({ owner: dd.opt.owner, itemId: dd.activeItem.id, dropdown: dd });
290
+ dd.close();
291
+ break;
292
+ case 'down':
293
+ case 'arrowdown':
294
+ if (dd.activeItem) {
295
+ ind = dd.items.indexOf(dd.activeItem);
296
+
297
+ if (ind < 0 || ind === dd.items.length - 1) return;
298
+
299
+ dd.activeItem = dd.items[ind + 1];
300
+ }
301
+ else if (dd.items.length > 0) {
302
+ dd.activeItem = dd.items[0];
303
+ }
304
+
305
+ dd.refreshState();
306
+ break;
307
+ case 'up':
308
+ case 'arrowup':
309
+ if (dd.activeItem) {
310
+ ind = dd.items.indexOf(dd.activeItem);
311
+
312
+ if (ind <= 0) return;
313
+
314
+ dd.activeItem = dd.items[ind - 1];
315
+ }
316
+ else if (dd.items.length > 0) {
317
+ dd.activeItem = dd.items[0];
318
+ }
319
+
320
+ dd.refreshState();
321
+ break;
322
+ case 'tab':
323
+ dd.close();
324
+ break;
325
+ default:
326
+ break;
327
+ }
328
+ }
329
+
330
+ document.addEventListener('keydown', onKeyDown);
331
+
332
+ const remClearEvents = dd.clearEvents;
333
+ dd.clearEvents = function () {
334
+ remClearEvents();
335
+ document.removeEventListener('keydown', onKeyDown);
336
+ }
337
+ }
338
+ }
339
+ // ==================================================================================================================================================================