@selkit/dom 0.1.1 → 0.2.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/dist/index.cjs CHANGED
@@ -155,6 +155,11 @@ var SelkitDom = class {
155
155
  #dropdownParent;
156
156
  #templateSelection;
157
157
  #templateOption;
158
+ #templateArrow;
159
+ #templateClear;
160
+ #templateTagRemove;
161
+ #templateGroup;
162
+ #templateEmpty;
158
163
  #sourceSelect;
159
164
  #control;
160
165
  #field;
@@ -192,6 +197,11 @@ var SelkitDom = class {
192
197
  this.#dropdownParent = resolveParent(cfg.dropdownParent);
193
198
  this.#templateSelection = cfg.templateSelection;
194
199
  this.#templateOption = cfg.templateOption;
200
+ this.#templateArrow = cfg.templateArrow;
201
+ this.#templateClear = cfg.templateClear;
202
+ this.#templateTagRemove = cfg.templateTagRemove;
203
+ this.#templateGroup = cfg.templateGroup;
204
+ this.#templateEmpty = cfg.templateEmpty;
195
205
  this.controller = (0, import_core.createSelkit)(cfg);
196
206
  this.element = document.createElement("div");
197
207
  this.element.className = this.#prefix;
@@ -433,15 +443,18 @@ var SelkitDom = class {
433
443
  this.element.classList.toggle(this.#cls("", "open"), s.isOpen);
434
444
  this.element.classList.toggle(this.#cls("", "disabled"), s.disabled);
435
445
  }
436
- /** 套用 templateSelection 到已選容器 字串走 textContent Node 直接掛入 無模板則用 label */
446
+ /** 套用模板輸出:字串走 textContent(防 XSS)Node 直接掛入 */
447
+ #applyTemplate(host, out) {
448
+ if (out instanceof Node) host.append(out);
449
+ else host.textContent = out;
450
+ }
451
+ /** 套用 templateSelection 到已選容器 無模板則用 label */
437
452
  #fillSelection(host, option, meta) {
438
453
  if (!this.#templateSelection) {
439
454
  host.textContent = option.label;
440
455
  return;
441
456
  }
442
- const out = this.#templateSelection(option, meta);
443
- if (out instanceof Node) host.append(out);
444
- else host.textContent = out;
457
+ this.#applyTemplate(host, this.#templateSelection(option, meta));
445
458
  }
446
459
  #renderField(s) {
447
460
  for (const child of Array.from(this.#field.children)) {
@@ -462,7 +475,11 @@ var SelkitDom = class {
462
475
  remove.className = this.#cls("tag-remove");
463
476
  remove.dataset.index = String(i);
464
477
  remove.setAttribute("aria-label", `Remove ${opt.label}`);
465
- remove.textContent = "\xD7";
478
+ if (this.#templateTagRemove) {
479
+ this.#applyTemplate(remove, this.#templateTagRemove(opt, { index: i }));
480
+ } else {
481
+ remove.textContent = "\xD7";
482
+ }
466
483
  tag.append(label, remove);
467
484
  frag.append(tag);
468
485
  });
@@ -492,12 +509,19 @@ var SelkitDom = class {
492
509
  clear.type = "button";
493
510
  clear.className = this.#cls("clear");
494
511
  clear.setAttribute("aria-label", "Clear");
495
- clear.textContent = "\xD7";
512
+ if (this.#templateClear) {
513
+ this.#applyTemplate(clear, this.#templateClear());
514
+ } else {
515
+ clear.textContent = "\xD7";
516
+ }
496
517
  this.#indicators.append(clear);
497
518
  }
498
519
  const arrow = document.createElement("span");
499
520
  arrow.className = this.#cls("arrow");
500
521
  arrow.setAttribute("aria-hidden", "true");
522
+ if (this.#templateArrow) {
523
+ this.#applyTemplate(arrow, this.#templateArrow({ open: s.isOpen }));
524
+ }
501
525
  this.#indicators.append(arrow);
502
526
  }
503
527
  #renderOptions(s) {
@@ -507,7 +531,19 @@ var SelkitDom = class {
507
531
  if (view.rows.length === 0) {
508
532
  const empty = document.createElement("div");
509
533
  empty.className = this.#cls("empty");
510
- empty.textContent = this.controller.getEmptyMessage();
534
+ const message = this.controller.getEmptyMessage();
535
+ if (this.#templateEmpty) {
536
+ this.#applyTemplate(
537
+ empty,
538
+ this.#templateEmpty({
539
+ reason: this.controller.getEmptyReason(),
540
+ message,
541
+ query: s.query
542
+ })
543
+ );
544
+ } else {
545
+ empty.textContent = message;
546
+ }
511
547
  this.#dropdown.append(empty);
512
548
  return;
513
549
  }
