@ship-ui/core 0.22.14 → 0.22.15

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.
Files changed (72) hide show
  1. package/README.md +46 -0
  2. package/assets/mcp/components.json +578 -1
  3. package/bin/src/scanner.zig +9 -18
  4. package/fesm2022/ship-ui-core-sh-form-field-experimental.mjs +17 -2
  5. package/fesm2022/ship-ui-core-sh-form-field-experimental.mjs.map +1 -1
  6. package/fesm2022/ship-ui-core-ship-a11y-keybindings.mjs +433 -0
  7. package/fesm2022/ship-ui-core-ship-a11y-keybindings.mjs.map +1 -0
  8. package/fesm2022/ship-ui-core-ship-accordion.mjs +1 -0
  9. package/fesm2022/ship-ui-core-ship-accordion.mjs.map +1 -1
  10. package/fesm2022/ship-ui-core-ship-alert.mjs +3 -2
  11. package/fesm2022/ship-ui-core-ship-alert.mjs.map +1 -1
  12. package/fesm2022/ship-ui-core-ship-blueprint.mjs +14 -9
  13. package/fesm2022/ship-ui-core-ship-blueprint.mjs.map +1 -1
  14. package/fesm2022/ship-ui-core-ship-checkbox.mjs +16 -14
  15. package/fesm2022/ship-ui-core-ship-checkbox.mjs.map +1 -1
  16. package/fesm2022/ship-ui-core-ship-color-picker.mjs +3 -1
  17. package/fesm2022/ship-ui-core-ship-color-picker.mjs.map +1 -1
  18. package/fesm2022/ship-ui-core-ship-datepicker.mjs +51 -29
  19. package/fesm2022/ship-ui-core-ship-datepicker.mjs.map +1 -1
  20. package/fesm2022/ship-ui-core-ship-dialog.mjs +10 -5
  21. package/fesm2022/ship-ui-core-ship-dialog.mjs.map +1 -1
  22. package/fesm2022/ship-ui-core-ship-divider.mjs +4 -2
  23. package/fesm2022/ship-ui-core-ship-divider.mjs.map +1 -1
  24. package/fesm2022/ship-ui-core-ship-editor.mjs +2673 -0
  25. package/fesm2022/ship-ui-core-ship-editor.mjs.map +1 -0
  26. package/fesm2022/ship-ui-core-ship-icon.mjs +2 -2
  27. package/fesm2022/ship-ui-core-ship-icon.mjs.map +1 -1
  28. package/fesm2022/ship-ui-core-ship-list.mjs +4 -2
  29. package/fesm2022/ship-ui-core-ship-list.mjs.map +1 -1
  30. package/fesm2022/ship-ui-core-ship-menu.mjs +8 -5
  31. package/fesm2022/ship-ui-core-ship-menu.mjs.map +1 -1
  32. package/fesm2022/ship-ui-core-ship-popover.mjs +10 -5
  33. package/fesm2022/ship-ui-core-ship-popover.mjs.map +1 -1
  34. package/fesm2022/ship-ui-core-ship-progress-bar.mjs +5 -1
  35. package/fesm2022/ship-ui-core-ship-progress-bar.mjs.map +1 -1
  36. package/fesm2022/ship-ui-core-ship-radio.mjs +16 -14
  37. package/fesm2022/ship-ui-core-ship-radio.mjs.map +1 -1
  38. package/fesm2022/ship-ui-core-ship-select.mjs +9 -9
  39. package/fesm2022/ship-ui-core-ship-select.mjs.map +1 -1
  40. package/fesm2022/ship-ui-core-ship-sidenav.mjs +2 -2
  41. package/fesm2022/ship-ui-core-ship-sidenav.mjs.map +1 -1
  42. package/fesm2022/ship-ui-core-ship-spinner.mjs +3 -1
  43. package/fesm2022/ship-ui-core-ship-spinner.mjs.map +1 -1
  44. package/fesm2022/ship-ui-core-ship-spotlight.mjs +77 -24
  45. package/fesm2022/ship-ui-core-ship-spotlight.mjs.map +1 -1
  46. package/fesm2022/ship-ui-core-ship-table.mjs +94 -119
  47. package/fesm2022/ship-ui-core-ship-table.mjs.map +1 -1
  48. package/fesm2022/ship-ui-core-ship-theme-toggle.mjs +2 -2
  49. package/fesm2022/ship-ui-core-ship-theme-toggle.mjs.map +1 -1
  50. package/fesm2022/ship-ui-core-ship-toggle-card.mjs +24 -3
  51. package/fesm2022/ship-ui-core-ship-toggle-card.mjs.map +1 -1
  52. package/fesm2022/ship-ui-core-ship-toggle.mjs +16 -14
  53. package/fesm2022/ship-ui-core-ship-toggle.mjs.map +1 -1
  54. package/fesm2022/ship-ui-core-ship-tree.mjs +2 -2
  55. package/fesm2022/ship-ui-core-ship-tree.mjs.map +1 -1
  56. package/fesm2022/ship-ui-core-ship-virtual-scroll.mjs +2 -2
  57. package/fesm2022/ship-ui-core-ship-virtual-scroll.mjs.map +1 -1
  58. package/fesm2022/ship-ui-core.mjs +36 -23
  59. package/fesm2022/ship-ui-core.mjs.map +1 -1
  60. package/package.json +33 -2
  61. package/types/ship-ui-core-sh-form-field-experimental.d.ts +2 -0
  62. package/types/ship-ui-core-ship-a11y-keybindings.d.ts +102 -0
  63. package/types/ship-ui-core-ship-blueprint.d.ts +1 -1
  64. package/types/ship-ui-core-ship-checkbox.d.ts +2 -1
  65. package/types/ship-ui-core-ship-editor.d.ts +168 -0
  66. package/types/ship-ui-core-ship-radio.d.ts +2 -1
  67. package/types/ship-ui-core-ship-spotlight.d.ts +1 -1
  68. package/types/ship-ui-core-ship-table.d.ts +1 -0
  69. package/types/ship-ui-core-ship-toggle-card.d.ts +1 -0
  70. package/types/ship-ui-core-ship-toggle.d.ts +2 -1
  71. package/types/ship-ui-core.d.ts +3 -0
  72. package/bin/ship-fg-scanner +0 -0
@@ -18,28 +18,19 @@ const PackageJson = struct {
18
18
  libraryIcons: ?[][]const u8 = null,
19
19
  };
20
20
 
