svelte-incant 0.6.1 → 0.7.1

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.
@@ -1,15 +1,19 @@
1
1
  <script lang="ts">
2
2
  import * as Kbd from './ui/kbd/index.js';
3
- import { keyToSymbol } from '../utils.js';
3
+ import { getKeyLabel, getIsMac } from '../utils.js';
4
4
 
5
5
  let {
6
6
  keys,
7
- isChord = false
7
+ isChord = false,
8
+ formatShortcut
8
9
  }: {
9
10
  keys: string | string[] | string[][];
10
11
  isChord?: boolean;
12
+ formatShortcut?: (keys: string[][], isChord: boolean, isMac: boolean) => string;
11
13
  } = $props();
12
14
 
15
+ const isMac = getIsMac();
16
+
13
17
  type KeyCombination = string[];
14
18
 
15
19
  const MODIFIER_KEYS = new Set([
@@ -63,22 +67,32 @@
63
67
  );
64
68
 
65
69
  const formattedParts = $derived.by(() => {
66
- const combos = keyGroups.map((group) => group.map(keyToSymbol).join(' '));
70
+ const combos = keyGroups.map((group) => group.map((key) => getKeyLabel(key, isMac)).join(' '));
67
71
  return formatter.formatToParts(combos);
68
72
  });
73
+
74
+ const customFormatted = $derived(
75
+ formatShortcut ? formatShortcut(keyGroups, isChordMode, isMac) : null
76
+ );
69
77
  </script>
70
78
 
71
- <Kbd.Group class="incant-kbds-container">
72
- {#each formattedParts as part (part)}
73
- {#if part.type === 'element'}
74
- <Kbd.Root>{part.value}</Kbd.Root>
75
- {:else if isChordMode}
76
- <span class="incant-kbds-chord-separator">→</span>
77
- {:else}
78
- <span class="incant-kbds-separator">{part.value}</span>
79
- {/if}
80
- {/each}
81
- </Kbd.Group>
79
+ {#if customFormatted !== null}
80
+ <Kbd.Group class="incant-kbds-container">
81
+ <Kbd.Root>{customFormatted}</Kbd.Root>
82
+ </Kbd.Group>
83
+ {:else}
84
+ <Kbd.Group class="incant-kbds-container">
85
+ {#each formattedParts as part (part)}
86
+ {#if part.type === 'element'}
87
+ <Kbd.Root>{part.value}</Kbd.Root>
88
+ {:else if isChordMode}
89
+ <span class="incant-kbds-chord-separator">→</span>
90
+ {:else}
91
+ <span class="incant-kbds-separator">{part.value}</span>
92
+ {/if}
93
+ {/each}
94
+ </Kbd.Group>
95
+ {/if}
82
96
 
83
97
  <style>
84
98
  .incant-kbds-separator {
@@ -1,6 +1,7 @@
1
1
  type $$ComponentProps = {
2
2
  keys: string | string[] | string[][];
3
3
  isChord?: boolean;
4
+ formatShortcut?: (keys: string[][], isChord: boolean, isMac: boolean) => string;
4
5
  };
5
6
  declare const Kbds: import("svelte").Component<$$ComponentProps, {}, "">;
6
7
  type Kbds = ReturnType<typeof Kbds>;
@@ -6,4 +6,4 @@ export { default as Shortcut } from './shortcut.svelte';
6
6
  export { shortcut } from './attachment.svelte.js';
7
7
  export { chordRegistry, chords, get_current_progress } from './chord.svelte.js';
8
8
  export { closePalette, openPalette, paletteState, shortcuts, togglePalette } from './palette.svelte.js';
9
- export { getIsMac, keyToSymbol } from './utils';
9
+ export { getIsMac, getKeyLabel, keyToSymbol } from './utils';
@@ -5,4 +5,4 @@ export { default as Shortcut } from './shortcut.svelte';
5
5
  export { shortcut } from './attachment.svelte.js';
6
6
  export { chordRegistry, chords, get_current_progress } from './chord.svelte.js';
7
7
  export { closePalette, openPalette, paletteState, shortcuts, togglePalette } from './palette.svelte.js';
8
- export { getIsMac, keyToSymbol } from './utils';
8
+ export { getIsMac, getKeyLabel, keyToSymbol } from './utils';
@@ -54,6 +54,7 @@
54
54
  let {
55
55
  position = 'bottom-right',
56
56
  showToggles = false,
57
+ formatShortcut,
57
58
  texts = {
58
59
  shortcutDescription: 'Open shortcut palette',
59
60
  tooltipContent: 'Press ?',
@@ -74,6 +75,7 @@
74
75
  }: {
75
76
  position?: PalettePosition;
76
77
  showToggles?: boolean;
78
+ formatShortcut?: (keys: string[][], isChord: boolean, isMac: boolean) => string;
77
79
  texts?: {
78
80
  shortcutDescription?: string;
79
81
  tooltipContent?: string;
@@ -92,7 +94,6 @@
92
94
  };
93
95
  } = $props();
94
96
 
95
- let open = $derived(paletteState.open);
96
97
  let tooltip_open = $state(false);
97
98
 
98
99
  const pressed_keys = new PressedKeys();
@@ -142,7 +143,7 @@
142
143
  });
143
144
  </script>
144
145
 
145
- <Shortcut keys={[['?'], ['/']]} description={texts.shortcutDescription} action={togglePalette} />
146
+ <Shortcut keys={[['?']]} description={texts.shortcutDescription} action={togglePalette} />
146
147
 
147
148
  <Tooltip.Provider delayDuration={0}>
148
149
  <!-- <Tooltip.Root bind:open={tooltip_open}> -->
@@ -161,7 +162,7 @@
161
162
  </Tooltip.Root>
162
163
  </Tooltip.Provider>
163
164
 
164
- <Dialog.Root bind:open>
165
+ <Dialog.Root bind:open={paletteState.open}>
165
166
  <Dialog.Content portalProps={{ disabled: true }}>
166
167
  <Dialog.Header>
167
168
  <Dialog.Title>{texts.dialogTitle}</Dialog.Title>
@@ -185,7 +186,7 @@
185
186
  {#each filtered_shortcuts as item (item.type === 'shortcut' ? slugify(item.keys) : slugifyChord(item.keys))}
186
187
  <Table.Row>
187
188
  <Table.Cell class="incant-palette-cell-keys">
188
- <Kbds keys={item.keys} isChord={item.type === 'chord'} />
189
+ <Kbds keys={item.keys} isChord={item.type === 'chord'} {formatShortcut} />
189
190
  </Table.Cell>
190
191
  <Table.Cell>{item.description}</Table.Cell>
191
192
  {#if showToggles}
@@ -212,7 +213,7 @@
212
213
  <Table.Row>
213
214
  <Table.Cell colspan={showToggles ? 3 : 2} class="incant-palette-empty-state">
214
215
  {texts.emptyState}
215
- <Kbds keys={all_keys} />
216
+ <Kbds keys={all_keys} {formatShortcut} />
216
217
 
217
218
  .
218
219
  </Table.Cell>
@@ -227,7 +228,7 @@
227
228
 
228
229
  {#if chordDisplay}
229
230
  <div class="incant-chord-display">
230
- <Kbds keys={chordDisplay.completedSteps} />
231
+ <Kbds keys={chordDisplay.completedSteps} isChord={true} {formatShortcut} />
231
232
  {#if chordDisplay.hasMore}
232
233
  <span class="incant-chord-display-arrow">→</span>
233
234
  <div class="incant-chord-display-next">
@@ -2,6 +2,7 @@ export type PalettePosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-
2
2
  type $$ComponentProps = {
3
3
  position?: PalettePosition;
4
4
  showToggles?: boolean;
5
+ formatShortcut?: (keys: string[][], isChord: boolean, isMac: boolean) => string;
5
6
  texts?: {
6
7
  shortcutDescription?: string;
7
8
  tooltipContent?: string;
@@ -49,11 +49,16 @@ class ShortcutRegistry {
49
49
  }
50
50
  }
51
51
  startListening() {
52
- if (this.isListening)
52
+ if (this.isListening) {
53
+ console.warn('Keyboard listeners already started');
53
54
  return;
55
+ }
54
56
  this.isListening = true;
55
57
  $effect(() => {
56
58
  this.syncKeyboardListeners();
59
+ return () => {
60
+ this.isListening = false;
61
+ };
57
62
  });
58
63
  }
59
64
  normalizeKeys(keys) {
@@ -5,18 +5,21 @@
5
5
  let {
6
6
  keys,
7
7
  description,
8
- action
8
+ action,
9
+ preventDefault
9
10
  }: {
10
11
  keys: string | string[] | string[][];
11
12
  description?: string;
12
13
  action: () => void;
14
+ preventDefault?: boolean;
13
15
  } = $props();
14
16
 
15
- watch([() => keys, () => description, () => action], () => {
17
+ watch([() => keys, () => description, () => action, () => preventDefault], () => {
16
18
  add_shortcut({
17
19
  keys,
18
20
  description,
19
- action
21
+ action,
22
+ preventDefault
20
23
  });
21
24
 
22
25
  return () => {
@@ -2,6 +2,7 @@ type $$ComponentProps = {
2
2
  keys: string | string[] | string[][];
3
3
  description?: string;
4
4
  action: () => void;
5
+ preventDefault?: boolean;
5
6
  };
6
7
  declare const Shortcut: import("svelte").Component<$$ComponentProps, {}, "">;
7
8
  type Shortcut = ReturnType<typeof Shortcut>;
@@ -9,4 +9,5 @@ export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & {
9
9
  ref?: U | null;
10
10
  };
11
11
  export declare function keyToSymbol(key: string): string;
12
+ export declare function getKeyLabel(key: string, isMac: boolean): string;
12
13
  export declare function getIsMac(): boolean;
@@ -28,9 +28,49 @@ export function keyToSymbol(key) {
28
28
  const lower = key.toLowerCase();
29
29
  return symbols[lower] ?? key.toUpperCase();
30
30
  }
31
- // export const isMac = $derived(
32
- // typeof navigator !== 'undefined' ? navigator.platform.toUpperCase().includes('MAC') : false
33
- // );
31
+ export function getKeyLabel(key, isMac) {
32
+ const macSymbols = {
33
+ control: '⌃',
34
+ ctrl: '⌃',
35
+ alt: '⌥',
36
+ option: '⌥',
37
+ shift: '⇧',
38
+ meta: '⌘',
39
+ command: '⌘',
40
+ cmd: '⌘'
41
+ };
42
+ const textLabels = {
43
+ control: 'Ctrl',
44
+ ctrl: 'Ctrl',
45
+ alt: 'Alt',
46
+ option: 'Alt',
47
+ shift: 'Shift',
48
+ meta: 'Win',
49
+ command: 'Win',
50
+ cmd: 'Win',
51
+ enter: 'Enter',
52
+ return: 'Enter',
53
+ backspace: 'Backspace',
54
+ delete: 'Delete',
55
+ escape: 'Esc',
56
+ esc: 'Esc',
57
+ tab: 'Tab',
58
+ space: 'Space',
59
+ up: '↑',
60
+ down: '↓',
61
+ left: '←',
62
+ right: '→',
63
+ arrowup: '↑',
64
+ arrowdown: '↓',
65
+ arrowleft: '←',
66
+ arrowright: '→'
67
+ };
68
+ const lower = key.toLowerCase();
69
+ if (isMac && macSymbols[lower]) {
70
+ return macSymbols[lower];
71
+ }
72
+ return textLabels[lower] ?? key.toUpperCase();
73
+ }
34
74
  export function getIsMac() {
35
75
  return typeof navigator !== 'undefined'
36
76
  ? navigator.platform.toUpperCase().includes('MAC')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-incant",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "url": "https://github.com/mastermakrela/svelte-incant"