@@ -536,7 +572,14 @@ var SelkitDom = class {
536
572
  const group = document.createElement("div");
537
573
  group.className = this.#cls("group");
538
574
  if (row.disabled) group.classList.add(this.#cls("group", "disabled"));
539
- group.textContent = row.label;
575
+ if (this.#templateGroup) {
576
+ this.#applyTemplate(
577
+ group,
578
+ this.#templateGroup({ label: row.label, disabled: !!row.disabled })
579
+ );
580
+ } else {
581
+ group.textContent = row.label;
582
+ }
540
583
  this.#dropdown.append(group);
541
584
  continue;
542
585
  }
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { SelkitController, SelkitConfig, SelkitOption } from '@selkit/core';
1
+ import { SelkitController, SelkitConfig, SelkitOption, SelkitEmptyReason } from '@selkit/core';
2
2
 
3
3
  interface SelkitDomConfig<T = unknown> extends SelkitConfig<T> {
4
4
  /** class 前綴 預設 "selkit" */
@@ -28,6 +28,27 @@ interface SelkitDomConfig<T = unknown> extends SelkitConfig<T> {
28
28
  active: boolean;
29
29
  selected: boolean;
30
30
  }) => string | Node;
31
+ /** 自訂下拉箭頭內容(▾)外殼保留 回傳 string 走 textContent Node 直接掛入 */
32
+ templateArrow?: (meta: {
33
+ open: boolean;
34
+ }) => string | Node;
35
+ /** 自訂清除鈕內容(×)按鈕外殼與 click 行為保留 */
36
+ templateClear?: () => string | Node;
37
+ /** 自訂多選標籤移除鈕內容(×)按鈕外殼與 click 行為保留 */
38
+ templateTagRemove?: (option: SelkitOption<T>, meta: {
39
+ index: number;
40
+ }) => string | Node;
41
+ /** 自訂分組標題內容 外殼保留 */
42
+ templateGroup?: (meta: {
43
+ label: string;
44
+ disabled: boolean;
45
+ }) => string | Node;
46
+ /** 自訂下拉為空/載入中的整塊內容 reason 分流 message 為預設文字 */
47
+ templateEmpty?: (meta: {
48
+ reason: SelkitEmptyReason;
49
+ message: string;
50
+ query: string;
51
+ }) => string | Node;
31
52
  }
32
53
  interface SelkitDomInstance<T = unknown> {
33
54
  readonly controller: SelkitController<T>;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { SelkitController, SelkitConfig, SelkitOption } from '@selkit/core';
1
+ import { SelkitController, SelkitConfig, SelkitOption, SelkitEmptyReason } from '@selkit/core';
2
2
 
3
3
  interface SelkitDomConfig<T = unknown> extends SelkitConfig<T> {
4
4
  /** class 前綴 預設 "selkit" */
@@ -28,6 +28,27 @@ interface SelkitDomConfig<T = unknown> extends SelkitConfig<T> {
28
28
  active: boolean;
29
29
  selected: boolean;
30
30
  }) => string | Node;
31
+ /** 自訂下拉箭頭內容(▾)外殼保留 回傳 string 走 textContent Node 直接掛入 */
32
+ templateArrow?: (meta: {
33
+ open: boolean;
34
+ }) => string | Node;
35
+ /** 自訂清除鈕內容(×)按鈕外殼與 click 行為保留 */
36
+ templateClear?: () => string | Node;
37
+ /** 自訂多選標籤移除鈕內容(×)按鈕外殼與 click 行為保留 */
38
+ templateTagRemove?: (option: SelkitOption<T>, meta: {
39
+ index: number;
40
+ }) => string | Node;
41
+ /** 自訂分組標題內容 外殼保留 */
42
+ templateGroup?: (meta: {
43
+ label: string;
44
+ disabled: boolean;
45
+ }) => string | Node;
46
+ /** 自訂下拉為空/載入中的整塊內容 reason 分流 message 為預設文字 */
47
+ templateEmpty?: (meta: {
48
+ reason: SelkitEmptyReason;
49
+ message: string;
50
+ query: string;
51
+ }) => string | Node;
31
52
  }
