wunderbaum 0.0.1 → 0.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.
package/src/wb_options.ts CHANGED
@@ -7,8 +7,10 @@
7
7
  import {
8
8
  BoolOptionResolver,
9
9
  NavigationModeOption,
10
- WbEventType,
10
+ WbNodeEventType,
11
+ WbTreeEventType,
11
12
  } from "./common";
13
+ import { DndOptionsType } from "./wb_ext_dnd";
12
14
 
13
15
  export interface WbNodeData {
14
16
  title: string;
@@ -16,7 +18,7 @@ export interface WbNodeData {
16
18
  refKey?: string;
17
19
  expanded?: boolean;
18
20
  selected?: boolean;
19
- checkbox?: boolean | "radio" | BoolOptionResolver;
21
+ checkbox?: boolean | string;
20
22
  children?: Array<WbNodeData>;
21
23
  // ...any?: Any;
22
24
  }
@@ -34,6 +36,21 @@ export interface WbNodeData {
34
36
  * ...
35
37
  * });
36
38
  * ```
39
+ *
40
+ * Event handlers are also passed as callbacks
41
+ *
42
+ * ```js
43
+ * const tree = new mar10.Wunderbaum({
44
+ * ...
45
+ * init: (e) => {
46
+ * console.log(`Tree ${e.tree} was initialized and loaded.`)
47
+ * },
48
+ * activate: (e) => {
49
+ * console.log(`Node ${e.node} was activated.`)
50
+ * },
51
+ * ...
52
+ * });
53
+ * ```
37
54
  */
38
55
  export interface WunderbaumOptions {
39
56
  /**
@@ -62,56 +79,186 @@ export interface WunderbaumOptions {
62
79
  *
63
80
  * Default: `{}`.
64
81
  */
65
- types: any; //[key: string]: any;
82
+ types?: any; //[key: string]: any;
66
83
  /**
67
84
  * A list of maps that define column headers. If this option is set,
68
- * Wunderbaum becomes a tree-grid control instead of a plain tree.
85
+ * Wunderbaum becomes a treegrid control instead of a plain tree.
69
86
  * Column definitions can be passed as tree option, or be part of a `source`
70
87
  * response.
71
88
  * Default: `[]` meaning this is a plain tree.
72
89
  */
73
90
  columns?: Array<any>;
74
91
  /**
75
- *
92
+ * If true, add a `wb-skeleton` class to all nodes, that will result in a
93
+ * 'glow' effect. Typically used with initial dummy nodes, while loading the
94
+ * real data.
76
95
  * Default: false.
77
96
  */
78
- skeleton: false;
97
+ skeleton?: false;
98
+ /**
99
+ * Translation map for some system messages.
100
+ */
101
+ strings?: any; //[key: string] string;
102
+ /**
103
+ * 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
104
+ * Default: 3 (4 in local debug environment)
105
+ */
106
+ debugLevel: number;
107
+ /**
108
+ * Number of levels that are forced to be expanded, and have no expander icon.
109
+ * Default: 0
110
+ */
111
+ minExpandLevel?: number;
112
+ // escapeTitles: boolean;
113
+ /**
114
+ * Height of the header row div.
115
+ * Default: 22
116
+ */
117
+ headerHeightPx: number;
118
+ /**
119
+ * Height of a node row div.
120
+ * Default: 22
121
+ */
122
+ rowHeightPx?: number;
123
+ /**
124
+ * Collapse siblings when a node is expanded.
125
+ * Default: false
126
+ */
127
+ autoCollapse?: boolean;
128
+ /**
129
+ * Default: NavigationModeOption.startRow
130
+ */
131
+ navigationMode?: NavigationModeOption;
132
+ /**
133
+ * Show/hide header (pass bool or string)
134
+ */
135
+ header?: boolean | string | null;
79
136
  /**
80
137
  *
81
- * Default: false.
82
138
  */
83
- strings: any; //[key: string] string;
139
+ showSpinner?: boolean;
140
+ /**
141
+ * Default: true
142
+ */
143
+ checkbox?: boolean | "radio" | BoolOptionResolver;
144
+ /**
145
+ * Default: 200
146
+ */
147
+ updateThrottleWait?: number;
148
+ // --- KeyNav ---
84
149
  /**
150
+ * Default: true
85
151
  */
86
- debugLevel: 3;
87
- minExpandLevel: 0;
88
- escapeTitles: true;
89
- headerHeightPx: 22;
90
- autoCollapse: false;
152
+ quicksearch?: boolean;
153
+
91
154
  // --- Extensions ---
92
- dnd: any; // = {};
155
+ dnd?: DndOptionsType; // = {};
93
156
  filter: any; // = {};
157
+ grid: any; // = {};
158
+ // --- Events ---
94
159
  /**
95
- * A list of maps that define column headers. If this option is set,
96
- * Wunderbaum becomes a tree grid control instead of a plain tree.
97
- * Column definitions can be passed as tree option, or be part of a `source`
98
- * response.
160
+ *
161
+ * @category Callback
99
162
  */
100
- navigationMode?: NavigationModeOption;
101
- // --- Events ---
163
+ activate?: (e: WbNodeEventType) => void;
164
+ /**
165
+ *
166
+ * @category Callback
167
+ */
168
+ change?: (e: WbNodeEventType) => void;
169
+ /**
170
+ *
171
+ * Return `false` to prevent default handling, e.g. activating the node.
172
+ * @category Callback
173
+ */
174
+ click?: (e: WbTreeEventType) => void;
175
+ /**
176
+ *
177
+ * @category Callback
178
+ */
179
+ deactivate?: (e: WbNodeEventType) => void;
180
+ /**
181
+ *
182
+ * @category Callback
183
+ */
184
+ discard?: (e: WbNodeEventType) => void;
185
+ /**
186
+ *
187
+ * @category Callback
188
+ */
189
+ enhanceTitle?: (e: WbNodeEventType) => void;
190
+ /**
191
+ *
192
+ * @category Callback
193
+ */
194
+ error?: (e: WbTreeEventType) => void;
195
+ /**
196
+ *
197
+ * Check `e.flag` for status.
198
+ * @category Callback
199
+ */
200
+ focus?: (e: WbTreeEventType) => void;
102
201
  /**
103
- * Called after initial data was loaded and tree markup was rendered.
202
+ * Fires when the tree markup was created and the initial source data was loaded.
203
+ * Typical use cases would be activating a node, setting focus, enabling other
204
+ * controls on the page, etc.<br>
205
+ * Check `e.error` for status.
104
206
  * @category Callback
105
207
  */
106
- init?: (e: WbEventType) => void;
208
+ init?: (e: WbTreeEventType) => void;
107
209
  /**
108
- * Called after data was loaded from local storage.
210
+ *
211
+ * @category Callback
212
+ */
213
+ keydown?: (e: WbNodeEventType) => void;
214
+ /**
215
+ * Fires when a node that was marked 'lazy', is expanded for the first time.
216
+ * Typically we return an endpoint URL or the Promise of a fetch request that
217
+ * provides a (potentially nested) list of child nodes.
218
+ * @category Callback
219
+ */
220
+ lazyLoad?: (e: WbNodeEventType) => void;
221
+ /**
222
+ * Fires when data was loaded (initial request, reload, or lazy loading),
223
+ * after the data is applied and rendered.
224
+ * @category Callback
225
+ */
226
+ load?: (e: WbNodeEventType) => void;
227
+ /**
228
+ * @category Callback
229
+ */
230
+ modifyChild?: (e: WbNodeEventType) => void;
231
+ /**
232
+ * Fires when data was fetched (initial request, reload, or lazy loading),
233
+ * but before the data is applied and rendered.
234
+ * Here we can modify and adjust the received data, for example to convert an
235
+ * external response to native Wunderbaum syntax.
236
+ * @category Callback
237
+ */
238
+ receive?: (e: WbNodeEventType) => void;
239
+ /**
240
+ * Fires when a node is about to be displayed.
241
+ * The default HTML markup is already created, but not yet added to the DOM.
242
+ * Now we can tweak the markup, create HTML elements in this node's column
243
+ * cells, etc.
244
+ * See also `Custom Rendering` for details.
245
+ * @category Callback
246
+ */
247
+ render?: (e: WbNodeEventType) => void;
248
+ /**
249
+ *
250
+ * @category Callback
251
+ */
252
+ renderStatusNode?: (e: WbNodeEventType) => void;
253
+ /**
254
+ *
255
+ * Check `e.flag` for status.
109
256
  * @category Callback
110
257
  */
111
- update?: (e: WbEventType) => void;
258
+ select?: (e: WbNodeEventType) => void;
112
259
  /**
113
- * Called after data was loaded from local storage.
260
+ * Fires when the viewport content was updated, after scroling, expanding etc.
114
261
  * @category Callback
115
262
  */
116
- modifyChild?: (e: WbEventType) => void;
263
+ update?: (e: WbTreeEventType) => void;
117
264
  }
@@ -11,8 +11,11 @@ $error-color: #b5373b;
11
11
  $node-text-color: #56534c;
12
12
  $bg-highlight-color: #26a0da;
13
13
  $header-color: #dedede;
14
+ $background-color: white;
14
15
  $alternate-row-color: #fcfcfc;
15
16
  $alternate-row-color-even: #f7fcfe;
17
+ $focus-border-color: #275dc5;
18
+
16
19
  // derived
17
20
  $border-color: $node-text-color;
18
21
  $drop-source-color: adjust-color($node-text-color, $lightness: 50%);
@@ -22,14 +25,12 @@ $error-background-color: adjust-color($error-color, $lightness: 45%);
22
25
  // @debug $dim-color;
23
26
  $hover-color: adjust-color($bg-highlight-color, $lightness: 48%); // #f7f7f7;
24
27
  $hover-border-color: $hover-color;
25
- $focus-border-color: #969da3;
26
28
  $grid-color: #dedede;
27
29
  $active-color: #e5f3fb;
28
30
  $active-cell-color: $bg-highlight-color;
29
31
  $active-border-color: #70c0e7;
30
32
  $active-hover-color: #dceff8;
31
33
  $active-hover-border-color: $bg-highlight-color;
32
- $background-color: #fcfcfc;
33
34
  $active-column-color: $hover-color;
34
35
  $active-header-column-color: adjust-color($header-color, $lightness: -10%);
35
36
  // @debug $active-header-column-color;
@@ -60,6 +61,7 @@ $level-rainbow: rgba(255, 255, 232, 1), rgba(240, 255, 240, 1),
60
61
  div.wunderbaum {
61
62
  box-sizing: border-box;
62
63
  height: 100%; // fill parent container
64
+ background-color: $background-color;
63
65
  margin: 0;
64
66
  padding: 0;
65
67
 
@@ -67,14 +69,17 @@ div.wunderbaum {
67
69
  font-size: 14px;
68
70
 
69
71
  color: $node-text-color;
70
- border: 1px solid $border-color;
72
+ border: 2px solid $border-color;
73
+ border-radius: 4px;
74
+ background-clip: content-box; // Keep color outside rounded borders?
71
75
  overflow: hidden; // avoid unwanted effects when auto-resizing
72
76
 
73
77
  // Focus border is generated by the browsers per default:
74
- // &:focus,
75
- // &:focus-within {
76
- // border-color: $active-border-color;
77
- // }
78
+ &:focus,
79
+ &:focus-within {
80
+ border-color: $focus-border-color;
81
+ // outline-style: none;
82
+ }
78
83
 
79
84
  div.wb-scroll-container {
80
85
  position: relative;
@@ -103,42 +108,51 @@ div.wunderbaum {
103
108
  &.wb-selected {
104
109
  background-color: grayscale($active-color);
105
110
  border-color: grayscale($active-border-color);
111
+
106
112
  &:hover {
107
113
  background-color: grayscale($active-hover-color);
108
114
  }
109
115
  }
110
116
  }
111
117
  }
118
+
112
119
  div.wb-node-list {
113
120
  div.wb-row {
114
121
  &:hover {
115
122
  background-color: $hover-color;
116
123
  }
124
+
117
125
  &.wb-active,
118
126
  &.wb-selected {
119
127
  background-color: $active-color;
128
+
120
129
  // border-color: $active-border-color;
121
130
  &:hover {
122
131
  background-color: $active-hover-color;
123
132
  // border-color: $active-hover-border-color;
124
133
  }
125
134
  }
135
+
126
136
  &.wb-focus:not(.wb-active) {
127
137
  border-style: dotted;
128
138
  border-color: $active-border-color;
129
139
  }
140
+
130
141
  &.wb-active {
131
142
  // background-color: $active-hover-color;
132
143
  border-style: solid;
133
144
  border-color: $active-border-color;
145
+
134
146
  &:hover {
135
147
  // background-color: $active-hover-color;
136
148
  border-color: $active-hover-border-color;
137
149
  }
138
150
  }
151
+
139
152
  &.wb-loading {
140
153
  font-style: italic;
141
154
  }
155
+
142
156
  &.wb-dirty,
143
157
  .wb-col.wb-dirty {
144
158
  font-style: italic;
@@ -151,6 +165,7 @@ div.wunderbaum {
151
165
  );
152
166
  animation: wb-dirty-animation 2s linear infinite;
153
167
  }
168
+
154
169
  &.wb-error,
155
170
  &.wb-status-error {
156
171
  color: $error-color;
@@ -166,6 +181,7 @@ div.wunderbaum {
166
181
  border-bottom: 1px solid $border-color;
167
182
  padding: 0;
168
183
  background-color: $header-color;
184
+
169
185
  span.wb-col {
170
186
  font-weight: bold;
171
187
  // &::after {
@@ -177,14 +193,36 @@ div.wunderbaum {
177
193
  span.wb-col {
178
194
  position: absolute;
179
195
  display: inline-block;
180
- text-overflow: ellipsis;
196
+ // text-overflow: ellipsis;
181
197
  height: $row-inner-height;
182
198
  line-height: $row-inner-height;
183
199
  padding: 0 $col-padding-x;
184
200
  border-right: 1px solid $grid-color;
201
+
185
202
  &:last-of-type {
186
203
  border-right: none;
187
204
  }
205
+
206
+ overflow: visible; // allow resizer to overlap next col
207
+
208
+ span.wb-col-title {
209
+ width: 100%;
210
+ overflow: hidden;
211
+ text-overflow: ellipsis;
212
+ }
213
+
214
+ span.wb-col-resizer {
215
+ position: absolute;
216
+ top: 0;
217
+ // left: auto;
218
+ right: -1px;
219
+ width: 3px;
220
+ // float: right;
221
+ border: none;
222
+ border-right: 2px solid $border-color;
223
+ height: 100%;
224
+ cursor: col-resize;
225
+ }
188
226
  }
189
227
 
190
228
  span.wb-node {
@@ -208,18 +246,21 @@ div.wunderbaum {
208
246
  i.bi::before {
209
247
  vertical-align: baseline;
210
248
  }
249
+
211
250
  img.wb-icon {
212
251
  width: $icon-width;
213
252
  height: $icon-height;
214
253
  padding: $icon-padding-y $icon-padding-x;
215
254
  // margin-top: $icon-padding-y;
216
255
  }
256
+
217
257
  i.wb-indent {
218
258
  // border-left: 1px solid gray;
219
259
  &::before {
220
260
  content: "\00a0";
221
261
  }
222
262
  }
263
+
223
264
  i.wb-expander.wb-spin,
224
265
  i.wb-icon.wb-spin {
225
266
  height: unset; // Prevent wobble
@@ -248,68 +289,81 @@ div.wunderbaum {
248
289
  }
249
290
  }
250
291
  }
292
+
251
293
  &.wb-cell-mode div.wb-header div.wb-row span.wb-col {
252
294
  &.wb-active {
253
295
  background-color: $active-hover-color;
254
296
  }
255
297
  }
298
+
256
299
  div.wb-node-list div.wb-row {
257
300
  border-bottom-color: $grid-color;
301
+
258
302
  &:hover:not(.wb-active):not(.wb-selected) {
259
303
  background-color: $hover-color;
260
304
  // border-color: $hover-border-color;
261
305
  }
306
+
262
307
  &.wb-active {
263
308
  border-bottom-color: $active-border-color;
264
309
  }
310
+
265
311
  span.wb-col {
266
312
  border-right: 1px solid $grid-color;
267
313
 
268
314
  input.wb-input-edit,
269
- >input[type=color],
270
- >input[type=date],
271
- >input[type=datetime], // deprecated
272
- >input[type=datetime-local],
273
- >input[type=email],
274
- >input[type=month],
275
- >input[type=number],
276
- >input[type=password],
277
- >input[type=search],
278
- >input[type=tel],
279
- >input[type=text],
280
- >input[type=time],
281
- >input[type=url],
282
- >input[type=week],
283
- >select {
315
+ > input[type="color"],
316
+ > input[type="date"],
317
+ > input[type="datetime"],
318
+ > input[type="datetime-local"],
319
+ > input[type="email"],
320
+ > input[type="month"],
321
+ > input[type="number"],
322
+ > input[type="password"],
323
+ > input[type="search"],
324
+ > input[type="tel"],
325
+ > input[type="text"],
326
+ > input[type="time"],
327
+ > input[type="url"],
328
+ > input[type="week"],
329
+ > select {
284
330
  width: 100%;
285
331
  }
286
332
  }
287
333
  }
334
+
288
335
  &.wb-cell-mode {
289
336
  div.wb-row {
290
337
  background-color: $background-color;
338
+
291
339
  span.wb-col.wb-active {
292
340
  background-color: $active-column-color;
293
341
  }
342
+
294
343
  &.wb-active {
295
344
  background-color: $active-column-color;
345
+
296
346
  span.wb-col.wb-active {
297
347
  background-color: $active-cell-color;
298
348
  }
299
349
  }
300
350
  }
351
+
301
352
  &.wb-cell-edit-mode div.wb-row.wb-active span.wb-col.wb-active {
302
353
  background-color: $error-color;
303
354
  }
304
355
  }
356
+
305
357
  &.wb-alternate div.wb-node-list {
306
358
  div.wb-row:nth-of-type(even):not(.wb-active):not(.wb-selected) {
307
359
  background-color: $alternate-row-color;
360
+
308
361
  &:hover {
309
362
  background-color: $alternate-row-color-even;
310
363
  }
311
364
  }
312
365
  }
366
+
313
367
  // Dimm some colors if tree has no focus
314
368
  &:not(:focus-within),
315
369
  &:not(:focus) {
@@ -324,30 +378,38 @@ div.wunderbaum {
324
378
  &.wb-ext-filter-hide {
325
379
  div.wb-node-list div.wb-row {
326
380
  color: $filter-dim-color;
381
+
327
382
  &.wb-submatch {
328
383
  color: $filter-submatch-color;
329
384
  }
385
+
330
386
  &.wb-match {
331
387
  color: $node-text-color;
332
388
  }
333
389
  }
334
390
  }
391
+
335
392
  /* --- DND --- */
336
393
  div.wb-row.wb-drag-source {
337
394
  opacity: 0.5;
395
+
338
396
  .wb-node {
339
397
  background-color: $drop-source-color;
340
398
  }
341
399
  }
400
+
342
401
  div.wb-row.wb-drop-target {
343
402
  overflow: visible;
403
+
344
404
  .wb-node {
345
405
  background-color: $drop-target-color;
346
406
  overflow: visible;
407
+
347
408
  // z-index: 1000;
348
409
  .wb-icon {
349
410
  position: relative;
350
411
  overflow: visible;
412
+
351
413
  &::after {
352
414
  // use ::after, because ::before is used by icon fonts
353
415
  // vertical-align: baseline;
@@ -360,11 +422,13 @@ div.wunderbaum {
360
422
  }
361
423
  }
362
424
  }
425
+
363
426
  div.wb-row.wb-drop-target.wb-drop-before .wb-node .wb-icon::after {
364
427
  content: url(../docs/assets/drop_marker_insert_16x64.png);
365
428
  left: 0; // $icon-outer-width * 1.5;
366
429
  top: ($row-outer-height - 16) / 2 - $row-outer-height / 2;
367
430
  }
431
+
368
432
  div.wb-row.wb-drop-target.wb-drop-after .wb-node .wb-icon::after {
369
433
  content: url(../docs/assets/drop_marker_insert_16x64.png);
370
434
  left: 0; //$icon-outer-width * 1.5;
@@ -373,6 +437,7 @@ div.wunderbaum {
373
437
 
374
438
  &.wb-rainbow {
375
439
  @for $i from 1 through length($level-rainbow) {
440
+ i.wb-expander:nth-child(#{length($level-rainbow)}n + #{$i}),
376
441
  i.wb-indent:nth-child(#{length($level-rainbow)}n + #{$i}) {
377
442
  background: nth($level-rainbow, $i);
378
443
  }
@@ -382,16 +447,20 @@ div.wunderbaum {
382
447
  /* Fade out expanders, when container is not hovered or active */
383
448
  &.wb-fade-expander {
384
449
  i.wb-expander {
385
- transition: opacity 1.5s;
386
- opacity: 0;
450
+ // only text-alpha is animated, since we want to keep the background color
451
+ // transition: opacity 1.5s;
452
+ // opacity: 0;
453
+ transition: color 1.5s;
454
+ color: rgba($node-text-color, 0);
387
455
  }
456
+
388
457
  div.wb-row.wb-loading i.wb-expander,
389
458
  &:hover i.wb-expander,
390
459
  &:focus i.wb-expander,
391
460
  &:focus-within i.wb-expander,
392
461
  [class*="wb-statusnode-"] i.wb-expander {
393
- transition: opacity 0.6s;
394
- opacity: 1;
462
+ transition: color 0.6s;
463
+ color: $node-text-color;
395
464
  }
396
465
  }
397
466
 
@@ -412,10 +481,12 @@ div.wunderbaum {
412
481
  i.wb-checkbox {
413
482
  visibility: hidden;
414
483
  }
484
+
415
485
  .wb-row:hover i.wb-checkbox,
416
486
  .wb-row.wb-selected i.wb-checkbox {
417
487
  visibility: unset;
418
488
  }
489
+
419
490
  &:focus,
420
491
  &:focus-within {
421
492
  .wb-row.wb-active i.wb-checkbox {
@@ -423,28 +494,36 @@ div.wunderbaum {
423
494
  }
424
495
  }
425
496
  }
426
- } // div.wunderbaum
497
+ }
498
+
499
+ // div.wunderbaum
427
500
 
428
501
  /* --- TOOL CLASSES --- */
429
502
 
430
503
  .wb-helper-center {
431
504
  text-align: center;
432
505
  }
506
+
433
507
  .wb-helper-disabled {
434
508
  color: $dim-color;
435
509
  }
510
+
436
511
  .wb-helper-hidden {
437
512
  display: none;
438
513
  }
514
+
439
515
  .wb-helper-invalid {
440
516
  color: $error-color;
441
517
  }
518
+
442
519
  .wb-helper-lazy-expander {
443
520
  color: $bg-highlight-color;
444
521
  }
522
+
445
523
  .wb-helper-link {
446
524
  cursor: pointer;
447
525
  }
526
+
448
527
  // .wb-helper-edit-text {
449
528
  // // content-editable: true;
450
529
  // }
@@ -453,17 +532,21 @@ div.wunderbaum {
453
532
  .wb-helper-start {
454
533
  text-align: left;
455
534
  }
535
+
456
536
  .wb-helper-end {
457
537
  text-align: right;
458
538
  }
539
+
459
540
  .wb-rtl {
460
541
  .wb-helper-start {
461
542
  text-align: right;
462
543
  }
544
+
463
545
  .wb-helper-end {
464
546
  text-align: left;
465
547
  }
466
548
  }
549
+
467
550
  /* Class 'wb-tristate' is used to mark checkboxes that should toggle like
468
551
  * indeterminate -> checked -> unchecked -> indeterminate ...
469
552
  */
@@ -485,6 +568,7 @@ div.wunderbaum {
485
568
  0% {
486
569
  transform: rotate(0deg);
487
570
  }
571
+
488
572
  to {
489
573
  transform: rotate(1turn);
490
574
  }
@@ -494,6 +578,7 @@ div.wunderbaum {
494
578
  0% {
495
579
  background-color: hsl(200, 20%, 70%);
496
580
  }
581
+
497
582
  100% {
498
583
  background-color: hsl(200, 20%, 95%);
499
584
  }
@@ -503,6 +588,7 @@ div.wunderbaum {
503
588
  0% {
504
589
  background-position: 0 0;
505
590
  }
591
+
506
592
  100% {
507
593
  background-position: -70px 0;
508
594
  }