21
- pub fn main() !void {
22
- var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
23
- defer arena.deinit();
24
- const alloc = arena.allocator();
21
+ pub fn main(init: std.process.Init) !void {
22
+ const alloc = init.arena.allocator();
25
23
 
26
- var arg_it = try std.process.argsWithAllocator(alloc);
27
- defer arg_it.deinit();
24
+ const args = try init.minimal.args.toSlice(alloc);
28
25
 
29
- _ = arg_it.skip(); // skip executable name
30
-
31
- const target_dir = arg_it.next() orelse {
32
- std.debug.print("Usage: scanner <target_dir> <shipui_dir> <consumer_dir>\n", .{});
33
- std.process.exit(1);
34
- };
35
- const shipui_dir = arg_it.next() orelse {
36
- std.debug.print("Usage: scanner <target_dir> <shipui_dir> <consumer_dir>\n", .{});
37
- std.process.exit(1);
38
- };
39
- const consumer_dir = arg_it.next() orelse {
26
+ if (args.len < 4) {
40
27
  std.debug.print("Usage: scanner <target_dir> <shipui_dir> <consumer_dir>\n", .{});
41
28
  std.process.exit(1);
42
- };
29
+ }
30
+
31
+ const target_dir = args[1];
32
+ const shipui_dir = args[2];
33
+ const consumer_dir = args[3];
43
34
 
44
35
  var unique_icons = std.StringHashMap(void).init(alloc);
45
36
 
@@ -1,14 +1,29 @@
1
1
  import * as i0 from '@angular/core';
2
- import { effect, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { inject, ElementRef, effect, afterNextRender, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { createFormInputSignal } from '@ship-ui/core';
4
4
 
5
5
  class ShipFormFieldExperimental {
6
+ #selfRef;
6
7
  constructor() {
8
+ this.#selfRef = inject(ElementRef);
7
9
  this.firstInput = createFormInputSignal();
8
10
  this.hello = effect(() => {
9
11
  console.log('hello', this.firstInput());
10
12
  }, /* @ts-ignore */
11
13
  ...(ngDevMode ? [{ debugName: "hello" }] : /* istanbul ignore next */ []));
14
+ afterNextRender(() => {
15
+ const el = this.#selfRef.nativeElement;
16
+ const inputEl = el.querySelector('input') || el.querySelector('textarea');
17
+ const labelEl = el.querySelector('label');
18
+ if (inputEl) {
19
+ if (!inputEl.id) {
20
+ inputEl.id = `sh-input-${Math.random().toString(36).substring(2, 9)}`;
21
+ }
22
+ if (labelEl && !labelEl.getAttribute('for')) {
23
+ labelEl.setAttribute('for', inputEl.id);
24
+ }
25
+ }
26
+ });
12
27
  }
13
28
  myClick() {
14
29
  this.firstInput.update((x) => x + 'hello');
@@ -32,7 +47,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImpor
32
47
  `,
33
48
  changeDetection: ChangeDetectionStrategy.OnPush,
34
49
  }]
35
- }] });
50
+ }], ctorParameters: () => [] });
36
51
 
37
52
  /**
38
53
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ship-ui-core-sh-form-field-experimental.mjs","sources":["../../../projects/ship-ui/sh-form-field-experimental/sh-form-field-experimental.ts","../../../projects/ship-ui/sh-form-field-experimental/ship-ui-core-sh-form-field-experimental.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, effect } from '@angular/core';\nimport { createFormInputSignal } from '@ship-ui/core';\n\n@Component({\n selector: 'sh-form-field-experimental',\n imports: [],\n template: `\n <ng-content></ng-content>\n\n <button (click)=\"myClick()\">hello</button>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ShipFormFieldExperimental {\n firstInput = createFormInputSignal();\n\n hello = effect(() => {\n console.log('hello', this.firstInput());\n });\n\n myClick() {\n this.firstInput.update((x) => x + 'hello');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;MAaa,yBAAyB,CAAA;AAVtC,IAAA,WAAA,GAAA;QAWE,IAAA,CAAA,UAAU,GAAG,qBAAqB,EAAE;AAEpC,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,MAAK;YAClB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QACzC,CAAC;kFAAC;AAKH,IAAA;IAHC,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAC5C;8GATW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAzB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,4BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAP1B;;;;AAIT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;2FAGU,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAVrC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,4BAA4B;AACtC,oBAAA,OAAO,EAAE,EAAE;AACX,oBAAA,QAAQ,EAAE;;;;AAIT,EAAA,CAAA;oBACD,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAChD,iBAAA;;;ACZD;;AAEG;;;;"}
1
+ {"version":3,"file":"ship-ui-core-sh-form-field-experimental.mjs","sources":["../../../projects/ship-ui/sh-form-field-experimental/sh-form-field-experimental.ts","../../../projects/ship-ui/sh-form-field-experimental/ship-ui-core-sh-form-field-experimental.ts"],"sourcesContent":["import { afterNextRender, ChangeDetectionStrategy, Component, effect, ElementRef, inject } from '@angular/core';\nimport { createFormInputSignal } from '@ship-ui/core';\n\n@Component({\n selector: 'sh-form-field-experimental',\n imports: [],\n template: `\n <ng-content></ng-content>\n\n <button (click)=\"myClick()\">hello</button>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ShipFormFieldExperimental {\n #selfRef = inject(ElementRef);\n firstInput = createFormInputSignal();\n\n hello = effect(() => {\n console.log('hello', this.firstInput());\n });\n\n constructor() {\n afterNextRender(() => {\n const el = this.#selfRef.nativeElement;\n const inputEl = el.querySelector('input') || el.querySelector('textarea');\n const labelEl = el.querySelector('label');\n\n if (inputEl) {\n if (!inputEl.id) {\n inputEl.id = `sh-input-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n if (labelEl && !labelEl.getAttribute('for')) {\n labelEl.setAttribute('for', inputEl.id);\n }\n }\n });\n }\n\n myClick() {\n this.firstInput.update((x) => x + 'hello');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;MAaa,yBAAyB,CAAA;AACpC,IAAA,QAAQ;AAOR,IAAA,WAAA,GAAA;AAPA,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC;QAC7B,IAAA,CAAA,UAAU,GAAG,qBAAqB,EAAE;AAEpC,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,MAAK;YAClB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QACzC,CAAC;kFAAC;QAGA,eAAe,CAAC,MAAK;AACnB,YAAA,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa;AACtC,YAAA,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,aAAa,CAAC,UAAU,CAAC;YACzE,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC;YAEzC,IAAI,OAAO,EAAE;AACX,gBAAA,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE;oBACf,OAAO,CAAC,EAAE,GAAG,CAAA,SAAA,EAAY,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA,CAAE;gBACvE;gBAEA,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE;oBAC3C,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;gBACzC;YACF;AACF,QAAA,CAAC,CAAC;IACJ;IAEA,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAC5C;8GA5BW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAzB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,4BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAP1B;;;;AAIT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;2FAGU,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAVrC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,4BAA4B;AACtC,oBAAA,OAAO,EAAE,EAAE;AACX,oBAAA,QAAQ,EAAE;;;;AAIT,EAAA,CAAA;oBACD,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAChD,iBAAA;;;ACZD;;AAEG;;;;"}
@@ -0,0 +1,433 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, PLATFORM_ID, Injectable, ElementRef, Renderer2, input, output, effect, HostListener, Directive } from '@angular/core';
3
+ import { isPlatformBrowser } from '@angular/common';
4
+
5
+ const SHIP_A11Y_KEYBINDINGS_OVERRIDE = new InjectionToken('SHIP_A11Y_KEYBINDINGS_OVERRIDE');
6
+
7
+ /**
8
+ * Parses a keybinding shortcut string shorthand into a structured ParsedKeybinding object.
9
+ * Supporting modifiers: ctrlOrCmd (OS adaptive), ctrl, cmd/meta, alt/option, shift.
10
+ */
11
+ function parseKeybinding(shorthand, isMacPlatform) {
12
+ const parts = shorthand.split('+').map(p => p.trim().toLowerCase());
13
+ let ctrlKey = false;
14
+ let metaKey = false;
15
+ let altKey = false;
16
+ let shiftKey = false;
17
+ // The last part is the key/code
18
+ const keyPart = parts[parts.length - 1] || '';
19
+ // Parse any preceding parts as modifier keys
20
+ for (let i = 0; i < parts.length - 1; i++) {
21
+ const modifier = parts[i];
22
+ if (modifier === 'ctrlorcmd') {
23
+ if (isMacPlatform) {
24
+ metaKey = true;
25
+ }
26
+ else {
27
+ ctrlKey = true;
28
+ }
29
+ }
30
+ else if (modifier === 'ctrl') {
31
+ ctrlKey = true;
32
+ }
33
+ else if (modifier === 'meta' || modifier === 'cmd') {
34
+ metaKey = true;
35
+ }
36
+ else if (modifier === 'alt' || modifier === 'option') {
37
+ altKey = true;
38
+ }
39
+ else if (modifier === 'shift') {
40
+ shiftKey = true;
41
+ }
42
+ }
43
+ let key;
44
+ let code;
45
+ if (keyPart.startsWith('key') || keyPart.startsWith('digit')) {
46
+ code = keyPart; // Matches event.code (case-insensitive)
47
+ }
48
+ else {
49
+ // Map standard key names to event.key representation
50
+ if (keyPart === 'enter') {
51
+ key = 'enter';
52
+ }
53
+ else if (keyPart === 'esc' || keyPart === 'escape') {
54
+ key = 'escape';
55
+ }
56
+ else if (keyPart === 'space') {
57
+ key = ' ';
58
+ }
59
+ else if (keyPart === 'tab') {
60
+ key = 'tab';
61
+ }
62
+ else if (keyPart === 'backspace') {
63
+ key = 'backspace';
64
+ }
65
+ else if (keyPart === 'delete') {
66
+ key = 'delete';
67
+ }
68
+ else if (keyPart === 'up' || keyPart === 'arrowup') {
69
+ key = 'arrowup';
70
+ }
71
+ else if (keyPart === 'down' || keyPart === 'arrowdown') {
72
+ key = 'arrowdown';
73
+ }
74
+ else if (keyPart === 'left' || keyPart === 'arrowleft') {
75
+ key = 'arrowleft';
76
+ }
77
+ else if (keyPart === 'right' || keyPart === 'arrowright') {
78
+ key = 'arrowright';
79
+ }
80
+ else if (keyPart === 'home') {
81
+ key = 'home';
82
+ }
83
+ else if (keyPart === 'end') {
84
+ key = 'end';
85
+ }
86
+ else if (keyPart === 'pageup') {
87
+ key = 'pageup';
88
+ }
89
+ else if (keyPart === 'pagedown') {
90
+ key = 'pagedown';
91
+ }
92
+ else {
93
+ key = keyPart; // Raw key name
94
+ }
95
+ }
96
+ return { ctrlKey, metaKey, altKey, shiftKey, key, code };
97
+ }
98
+ /**
99
+ * Checks if a KeyboardEvent matches a ParsedKeybinding structure.
100
+ */
101
+ function matchKeybinding(event, parsed) {
102
+ if (event.ctrlKey !== parsed.ctrlKey)
103
+ return false;
104
+ if (event.metaKey !== parsed.metaKey)
105
+ return false;
106
+ if (event.altKey !== parsed.altKey)
107
+ return false;
108
+ if (event.shiftKey !== parsed.shiftKey)
109
+ return false;
110
+ if (parsed.code) {
111
+ return event.code.toLowerCase() === parsed.code;
112
+ }
113
+ if (parsed.key) {
114
+ if (parsed.key === ' ') {
115
+ return event.key === ' ' || event.key === 'Spacebar';
116
+ }
117
+ return event.key.toLowerCase() === parsed.key;
118
+ }
119
+ return false;
120
+ }
121
+ /**
122
+ * Formats a shorthand shortcut string into an OS-specific user-friendly string for display.
123
+ * E.g. 'ctrlOrCmd+Shift+KeyK' -> '⌘⇧K' on macOS, and 'Ctrl+Shift+K' on Windows/Linux.
124
+ */
125
+ function formatShortcut(shorthand, isMacPlatform) {
126
+ const parts = shorthand.split('+').map(p => p.trim());
127
+ const formattedParts = [];
128
+ for (let i = 0; i < parts.length; i++) {
129
+ const part = parts[i];
130
+ const lowerPart = part.toLowerCase();
131
+ if (i < parts.length - 1) {
132
+ if (lowerPart === 'ctrlorcmd') {
133
+ formattedParts.push(isMacPlatform ? '⌘' : 'Ctrl');
134
+ }
135
+ else if (lowerPart === 'ctrl') {
136
+ formattedParts.push(isMacPlatform ? '⌃' : 'Ctrl');
137
+ }
138
+ else if (lowerPart === 'meta' || lowerPart === 'cmd') {
139
+ formattedParts.push(isMacPlatform ? '⌘' : 'Win');
140
+ }
141
+ else if (lowerPart === 'alt' || lowerPart === 'option') {
142
+ formattedParts.push(isMacPlatform ? '⌥' : 'Alt');
143
+ }
144
+ else if (lowerPart === 'shift') {
145
+ formattedParts.push(isMacPlatform ? '⇧' : 'Shift');
146
+ }
147
+ }
148
+ else {
149
+ // Key/code part
150
+ if (lowerPart.startsWith('key')) {
151
+ formattedParts.push(part.substring(3).toUpperCase()); // E.g. KeyA -> A
152
+ }
153
+ else if (lowerPart.startsWith('digit')) {
154
+ formattedParts.push(part.substring(5)); // E.g. Digit1 -> 1
155
+ }
156
+ else {
157
+ // Capitalize first letter of standard keys
158
+ const capitalized = part.charAt(0).toUpperCase() + part.slice(1);
159
+ formattedParts.push(capitalized);
160
+ }
161
+ }
162
+ }
163
+ if (isMacPlatform) {
164
+ return formattedParts.join('');
165
+ }
166
+ else {
167
+ return formattedParts.join('+');
168
+ }
169
+ }
170
+
171
+ const DEFAULT_KEYBINDINGS = {
172
+ // Datepicker
173
+ 'datepicker.prev-month': 'PageUp',
174
+ 'datepicker.next-month': 'PageDown',
175
+ 'datepicker.prev-year': 'Shift+PageUp',
176
+ 'datepicker.next-year': 'Shift+PageDown',
177
+ 'datepicker.month-start': 'Home',
178
+ 'datepicker.month-end': 'End',
179
+ 'datepicker.day-next': 'ArrowRight, d',
180
+ 'datepicker.day-prev': 'ArrowLeft, a',
181
+ 'datepicker.week-next': 'ArrowDown, s',
182
+ 'datepicker.week-prev': 'ArrowUp, w',
183
+ // Selection Group (Tabs, Steppers, Button Groups)
184
+ 'selection-group.next': 'ArrowRight, ArrowDown, d, s',
185
+ 'selection-group.prev': 'ArrowLeft, ArrowUp, a, w',
186
+ 'selection-group.select': 'Enter, space',
187
+ // Select
188
+ 'select.next': 'ArrowDown, s',
189
+ 'select.prev': 'ArrowUp, w',
190
+ 'select.select': 'Enter, space',
191
+ 'select.close': 'Escape',
192
+ // Menu
193
+ 'menu.next': 'ArrowDown, s',
194
+ 'menu.prev': 'ArrowUp, w',
195
+ 'menu.open-submenu': 'ArrowRight, d',
196
+ 'menu.close-submenu': 'ArrowLeft, a',
197
+ 'menu.select': 'Enter, space',
198
+ // Spotlight
199
+ 'spotlight.next': 'ArrowDown, s',
200
+ 'spotlight.prev': 'ArrowUp, w',
201
+ 'spotlight.open': 'ctrlOrCmd+k',
202
+ // Dialog & Popover
203
+ 'dialog.close': 'Escape',
204
+ 'popover.close': 'Escape',
205
+ // Toggle Card
206
+ 'toggle-card.toggle': 'Enter, space',
207
+ // Form Controls
208
+ 'checkbox.toggle': 'Enter, space',
209
+ 'toggle.toggle': 'Enter, space',
210
+ 'radio.select': 'Enter, space',
211
+ // Table
212
+ 'table.sort': 'Enter, space',
213
+ // Blueprint
214
+ 'blueprint.cancel': 'Escape',
215
+ // Editor Toolbar
216
+ 'editor-toolbar.next': 'ArrowRight, ArrowDown',
217
+ 'editor-toolbar.prev': 'ArrowLeft, ArrowUp',
218
+ 'editor-toolbar.home': 'Home',
219
+ 'editor-toolbar.end': 'End',
220
+ };
221
+ class ShipA11yKeybindingsService {
222
+ #platformId = inject(PLATFORM_ID);
223
+ #overrides = inject(SHIP_A11Y_KEYBINDINGS_OVERRIDE, { optional: true });
224
+ // Store of action names to active shortcut strings
225
+ #bindings = new Map();
226
+ // Store of action names to default shortcut strings
227
+ #defaults = new Map();
228
+ /**
229
+ * Returns true if the platform is running on macOS.
230
+ */
231
+ get isMac() {
232
+ if (!isPlatformBrowser(this.#platformId))
233
+ return false;
234
+ return navigator.userAgent.toLowerCase().includes('mac');
235
+ }
236
+ constructor() {
237
+ this.registerDefaults(DEFAULT_KEYBINDINGS);
238
+ if (this.#overrides) {
239
+ this.registerOverrides(this.#overrides);
240
+ }
241
+ }
242
+ /**
243
+ * Registers default shortcuts for actions.
244
+ * If an override already exists for a given action, the override takes precedence.
245
+ */
246
+ registerDefaults(defaults) {
247
+ for (const [action, shortcut] of Object.entries(defaults)) {
248
+ this.#defaults.set(action, shortcut);
249
+ // If we don't have an override/binding for this action, set it as the active binding
250
+ if (!this.#bindings.has(action)) {
251
+ this.#bindings.set(action, shortcut);
252
+ }
253
+ }
254
+ }
255
+ /**
256
+ * Registers overrides for actions. Overwrites any existing or default bindings.
257
+ */
258
+ registerOverrides(overrides) {
259
+ for (const [action, shortcut] of Object.entries(overrides)) {
260
+ this.#bindings.set(action, shortcut);
261
+ }
262
+ }
263
+ /**
264
+ * Retrieves the active shortcut string for a registered action.
265
+ */
266
+ getShortcut(action) {
267
+ return this.#bindings.get(action);
268
+ }
269
+ /**
270
+ * Retrieves the default shortcut string for a registered action.
271
+ */
272
+ getDefaultShortcut(action) {
273
+ return this.#defaults.get(action);
274
+ }
275
+ /**
276
+ * Gets a formatted, user-friendly shortcut string for display.
277
+ * E.g. 'ctrlOrCmd+Shift+KeyK' -> '⌘⇧K' on macOS, 'Ctrl+Shift+K' on Windows/Linux.
278
+ * Supports comma-separated multiple shortcuts (e.g. 'ArrowRight, d' -> 'ArrowRight, D').
279
+ */
280
+ getDisplayShortcut(action) {
281
+ const shortcut = this.getShortcut(action);
282
+ if (!shortcut)
283
+ return undefined;
284
+ return shortcut
285
+ .split(',')
286
+ .map((part) => formatShortcut(part.trim(), this.isMac))
287
+ .join(', ');
288
+ }
289
+ /**
290
+ * Checks if a KeyboardEvent matches the configured keybinding for a given action.
291
+ * Supports comma-separated multiple shortcuts (e.g. 'ArrowRight, d').
292
+ */
293
+ matches(event, action) {
294
+ const shortcut = this.getShortcut(action);
295
+ if (!shortcut)
296
+ return false;
297
+ return shortcut.split(',').some((part) => {
298
+ const parsed = parseKeybinding(part.trim(), this.isMac);
299
+ return matchKeybinding(event, parsed);
300
+ });
301
+ }
302
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipA11yKeybindingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
303
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipA11yKeybindingsService, providedIn: 'root' }); }
304
+ }
305
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipA11yKeybindingsService, decorators: [{
306
+ type: Injectable,
307
+ args: [{
308
+ providedIn: 'root',
309
+ }]
310
+ }], ctorParameters: () => [] });
311
+
312
+ class ShipA11yKeybindingsDirective {
313
+ #service;
314
+ #elementRef;
315
+ #renderer;
316
+ #platformId;
317
+ constructor() {
318
+ this.#service = inject(ShipA11yKeybindingsService);
319
+ this.#elementRef = inject((ElementRef));
320
+ this.#renderer = inject(Renderer2);
321
+ this.#platformId = inject(PLATFORM_ID);
322
+ /**
323
+ * The registered action name (e.g. 'table.next-page', 'dialog.close').
324
+ */
325
+ this.shA11yKeybinding = input.required(/* @ts-ignore */
326
+ ...(ngDevMode ? [{ debugName: "shA11yKeybinding" }] : /* istanbul ignore next */ []));
327
+ /**
328
+ * Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown).
329
+ * Default is 'local'.
330
+ */
331
+ this.mode = input('local', /* @ts-ignore */
332
+ ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
333
+ /**
334
+ * Whether to prevent the default action when the keybinding matches.
335
+ */
336
+ this.preventDefault = input(true, /* @ts-ignore */
337
+ ...(ngDevMode ? [{ debugName: "preventDefault" }] : /* istanbul ignore next */ []));
338
+ /**
339
+ * Whether to stop event propagation when the keybinding matches.
340
+ */
341
+ this.stopPropagation = input(true, /* @ts-ignore */
342
+ ...(ngDevMode ? [{ debugName: "stopPropagation" }] : /* istanbul ignore next */ []));
343
+ /**
344
+ * Event emitted when the keybinding is triggered.
345
+ */
346
+ this.triggered = output();
347
+ // Dynamic effect to update aria-keyshortcuts on the host element when binding or service config changes
348
+ effect(() => {
349
+ const action = this.shA11yKeybinding();
350
+ const shortcut = this.#service.getShortcut(action);
351
+ if (shortcut) {
352
+ // Set the standardized shortcut string for screen reader accessibility
353
+ const ariaValue = this.#service.getDisplayShortcut(action) || shortcut;
354
+ this.#renderer.setAttribute(this.#elementRef.nativeElement, 'aria-keyshortcuts', ariaValue);
355
+ }
356
+ else {
357
+ this.#renderer.removeAttribute(this.#elementRef.nativeElement, 'aria-keyshortcuts');
358
+ }
359
+ });
360
+ // Dynamic effect for global keydown event subscription when mode is 'global'
361
+ effect((onCleanup) => {
362
+ if (this.mode() === 'global' && isPlatformBrowser(this.#platformId)) {
363
+ const listener = (event) => {
364
+ // Avoid firing global hotkeys when typing in editable elements unless modifiers (Ctrl/Cmd/Alt) are pressed
365
+ if (this.#isFocusInInput() && !event.ctrlKey && !event.metaKey && !event.altKey) {
366
+ return;
367
+ }
368
+ this.#checkAndTrigger(event);
369
+ };
370
+ window.addEventListener('keydown', listener);
371
+ onCleanup(() => {
372
+ window.removeEventListener('keydown', listener);
373
+ });
374
+ }
375
+ });
376
+ }
377
+ /**
378
+ * Local keydown listener when mode is 'local'
379
+ */
380
+ onKeyDown(event) {
381
+ if (this.mode() === 'local') {
382
+ this.#checkAndTrigger(event);
383
+ }
384
+ }
385
+ #checkAndTrigger(event) {
386
+ const action = this.shA11yKeybinding();
387
+ if (this.#service.matches(event, action)) {
388
+ if (this.preventDefault()) {
389
+ event.preventDefault();
390
+ }
391
+ if (this.stopPropagation()) {
392
+ event.stopPropagation();
393
+ }
394
+ this.triggered.emit(event);
395
+ // Declaratively invoke click() on host elements that support it (buttons, anchors, inputs, etc.)
396
+ const hostEl = this.#elementRef.nativeElement;
397
+ if (typeof hostEl.click === 'function') {
398
+ hostEl.click();
399
+ }
400
+ }
401
+ }
402
+ /**
403
+ * Returns true if focus is in a text input or editable element.
404
+ */
405
+ #isFocusInInput() {
406
+ const activeEl = document.activeElement;
407
+ if (!activeEl)
408
+ return false;
409
+ const tagName = activeEl.tagName.toLowerCase();
410
+ const isInput = tagName === 'input' || tagName === 'textarea' || tagName === 'select';
411
+ const isEditable = activeEl.hasAttribute('contenteditable') && activeEl.getAttribute('contenteditable') !== 'false';
412
+ return isInput || isEditable;
413
+ }
414
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipA11yKeybindingsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
415
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: ShipA11yKeybindingsDirective, isStandalone: true, selector: "[shA11yKeybinding]", inputs: { shA11yKeybinding: { classPropertyName: "shA11yKeybinding", publicName: "shA11yKeybinding", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, preventDefault: { classPropertyName: "preventDefault", publicName: "preventDefault", isSignal: true, isRequired: false, transformFunction: null }, stopPropagation: { classPropertyName: "stopPropagation", publicName: "stopPropagation", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { triggered: "triggered" }, host: { listeners: { "keydown": "onKeyDown($event)" } }, ngImport: i0 }); }
416
+ }
417
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipA11yKeybindingsDirective, decorators: [{
418
+ type: Directive,
419
+ args: [{
420
+ selector: '[shA11yKeybinding]',
421
+ standalone: true,
422
+ }]
423
+ }], ctorParameters: () => [], propDecorators: { shA11yKeybinding: [{ type: i0.Input, args: [{ isSignal: true, alias: "shA11yKeybinding", required: true }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], preventDefault: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventDefault", required: false }] }], stopPropagation: [{ type: i0.Input, args: [{ isSignal: true, alias: "stopPropagation", required: false }] }], triggered: [{ type: i0.Output, args: ["triggered"] }], onKeyDown: [{
424
+ type: HostListener,
425
+ args: ['keydown', ['$event']]
426
+ }] } });
427
+
428
+ /**
429
+ * Generated bundle index. Do not edit.
430
+ */
431
+
432
+ export { DEFAULT_KEYBINDINGS, SHIP_A11Y_KEYBINDINGS_OVERRIDE, ShipA11yKeybindingsDirective, ShipA11yKeybindingsService, formatShortcut, matchKeybinding, parseKeybinding };
433
+ //# sourceMappingURL=ship-ui-core-ship-a11y-keybindings.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ship-ui-core-ship-a11y-keybindings.mjs","sources":["../../../projects/ship-ui/ship-a11y-keybindings/ship-a11y-keybindings-override.token.ts","../../../projects/ship-ui/ship-a11y-keybindings/keybinding-utils.ts","../../../projects/ship-ui/ship-a11y-keybindings/ship-a11y-keybindings.service.ts","../../../projects/ship-ui/ship-a11y-keybindings/ship-a11y-keybindings.directive.ts","../../../projects/ship-ui/ship-a11y-keybindings/ship-ui-core-ship-a11y-keybindings.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport const SHIP_A11Y_KEYBINDINGS_OVERRIDE = new InjectionToken<Record<string, string>>('SHIP_A11Y_KEYBINDINGS_OVERRIDE');\n","export interface ParsedKeybinding {\n ctrlKey: boolean;\n metaKey: boolean;\n altKey: boolean;\n shiftKey: boolean;\n key?: string;\n code?: string;\n}\n\n/**\n * Parses a keybinding shortcut string shorthand into a structured ParsedKeybinding object.\n * Supporting modifiers: ctrlOrCmd (OS adaptive), ctrl, cmd/meta, alt/option, shift.\n */\nexport function parseKeybinding(shorthand: string, isMacPlatform: boolean): ParsedKeybinding {\n const parts = shorthand.split('+').map(p => p.trim().toLowerCase());\n \n let ctrlKey = false;\n let metaKey = false;\n let altKey = false;\n let shiftKey = false;\n \n // The last part is the key/code\n const keyPart = parts[parts.length - 1] || '';\n \n // Parse any preceding parts as modifier keys\n for (let i = 0; i < parts.length - 1; i++) {\n const modifier = parts[i];\n if (modifier === 'ctrlorcmd') {\n if (isMacPlatform) {\n metaKey = true;\n } else {\n ctrlKey = true;\n }\n } else if (modifier === 'ctrl') {\n ctrlKey = true;\n } else if (modifier === 'meta' || modifier === 'cmd') {\n metaKey = true;\n } else if (modifier === 'alt' || modifier === 'option') {\n altKey = true;\n } else if (modifier === 'shift') {\n shiftKey = true;\n }\n }\n \n let key: string | undefined;\n let code: string | undefined;\n \n if (keyPart.startsWith('key') || keyPart.startsWith('digit')) {\n code = keyPart; // Matches event.code (case-insensitive)\n } else {\n // Map standard key names to event.key representation\n if (keyPart === 'enter') {\n key = 'enter';\n } else if (keyPart === 'esc' || keyPart === 'escape') {\n key = 'escape';\n } else if (keyPart === 'space') {\n key = ' ';\n } else if (keyPart === 'tab') {\n key = 'tab';\n } else if (keyPart === 'backspace') {\n key = 'backspace';\n } else if (keyPart === 'delete') {\n key = 'delete';\n } else if (keyPart === 'up' || keyPart === 'arrowup') {\n key = 'arrowup';\n } else if (keyPart === 'down' || keyPart === 'arrowdown') {\n key = 'arrowdown';\n } else if (keyPart === 'left' || keyPart === 'arrowleft') {\n key = 'arrowleft';\n } else if (keyPart === 'right' || keyPart === 'arrowright') {\n key = 'arrowright';\n } else if (keyPart === 'home') {\n key = 'home';\n } else if (keyPart === 'end') {\n key = 'end';\n } else if (keyPart === 'pageup') {\n key = 'pageup';\n } else if (keyPart === 'pagedown') {\n key = 'pagedown';\n } else {\n key = keyPart; // Raw key name\n }\n }\n \n return { ctrlKey, metaKey, altKey, shiftKey, key, code };\n}\n\n/**\n * Checks if a KeyboardEvent matches a ParsedKeybinding structure.\n */\nexport function matchKeybinding(event: KeyboardEvent, parsed: ParsedKeybinding): boolean {\n if (event.ctrlKey !== parsed.ctrlKey) return false;\n if (event.metaKey !== parsed.metaKey) return false;\n if (event.altKey !== parsed.altKey) return false;\n if (event.shiftKey !== parsed.shiftKey) return false;\n \n if (parsed.code) {\n return event.code.toLowerCase() === parsed.code;\n }\n if (parsed.key) {\n if (parsed.key === ' ') {\n return event.key === ' ' || event.key === 'Spacebar';\n }\n return event.key.toLowerCase() === parsed.key;\n }\n \n return false;\n}\n\n/**\n * Formats a shorthand shortcut string into an OS-specific user-friendly string for display.\n * E.g. 'ctrlOrCmd+Shift+KeyK' -> '⌘⇧K' on macOS, and 'Ctrl+Shift+K' on Windows/Linux.\n */\nexport function formatShortcut(shorthand: string, isMacPlatform: boolean): string {\n const parts = shorthand.split('+').map(p => p.trim());\n const formattedParts: string[] = [];\n \n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const lowerPart = part.toLowerCase();\n \n if (i < parts.length - 1) {\n if (lowerPart === 'ctrlorcmd') {\n formattedParts.push(isMacPlatform ? '⌘' : 'Ctrl');\n } else if (lowerPart === 'ctrl') {\n formattedParts.push(isMacPlatform ? '⌃' : 'Ctrl');\n } else if (lowerPart === 'meta' || lowerPart === 'cmd') {\n formattedParts.push(isMacPlatform ? '⌘' : 'Win');\n } else if (lowerPart === 'alt' || lowerPart === 'option') {\n formattedParts.push(isMacPlatform ? '⌥' : 'Alt');\n } else if (lowerPart === 'shift') {\n formattedParts.push(isMacPlatform ? '⇧' : 'Shift');\n }\n } else {\n // Key/code part\n if (lowerPart.startsWith('key')) {\n formattedParts.push(part.substring(3).toUpperCase()); // E.g. KeyA -> A\n } else if (lowerPart.startsWith('digit')) {\n formattedParts.push(part.substring(5)); // E.g. Digit1 -> 1\n } else {\n // Capitalize first letter of standard keys\n const capitalized = part.charAt(0).toUpperCase() + part.slice(1);\n formattedParts.push(capitalized);\n }\n }\n }\n \n if (isMacPlatform) {\n return formattedParts.join('');\n } else {\n return formattedParts.join('+');\n }\n}\n","import { inject, Injectable, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { SHIP_A11Y_KEYBINDINGS_OVERRIDE } from './ship-a11y-keybindings-override.token';\nimport { parseKeybinding, matchKeybinding, formatShortcut } from './keybinding-utils';\n\nexport const DEFAULT_KEYBINDINGS: Record<string, string> = {\n // Datepicker\n 'datepicker.prev-month': 'PageUp',\n 'datepicker.next-month': 'PageDown',\n 'datepicker.prev-year': 'Shift+PageUp',\n 'datepicker.next-year': 'Shift+PageDown',\n 'datepicker.month-start': 'Home',\n 'datepicker.month-end': 'End',\n 'datepicker.day-next': 'ArrowRight, d',\n 'datepicker.day-prev': 'ArrowLeft, a',\n 'datepicker.week-next': 'ArrowDown, s',\n 'datepicker.week-prev': 'ArrowUp, w',\n\n // Selection Group (Tabs, Steppers, Button Groups)\n 'selection-group.next': 'ArrowRight, ArrowDown, d, s',\n 'selection-group.prev': 'ArrowLeft, ArrowUp, a, w',\n 'selection-group.select': 'Enter, space',\n\n // Select\n 'select.next': 'ArrowDown, s',\n 'select.prev': 'ArrowUp, w',\n 'select.select': 'Enter, space',\n 'select.close': 'Escape',\n\n // Menu\n 'menu.next': 'ArrowDown, s',\n 'menu.prev': 'ArrowUp, w',\n 'menu.open-submenu': 'ArrowRight, d',\n 'menu.close-submenu': 'ArrowLeft, a',\n 'menu.select': 'Enter, space',\n\n // Spotlight\n 'spotlight.next': 'ArrowDown, s',\n 'spotlight.prev': 'ArrowUp, w',\n 'spotlight.open': 'ctrlOrCmd+k',\n\n // Dialog & Popover\n 'dialog.close': 'Escape',\n 'popover.close': 'Escape',\n\n // Toggle Card\n 'toggle-card.toggle': 'Enter, space',\n\n // Form Controls\n 'checkbox.toggle': 'Enter, space',\n 'toggle.toggle': 'Enter, space',\n 'radio.select': 'Enter, space',\n\n // Table\n 'table.sort': 'Enter, space',\n\n // Blueprint\n 'blueprint.cancel': 'Escape',\n\n // Editor Toolbar\n 'editor-toolbar.next': 'ArrowRight, ArrowDown',\n 'editor-toolbar.prev': 'ArrowLeft, ArrowUp',\n 'editor-toolbar.home': 'Home',\n 'editor-toolbar.end': 'End',\n};\n\n@Injectable({\n providedIn: 'root',\n})\nexport class ShipA11yKeybindingsService {\n readonly #platformId = inject(PLATFORM_ID);\n readonly #overrides = inject(SHIP_A11Y_KEYBINDINGS_OVERRIDE, { optional: true });\n \n // Store of action names to active shortcut strings\n readonly #bindings = new Map<string, string>();\n \n // Store of action names to default shortcut strings\n readonly #defaults = new Map<string, string>();\n\n /**\n * Returns true if the platform is running on macOS.\n */\n get isMac(): boolean {\n if (!isPlatformBrowser(this.#platformId)) return false;\n return navigator.userAgent.toLowerCase().includes('mac');\n }\n\n constructor() {\n this.registerDefaults(DEFAULT_KEYBINDINGS);\n if (this.#overrides) {\n this.registerOverrides(this.#overrides);\n }\n }\n\n /**\n * Registers default shortcuts for actions.\n * If an override already exists for a given action, the override takes precedence.\n */\n registerDefaults(defaults: Record<string, string>): void {\n for (const [action, shortcut] of Object.entries(defaults)) {\n this.#defaults.set(action, shortcut);\n \n // If we don't have an override/binding for this action, set it as the active binding\n if (!this.#bindings.has(action)) {\n this.#bindings.set(action, shortcut);\n }\n }\n }\n\n /**\n * Registers overrides for actions. Overwrites any existing or default bindings.\n */\n registerOverrides(overrides: Record<string, string>): void {\n for (const [action, shortcut] of Object.entries(overrides)) {\n this.#bindings.set(action, shortcut);\n }\n }\n\n /**\n * Retrieves the active shortcut string for a registered action.\n */\n getShortcut(action: string): string | undefined {\n return this.#bindings.get(action);\n }\n\n /**\n * Retrieves the default shortcut string for a registered action.\n */\n getDefaultShortcut(action: string): string | undefined {\n return this.#defaults.get(action);\n }\n\n /**\n * Gets a formatted, user-friendly shortcut string for display.\n * E.g. 'ctrlOrCmd+Shift+KeyK' -> '⌘⇧K' on macOS, 'Ctrl+Shift+K' on Windows/Linux.\n * Supports comma-separated multiple shortcuts (e.g. 'ArrowRight, d' -> 'ArrowRight, D').\n */\n getDisplayShortcut(action: string): string | undefined {\n const shortcut = this.getShortcut(action);\n if (!shortcut) return undefined;\n return shortcut\n .split(',')\n .map((part) => formatShortcut(part.trim(), this.isMac))\n .join(', ');\n }\n\n /**\n * Checks if a KeyboardEvent matches the configured keybinding for a given action.\n * Supports comma-separated multiple shortcuts (e.g. 'ArrowRight, d').\n */\n matches(event: KeyboardEvent, action: string): boolean {\n const shortcut = this.getShortcut(action);\n if (!shortcut) return false;\n \n return shortcut.split(',').some((part) => {\n const parsed = parseKeybinding(part.trim(), this.isMac);\n return matchKeybinding(event, parsed);\n });\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n Directive,\n effect,\n ElementRef,\n HostListener,\n inject,\n input,\n output,\n PLATFORM_ID,\n Renderer2,\n} from '@angular/core';\nimport { ShipA11yKeybindingsService } from './ship-a11y-keybindings.service';\n\n@Directive({\n selector: '[shA11yKeybinding]',\n standalone: true,\n})\nexport class ShipA11yKeybindingsDirective {\n readonly #service = inject(ShipA11yKeybindingsService);\n readonly #elementRef = inject(ElementRef<HTMLElement>);\n readonly #renderer = inject(Renderer2);\n readonly #platformId = inject(PLATFORM_ID);\n\n /**\n * The registered action name (e.g. 'table.next-page', 'dialog.close').\n */\n shA11yKeybinding = input.required<string>();\n\n /**\n * Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown).\n * Default is 'local'.\n */\n mode = input<'global' | 'local'>('local');\n\n /**\n * Whether to prevent the default action when the keybinding matches.\n */\n preventDefault = input<boolean>(true);\n\n /**\n * Whether to stop event propagation when the keybinding matches.\n */\n stopPropagation = input<boolean>(true);\n\n /**\n * Event emitted when the keybinding is triggered.\n */\n triggered = output<KeyboardEvent>();\n\n constructor() {\n // Dynamic effect to update aria-keyshortcuts on the host element when binding or service config changes\n effect(() => {\n const action = this.shA11yKeybinding();\n const shortcut = this.#service.getShortcut(action);\n if (shortcut) {\n // Set the standardized shortcut string for screen reader accessibility\n const ariaValue = this.#service.getDisplayShortcut(action) || shortcut;\n this.#renderer.setAttribute(this.#elementRef.nativeElement, 'aria-keyshortcuts', ariaValue);\n } else {\n this.#renderer.removeAttribute(this.#elementRef.nativeElement, 'aria-keyshortcuts');\n }\n });\n\n // Dynamic effect for global keydown event subscription when mode is 'global'\n effect((onCleanup) => {\n if (this.mode() === 'global' && isPlatformBrowser(this.#platformId)) {\n const listener = (event: KeyboardEvent) => {\n // Avoid firing global hotkeys when typing in editable elements unless modifiers (Ctrl/Cmd/Alt) are pressed\n if (this.#isFocusInInput() && !event.ctrlKey && !event.metaKey && !event.altKey) {\n return;\n }\n this.#checkAndTrigger(event);\n };\n\n window.addEventListener('keydown', listener);\n onCleanup(() => {\n window.removeEventListener('keydown', listener);\n });\n }\n });\n }\n\n /**\n * Local keydown listener when mode is 'local'\n */\n @HostListener('keydown', ['$event'])\n onKeyDown(event: KeyboardEvent): void {\n if (this.mode() === 'local') {\n this.#checkAndTrigger(event);\n }\n }\n\n #checkAndTrigger(event: KeyboardEvent): void {\n const action = this.shA11yKeybinding();\n if (this.#service.matches(event, action)) {\n if (this.preventDefault()) {\n event.preventDefault();\n }\n if (this.stopPropagation()) {\n event.stopPropagation();\n }\n\n this.triggered.emit(event);\n\n // Declaratively invoke click() on host elements that support it (buttons, anchors, inputs, etc.)\n const hostEl = this.#elementRef.nativeElement;\n if (typeof hostEl.click === 'function') {\n hostEl.click();\n }\n }\n }\n\n /**\n * Returns true if focus is in a text input or editable element.\n */\n #isFocusInInput(): boolean {\n const activeEl = document.activeElement;\n if (!activeEl) return false;\n\n const tagName = activeEl.tagName.toLowerCase();\n const isInput = tagName === 'input' || tagName === 'textarea' || tagName === 'select';\n const isEditable = activeEl.hasAttribute('contenteditable') && activeEl.getAttribute('contenteditable') !== 'false';\n\n return isInput || isEditable;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -42,6 +42,7 @@ class ShipAccordion {
42
42
  if (summary && !summary.querySelector('sh-icon')) {
43
43
  const icon = document.createElement('sh-icon');
44
44
  icon.textContent = 'caret-down';
45
+ icon.setAttribute('aria-hidden', 'true');
45
46
  summary.appendChild(icon);
46
47
  }
47
48
  let contentWrapper = details.querySelector(':scope > .content');
@@ -1 +1 @@
1
- {"version":3,"file":"ship-ui-core-ship-accordion.mjs","sources":["../../../projects/ship-ui/ship-accordion/ship-accordion.ts","../../../projects/ship-ui/ship-accordion/ship-ui-core-ship-accordion.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n effect,\n ElementRef,\n inject,\n input,\n model,\n ViewEncapsulation,\n} from '@angular/core';\nimport { contentProjectionSignal, shipComponentClasses, ShipVariant } from '@ship-ui/core';\n\n@Component({\n selector: 'sh-accordion',\n styleUrl: './ship-accordion.scss',\n encapsulation: ViewEncapsulation.None,\n template: `\n <ng-content></ng-content>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.sh-accordion]': 'true',\n '[class]': 'hostClasses()',\n },\n})\nexport class ShipAccordion {\n private selfElement = inject(ElementRef<HTMLElement>).nativeElement;\n\n readonly name = input<string>(`sh-accordion-${Math.random().toString(36).substring(2, 9)}`);\n readonly value = model<string | null>(null);\n readonly allowMultiple = input<boolean>(false);\n readonly variant = input<ShipVariant | null>(null);\n readonly size = input<string | null>(null);\n\n hostClasses = shipComponentClasses('accordion', {\n variant: this.variant,\n size: this.size,\n });\n\n protected items = contentProjectionSignal<HTMLDetailsElement>('details', {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['open', 'value'],\n });\n\n constructor() {\n this.selfElement.addEventListener('toggle', this.onToggle.bind(this), true);\n\n effect(() => {\n const isMultiple = this.allowMultiple();\n const groupName = this.name();\n const valStr = this.value();\n const vals = valStr ? valStr.split(',').filter((v) => v !== '') : [];\n\n this.items().forEach((details) => {\n if (!isMultiple) {\n details.setAttribute('name', groupName);\n } else {\n details.removeAttribute('name');\n }\n\n const summary = details.querySelector('summary');\n if (summary && !summary.querySelector('sh-icon')) {\n const icon = document.createElement('sh-icon');\n icon.textContent = 'caret-down';\n summary.appendChild(icon);\n }\n\n let contentWrapper = details.querySelector(':scope > .content');\n\n if (!contentWrapper) {\n const newWrapper = document.createElement('div');\n newWrapper.className = 'content';\n const childrenToMove = Array.from(details.childNodes).filter((node) => {\n if (node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName.toLowerCase() === 'summary') {\n return false;\n }\n return true;\n });\n childrenToMove.forEach((child) => newWrapper.appendChild(child));\n details.appendChild(newWrapper);\n contentWrapper = newWrapper;\n }\n\n const itemVal = details.getAttribute('value');\n if (itemVal && vals.includes(itemVal)) {\n // Avoid triggering endless loops by only setting open if it actually changed\n if (!details.open) details.open = true;\n } else if (itemVal) {\n if (details.open) details.open = false;\n }\n });\n });\n }\n\n onToggle(event: Event) {\n const target = event.target as HTMLDetailsElement;\n if (target.tagName !== 'DETAILS') return;\n if (!this.selfElement.contains(target)) return;\n\n const itemVal = target.getAttribute('value');\n if (!itemVal) return; // Uncontrolled details element\n\n const isOpen = target.open;\n\n if (this.allowMultiple()) {\n let vals = this.value()\n ? this.value()!\n .split(',')\n .filter((v) => v !== '')\n : [];\n if (isOpen && !vals.includes(itemVal)) vals.push(itemVal);\n if (!isOpen) vals = vals.filter((v) => v !== itemVal);\n this.value.set(vals.join(','));\n } else {\n if (isOpen) {\n this.value.set(itemVal);\n } else {\n if (this.value() === itemVal) {\n this.value.set(null);\n }\n }\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ship-ui-core-ship-accordion.mjs","sources":["../../../projects/ship-ui/ship-accordion/ship-accordion.ts","../../../projects/ship-ui/ship-accordion/ship-ui-core-ship-accordion.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n effect,\n ElementRef,\n inject,\n input,\n model,\n ViewEncapsulation,\n} from '@angular/core';\nimport { contentProjectionSignal, shipComponentClasses, ShipVariant } from '@ship-ui/core';\n\n@Component({\n selector: 'sh-accordion',\n styleUrl: './ship-accordion.scss',\n encapsulation: ViewEncapsulation.None,\n template: `\n <ng-content></ng-content>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.sh-accordion]': 'true',\n '[class]': 'hostClasses()',\n },\n})\nexport class ShipAccordion {\n private selfElement = inject(ElementRef<HTMLElement>).nativeElement;\n\n readonly name = input<string>(`sh-accordion-${Math.random().toString(36).substring(2, 9)}`);\n readonly value = model<string | null>(null);\n readonly allowMultiple = input<boolean>(false);\n readonly variant = input<ShipVariant | null>(null);\n readonly size = input<string | null>(null);\n\n hostClasses = shipComponentClasses('accordion', {\n variant: this.variant,\n size: this.size,\n });\n\n protected items = contentProjectionSignal<HTMLDetailsElement>('details', {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['open', 'value'],\n });\n\n constructor() {\n this.selfElement.addEventListener('toggle', this.onToggle.bind(this), true);\n\n effect(() => {\n const isMultiple = this.allowMultiple();\n const groupName = this.name();\n const valStr = this.value();\n const vals = valStr ? valStr.split(',').filter((v) => v !== '') : [];\n\n this.items().forEach((details) => {\n if (!isMultiple) {\n details.setAttribute('name', groupName);\n } else {\n details.removeAttribute('name');\n }\n\n const summary = details.querySelector('summary');\n if (summary && !summary.querySelector('sh-icon')) {\n const icon = document.createElement('sh-icon');\n icon.textContent = 'caret-down';\n icon.setAttribute('aria-hidden', 'true');\n summary.appendChild(icon);\n }\n\n let contentWrapper = details.querySelector(':scope > .content');\n\n if (!contentWrapper) {\n const newWrapper = document.createElement('div');\n newWrapper.className = 'content';\n const childrenToMove = Array.from(details.childNodes).filter((node) => {\n if (node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName.toLowerCase() === 'summary') {\n return false;\n }\n return true;\n });\n childrenToMove.forEach((child) => newWrapper.appendChild(child));\n details.appendChild(newWrapper);\n contentWrapper = newWrapper;\n }\n\n const itemVal = details.getAttribute('value');\n if (itemVal && vals.includes(itemVal)) {\n // Avoid triggering endless loops by only setting open if it actually changed\n if (!details.open) details.open = true;\n } else if (itemVal) {\n if (details.open) details.open = false;\n }\n });\n });\n }\n\n onToggle(event: Event) {\n const target = event.target as HTMLDetailsElement;\n if (target.tagName !== 'DETAILS') return;\n if (!this.selfElement.contains(target)) return;\n\n const itemVal = target.getAttribute('value');\n if (!itemVal) return; // Uncontrolled details element\n\n const isOpen = target.open;\n\n if (this.allowMultiple()) {\n let vals = this.value()\n ? this.value()!\n .split(',')\n .filter((v) => v !== '')\n : [];\n if (isOpen && !vals.includes(itemVal)) vals.push(itemVal);\n if (!isOpen) vals = vals.filter((v) => v !== itemVal);\n this.value.set(vals.join(','));\n } else {\n if (isOpen) {\n this.value.set(itemVal);\n } else {\n if (this.value() === itemVal) {\n this.value.set(null);\n }\n }\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}