32
53
  interface SelkitDomInstance<T = unknown> {
33
54
  readonly controller: SelkitController<T>;
package/dist/index.js CHANGED
@@ -124,6 +124,11 @@ var SelkitDom = class {
124
124
  #dropdownParent;
125
125
  #templateSelection;
126
126
  #templateOption;
127
+ #templateArrow;
128
+ #templateClear;
129
+ #templateTagRemove;
130
+ #templateGroup;
131
+ #templateEmpty;
127
132
  #sourceSelect;
128
133
  #control;
129
134
  #field;
@@ -161,6 +166,11 @@ var SelkitDom = class {
161
166
  this.#dropdownParent = resolveParent(cfg.dropdownParent);
162
167
  this.#templateSelection = cfg.templateSelection;
163
168
  this.#templateOption = cfg.templateOption;
169
+ this.#templateArrow = cfg.templateArrow;
170
+ this.#templateClear = cfg.templateClear;
171
+ this.#templateTagRemove = cfg.templateTagRemove;
172
+ this.#templateGroup = cfg.templateGroup;
173
+ this.#templateEmpty = cfg.templateEmpty;
164
174
  this.controller = createSelkit(cfg);
165
175
  this.element = document.createElement("div");
166
176
  this.element.className = this.#prefix;
@@ -402,15 +412,18 @@ var SelkitDom = class {
402
412
  this.element.classList.toggle(this.#cls("", "open"), s.isOpen);
403
413
  this.element.classList.toggle(this.#cls("", "disabled"), s.disabled);
404
414
  }
405
- /** 套用 templateSelection 到已選容器 字串走 textContent Node 直接掛入 無模板則用 label */
415
+ /** 套用模板輸出:字串走 textContent(防 XSS)Node 直接掛入 */
416
+ #applyTemplate(host, out) {
417
+ if (out instanceof Node) host.append(out);
418
+ else host.textContent = out;
419
+ }
420
+ /** 套用 templateSelection 到已選容器 無模板則用 label */
406
421
  #fillSelection(host, option, meta) {
407
422
  if (!this.#templateSelection) {
408
423
  host.textContent = option.label;
409
424
  return;
410
425
  }
411
- const out = this.#templateSelection(option, meta);
412
- if (out instanceof Node) host.append(out);
413
- else host.textContent = out;
426
+ this.#applyTemplate(host, this.#templateSelection(option, meta));
414
427
  }
415
428
  #renderField(s) {
416
429
  for (const child of Array.from(this.#field.children)) {
@@ -431,7 +444,11 @@ var SelkitDom = class {
431
444
  remove.className = this.#cls("tag-remove");
432
445
  remove.dataset.index = String(i);
433
446
  remove.setAttribute("aria-label", `Remove ${opt.label}`);
434
- remove.textContent = "\xD7";
447
+ if (this.#templateTagRemove) {
448
+ this.#applyTemplate(remove, this.#templateTagRemove(opt, { index: i }));
449
+ } else {
450
+ remove.textContent = "\xD7";
451
+ }
435
452
  tag.append(label, remove);
436
453
  frag.append(tag);
437
454
  });
@@ -461,12 +478,19 @@ var SelkitDom = class {
461
478
  clear.type = "button";
462
479
  clear.className = this.#cls("clear");
463
480
  clear.setAttribute("aria-label", "Clear");
464
- clear.textContent = "\xD7";
481
+ if (this.#templateClear) {
482
+ this.#applyTemplate(clear, this.#templateClear());
483
+ } else {
484
+ clear.textContent = "\xD7";
485
+ }
465
486
  this.#indicators.append(clear);
466
487
  }
467
488
  const arrow = document.createElement("span");
468
489
  arrow.className = this.#cls("arrow");
469
490
  arrow.setAttribute("aria-hidden", "true");
491
+ if (this.#templateArrow) {
492
+ this.#applyTemplate(arrow, this.#templateArrow({ open: s.isOpen }));
493
+ }
470
494
  this.#indicators.append(arrow);
471
495
  }
472
496
  #renderOptions(s) {
@@ -476,7 +500,19 @@ var SelkitDom = class {
476
500
  if (view.rows.length === 0) {
477
501
  const empty = document.createElement("div");
478
502
  empty.className = this.#cls("empty");
479
- empty.textContent = this.controller.getEmptyMessage();
503
+ const message = this.controller.getEmptyMessage();
504
+ if (this.#templateEmpty) {
505
+ this.#applyTemplate(
506
+ empty,
507
+ this.#templateEmpty({
508
+ reason: this.controller.getEmptyReason(),
509
+ message,
510
+ query: s.query
511
+ })
512
+ );
513
+ } else {
514
+ empty.textContent = message;
515
+ }
480
516
  this.#dropdown.append(empty);
481
517
  return;
482
518
  }
@@ -505,7 +541,14 @@ var SelkitDom = class {
505
541
  const group = document.createElement("div");
506
542
  group.className = this.#cls("group");
507
543
  if (row.disabled) group.classList.add(this.#cls("group", "disabled"));
508
- group.textContent = row.label;
544
+ if (this.#templateGroup) {
545
+ this.#applyTemplate(
546
+ group,
547
+ this.#templateGroup({ label: row.label, disabled: !!row.disabled })
548
+ );
549
+ } else {
550
+ group.textContent = row.label;
551
+ }
509
552
  this.#dropdown.append(group);
510
553
  continue;
511
554
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selkit/dom",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Vanilla JS renderer for Selkit — DOM, events, a11y and default positioner.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "sideEffects": false,
21
21
  "dependencies": {
22
- "@selkit/core": "0.1.1"
22
+ "@selkit/core": "0.2.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "axe-core": "^4.12.1",