@rtif-sdk/web 1.0.0 → 1.3.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.
Files changed (70) hide show
  1. package/dist/block-drag-handler.d.ts +5 -0
  2. package/dist/block-drag-handler.d.ts.map +1 -1
  3. package/dist/block-drag-handler.js +32 -3
  4. package/dist/block-drag-handler.js.map +1 -1
  5. package/dist/block-renderer.d.ts +12 -6
  6. package/dist/block-renderer.d.ts.map +1 -1
  7. package/dist/block-renderer.js +98 -9
  8. package/dist/block-renderer.js.map +1 -1
  9. package/dist/block-type-dropdown.d.ts +78 -0
  10. package/dist/block-type-dropdown.d.ts.map +1 -0
  11. package/dist/block-type-dropdown.js +276 -0
  12. package/dist/block-type-dropdown.js.map +1 -0
  13. package/dist/color-picker.d.ts +91 -0
  14. package/dist/color-picker.d.ts.map +1 -0
  15. package/dist/color-picker.js +346 -0
  16. package/dist/color-picker.js.map +1 -0
  17. package/dist/content-handlers.d.ts +7 -8
  18. package/dist/content-handlers.d.ts.map +1 -1
  19. package/dist/content-handlers.js +122 -93
  20. package/dist/content-handlers.js.map +1 -1
  21. package/dist/editor.d.ts.map +1 -1
  22. package/dist/editor.js +224 -15
  23. package/dist/editor.js.map +1 -1
  24. package/dist/embed-utils.d.ts +148 -0
  25. package/dist/embed-utils.d.ts.map +1 -0
  26. package/dist/embed-utils.js +197 -0
  27. package/dist/embed-utils.js.map +1 -0
  28. package/dist/font-family-picker.d.ts +105 -0
  29. package/dist/font-family-picker.d.ts.map +1 -0
  30. package/dist/font-family-picker.js +314 -0
  31. package/dist/font-family-picker.js.map +1 -0
  32. package/dist/font-size-picker.d.ts +82 -0
  33. package/dist/font-size-picker.d.ts.map +1 -0
  34. package/dist/font-size-picker.js +290 -0
  35. package/dist/font-size-picker.js.map +1 -0
  36. package/dist/index.d.ts +18 -4
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +30 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/plugins/index.d.ts +2 -1
  41. package/dist/plugins/index.d.ts.map +1 -1
  42. package/dist/plugins/index.js +1 -1
  43. package/dist/plugins/index.js.map +1 -1
  44. package/dist/plugins/link-plugin.d.ts +4 -0
  45. package/dist/plugins/link-plugin.d.ts.map +1 -1
  46. package/dist/plugins/link-plugin.js +17 -0
  47. package/dist/plugins/link-plugin.js.map +1 -1
  48. package/dist/plugins/mark-utils.d.ts +31 -0
  49. package/dist/plugins/mark-utils.d.ts.map +1 -1
  50. package/dist/plugins/mark-utils.js +46 -0
  51. package/dist/plugins/mark-utils.js.map +1 -1
  52. package/dist/renderer.d.ts +31 -2
  53. package/dist/renderer.d.ts.map +1 -1
  54. package/dist/renderer.js +131 -55
  55. package/dist/renderer.js.map +1 -1
  56. package/dist/selection-sync.d.ts +2 -26
  57. package/dist/selection-sync.d.ts.map +1 -1
  58. package/dist/selection-sync.js +49 -13
  59. package/dist/selection-sync.js.map +1 -1
  60. package/dist/spatial-index.d.ts +203 -0
  61. package/dist/spatial-index.d.ts.map +1 -0
  62. package/dist/spatial-index.js +211 -0
  63. package/dist/spatial-index.js.map +1 -0
  64. package/dist/types.d.ts +93 -0
  65. package/dist/types.d.ts.map +1 -1
  66. package/dist/virtual-viewport.d.ts +241 -0
  67. package/dist/virtual-viewport.d.ts.map +1 -0
  68. package/dist/virtual-viewport.js +584 -0
  69. package/dist/virtual-viewport.js.map +1 -0
  70. package/package.json +17 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed-utils.d.ts","sourceRoot":"","sources":["../src/embed-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAchE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM9D;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOrD;AAMD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAqDvF"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Embed utilities — pure URL transformation functions for provider-specific
3
+ * embed rendering.
4
+ *
5
+ * All functions are pure with zero DOM dependency. They convert a stored
6
+ * `(url, embedType)` pair into rendering configuration at render time,
7
+ * keeping the document model clean.
8
+ *
9
+ * @module
10
+ */
11
+ // ---------------------------------------------------------------------------
12
+ // Video ID extraction
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Extract a YouTube video ID from a URL.
16
+ *
17
+ * Supports `youtube.com/watch?v=ID` and `youtu.be/ID` formats.
18
+ * Returns `null` if the URL is not a recognized YouTube URL.
19
+ *
20
+ * @param url - The URL to extract from
21
+ * @returns The video ID string, or null
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * extractYouTubeVideoId('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); // 'dQw4w9WgXcQ'
26
+ * extractYouTubeVideoId('https://youtu.be/dQw4w9WgXcQ'); // 'dQw4w9WgXcQ'
27
+ * extractYouTubeVideoId('https://example.com'); // null
28
+ * ```
29
+ */
30
+ export function extractYouTubeVideoId(url) {
31
+ // youtube.com/watch?v=ID
32
+ const longMatch = /^https?:\/\/(?:www\.)?youtube\.com\/watch\?.*v=([\w-]+)/.exec(url);
33
+ if (longMatch) {
34
+ return longMatch[1] ?? null;
35
+ }
36
+ // youtu.be/ID
37
+ const shortMatch = /^https?:\/\/youtu\.be\/([\w-]+)/.exec(url);
38
+ if (shortMatch) {
39
+ return shortMatch[1] ?? null;
40
+ }
41
+ return null;
42
+ }
43
+ /**
44
+ * Extract a Vimeo video ID from a URL.
45
+ *
46
+ * Supports `vimeo.com/ID` format where ID is numeric.
47
+ * Returns `null` if the URL is not a recognized Vimeo URL.
48
+ *
49
+ * @param url - The URL to extract from
50
+ * @returns The video ID string, or null
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * extractVimeoVideoId('https://vimeo.com/123456789'); // '123456789'
55
+ * extractVimeoVideoId('https://example.com'); // null
56
+ * ```
57
+ */
58
+ export function extractVimeoVideoId(url) {
59
+ const match = /^https?:\/\/(?:www\.)?vimeo\.com\/(\d+)/.exec(url);
60
+ if (match) {
61
+ return match[1] ?? null;
62
+ }
63
+ return null;
64
+ }
65
+ // ---------------------------------------------------------------------------
66
+ // Embed URL builders
67
+ // ---------------------------------------------------------------------------
68
+ /**
69
+ * Build a YouTube embed iframe URL from a video ID.
70
+ *
71
+ * @param videoId - The YouTube video ID
72
+ * @returns The embeddable YouTube URL
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * youtubeEmbedSrc('dQw4w9WgXcQ'); // 'https://www.youtube.com/embed/dQw4w9WgXcQ'
77
+ * ```
78
+ */
79
+ export function youtubeEmbedSrc(videoId) {
80
+ return `https://www.youtube.com/embed/${videoId}`;
81
+ }
82
+ /**
83
+ * Build a Vimeo embed iframe URL from a video ID.
84
+ *
85
+ * @param videoId - The Vimeo video ID
86
+ * @returns The embeddable Vimeo URL
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * vimeoEmbedSrc('123456789'); // 'https://player.vimeo.com/video/123456789'
91
+ * ```
92
+ */
93
+ export function vimeoEmbedSrc(videoId) {
94
+ return `https://player.vimeo.com/video/${videoId}`;
95
+ }
96
+ // ---------------------------------------------------------------------------
97
+ // Display URL extraction
98
+ // ---------------------------------------------------------------------------
99
+ /**
100
+ * Extract a display-friendly hostname from a URL, stripping the `www.` prefix.
101
+ *
102
+ * Falls back to the raw input string if URL parsing fails.
103
+ *
104
+ * @param url - The URL to extract a hostname from
105
+ * @returns A display-friendly hostname string
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * extractDisplayUrl('https://www.youtube.com/watch?v=abc'); // 'youtube.com'
110
+ * extractDisplayUrl('https://example.com/page'); // 'example.com'
111
+ * extractDisplayUrl('not a url'); // 'not a url'
112
+ * ```
113
+ */
114
+ export function extractDisplayUrl(url) {
115
+ try {
116
+ const parsed = new URL(url);
117
+ return parsed.hostname.replace(/^www\./, '');
118
+ }
119
+ catch {
120
+ return url;
121
+ }
122
+ }
123
+ // ---------------------------------------------------------------------------
124
+ // Main config function
125
+ // ---------------------------------------------------------------------------
126
+ /**
127
+ * Compute the embed render configuration for a given URL and embed type.
128
+ *
129
+ * This is the main function called by the embed renderer at render time.
130
+ * It maps the stored `(url, embedType)` attrs to a provider-specific
131
+ * configuration object that determines what DOM content to create.
132
+ *
133
+ * @param url - The embed URL from `block.attrs.url`
134
+ * @param embedType - The embed type from `block.attrs.embedType` (e.g., 'video', 'tweet')
135
+ * @returns The render configuration for the embed
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * const config = getEmbedRenderConfig('https://youtube.com/watch?v=abc', 'video');
140
+ * // config.provider === 'youtube'
141
+ * // config.iframeSrc === 'https://www.youtube.com/embed/abc'
142
+ *
143
+ * const tweet = getEmbedRenderConfig('https://twitter.com/user/status/123', 'tweet');
144
+ * // tweet.provider === 'twitter'
145
+ * // tweet.iframeSrc === undefined
146
+ * ```
147
+ */
148
+ export function getEmbedRenderConfig(url, embedType) {
149
+ const displayUrl = extractDisplayUrl(url);
150
+ if (embedType === 'video') {
151
+ // Try YouTube
152
+ const ytId = extractYouTubeVideoId(url);
153
+ if (ytId) {
154
+ return {
155
+ provider: 'youtube',
156
+ iframeSrc: youtubeEmbedSrc(ytId),
157
+ title: 'YouTube video',
158
+ url,
159
+ displayUrl,
160
+ };
161
+ }
162
+ // Try Vimeo
163
+ const vimeoId = extractVimeoVideoId(url);
164
+ if (vimeoId) {
165
+ return {
166
+ provider: 'vimeo',
167
+ iframeSrc: vimeoEmbedSrc(vimeoId),
168
+ title: 'Vimeo video',
169
+ url,
170
+ displayUrl,
171
+ };
172
+ }
173
+ // Unknown video provider — fall through to generic
174
+ return {
175
+ provider: 'generic',
176
+ title: 'Embedded content',
177
+ url,
178
+ displayUrl,
179
+ };
180
+ }
181
+ if (embedType === 'tweet') {
182
+ return {
183
+ provider: 'twitter',
184
+ title: 'Post on X',
185
+ url,
186
+ displayUrl,
187
+ };
188
+ }
189
+ // Unknown or missing embed type
190
+ return {
191
+ provider: 'generic',
192
+ title: 'Embedded content',
193
+ url,
194
+ displayUrl,
195
+ };
196
+ }
197
+ //# sourceMappingURL=embed-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed-utils.js","sourceRoot":"","sources":["../src/embed-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Font family picker — a dropdown for selecting text font.
3
+ *
4
+ * Each option is rendered in its own font face so the user can preview
5
+ * how the font looks. Shows the current font name in the trigger button.
6
+ * Uses the same `mousedown + preventDefault()` pattern as the toolbar to
7
+ * avoid stealing editor focus.
8
+ *
9
+ * @module
10
+ */
11
+ import type { IEditorEngine } from '@rtif-sdk/engine';
12
+ import type { CommandBus } from './command-bus.js';
13
+ /**
14
+ * A font family option with a display label and CSS font-family stack.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const custom: FontFamilyOption = {
19
+ * value: 'Comic Sans MS',
20
+ * label: 'Comic Sans',
21
+ * stack: '"Comic Sans MS", cursive',
22
+ * };
23
+ * ```
24
+ */
25
+ export interface FontFamilyOption {
26
+ /** The font family value stored in the mark (e.g., "Georgia"). */
27
+ readonly value: string;
28
+ /** Display label shown in the dropdown (e.g., "Georgia"). */
29
+ readonly label: string;
30
+ /** Full CSS font-family stack for rendering the option (e.g., "Georgia, serif"). */
31
+ readonly stack: string;
32
+ }
33
+ /**
34
+ * Default font families displayed in the picker dropdown.
35
+ *
36
+ * Organized by category: sans-serif, serif, monospace.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const picker = createFontFamilyPicker({
41
+ * container, bus, engine,
42
+ * families: DEFAULT_FONT_FAMILIES.filter(f => f.stack.includes('sans')),
43
+ * });
44
+ * ```
45
+ */
46
+ export declare const DEFAULT_FONT_FAMILIES: readonly FontFamilyOption[];
47
+ /**
48
+ * Configuration for the font family picker.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const picker = createFontFamilyPicker({
53
+ * container: document.getElementById('toolbar')!,
54
+ * bus: editor.commandBus,
55
+ * engine,
56
+ * });
57
+ * ```
58
+ */
59
+ export interface FontFamilyPickerConfig {
60
+ /** The DOM element to render the picker into. */
61
+ readonly container: HTMLElement;
62
+ /** The command bus for executing font family commands. */
63
+ readonly bus: CommandBus;
64
+ /** The engine for querying the current mark value. */
65
+ readonly engine: IEditorEngine;
66
+ /** Font families to display (default: {@link DEFAULT_FONT_FAMILIES}). */
67
+ readonly families?: readonly FontFamilyOption[];
68
+ }
69
+ /**
70
+ * Handle returned by {@link createFontFamilyPicker} for lifecycle management.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * const picker = createFontFamilyPicker({ container, bus, engine });
75
+ * // ... later:
76
+ * picker.destroy();
77
+ * ```
78
+ */
79
+ export interface FontFamilyPickerHandle {
80
+ /** The picker's root wrapper element. */
81
+ readonly element: HTMLElement;
82
+ /** Remove all event listeners and DOM nodes. Idempotent. */
83
+ destroy(): void;
84
+ }
85
+ /**
86
+ * Create a font family picker dropdown backed by a {@link CommandBus}.
87
+ *
88
+ * The picker reflects the current font family by querying the engine's
89
+ * mark state. It subscribes to the bus for live updates. Each option
90
+ * is rendered in its own font face for preview.
91
+ *
92
+ * @param config - Picker configuration
93
+ * @returns A handle for lifecycle management
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * const picker = createFontFamilyPicker({
98
+ * container: toolbar,
99
+ * bus: editor.commandBus,
100
+ * engine,
101
+ * });
102
+ * ```
103
+ */
104
+ export declare function createFontFamilyPicker(config: FontFamilyPickerConfig): FontFamilyPickerHandle;
105
+ //# sourceMappingURL=font-family-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-family-picker.d.ts","sourceRoot":"","sources":["../src/font-family-picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAanD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,gBAAgB,EAiB5D,CAAC;AAMF;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,sBAAsB;IACrC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC,0DAA0D;IAC1D,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACjD;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAsB;IACrC,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,4DAA4D;IAC5D,OAAO,IAAI,IAAI,CAAC;CACjB;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,sBAAsB,CAwS7F"}
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Font family picker — a dropdown for selecting text font.
3
+ *
4
+ * Each option is rendered in its own font face so the user can preview
5
+ * how the font looks. Shows the current font name in the trigger button.
6
+ * Uses the same `mousedown + preventDefault()` pattern as the toolbar to
7
+ * avoid stealing editor focus.
8
+ *
9
+ * @module
10
+ */
11
+ import { getMarkValueAtSelection } from './plugins/mark-utils.js';
12
+ // ---------------------------------------------------------------------------
13
+ // Module-scoped counter for unique IDs
14
+ // ---------------------------------------------------------------------------
15
+ let instanceCounter = 0;
16
+ /**
17
+ * Default font families displayed in the picker dropdown.
18
+ *
19
+ * Organized by category: sans-serif, serif, monospace.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const picker = createFontFamilyPicker({
24
+ * container, bus, engine,
25
+ * families: DEFAULT_FONT_FAMILIES.filter(f => f.stack.includes('sans')),
26
+ * });
27
+ * ```
28
+ */
29
+ export const DEFAULT_FONT_FAMILIES = [
30
+ // Sans-serif
31
+ { value: 'Arial', label: 'Arial', stack: 'Arial, sans-serif' },
32
+ { value: 'Helvetica Neue', label: 'Helvetica Neue', stack: '"Helvetica Neue", Helvetica, sans-serif' },
33
+ { value: 'Verdana', label: 'Verdana', stack: 'Verdana, sans-serif' },
34
+ { value: 'Trebuchet MS', label: 'Trebuchet MS', stack: '"Trebuchet MS", sans-serif' },
35
+ { value: 'Tahoma', label: 'Tahoma', stack: 'Tahoma, sans-serif' },
36
+ // Serif
37
+ { value: 'Times New Roman', label: 'Times New Roman', stack: '"Times New Roman", Times, serif' },
38
+ { value: 'Georgia', label: 'Georgia', stack: 'Georgia, serif' },
39
+ { value: 'Garamond', label: 'Garamond', stack: 'Garamond, serif' },
40
+ { value: 'Palatino', label: 'Palatino', stack: 'Palatino, "Palatino Linotype", serif' },
41
+ // Monospace
42
+ { value: 'Courier New', label: 'Courier New', stack: '"Courier New", Courier, monospace' },
43
+ { value: 'Lucida Console', label: 'Lucida Console', stack: '"Lucida Console", Monaco, monospace' },
44
+ { value: 'Consolas', label: 'Consolas', stack: 'Consolas, monospace' },
45
+ { value: 'Monaco', label: 'Monaco', stack: 'Monaco, monospace' },
46
+ ];
47
+ // ---------------------------------------------------------------------------
48
+ // Factory
49
+ // ---------------------------------------------------------------------------
50
+ /**
51
+ * Create a font family picker dropdown backed by a {@link CommandBus}.
52
+ *
53
+ * The picker reflects the current font family by querying the engine's
54
+ * mark state. It subscribes to the bus for live updates. Each option
55
+ * is rendered in its own font face for preview.
56
+ *
57
+ * @param config - Picker configuration
58
+ * @returns A handle for lifecycle management
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const picker = createFontFamilyPicker({
63
+ * container: toolbar,
64
+ * bus: editor.commandBus,
65
+ * engine,
66
+ * });
67
+ * ```
68
+ */
69
+ export function createFontFamilyPicker(config) {
70
+ const { container, bus, engine, families = DEFAULT_FONT_FAMILIES, } = config;
71
+ const doc = container.ownerDocument;
72
+ const id = ++instanceCounter;
73
+ const menuId = `rtif-font-family-menu-${id}`;
74
+ let isOpen = false;
75
+ let highlightIndex = -1;
76
+ let destroyed = false;
77
+ // Total selectable items: "Default" + family options
78
+ const totalItems = 1 + families.length;
79
+ // -----------------------------------------------------------------------
80
+ // DOM creation
81
+ // -----------------------------------------------------------------------
82
+ const wrapper = doc.createElement('div');
83
+ wrapper.className = 'rtif-font-family-picker';
84
+ // Trigger button
85
+ const trigger = doc.createElement('button');
86
+ trigger.type = 'button';
87
+ trigger.className = 'rtif-font-family-picker-trigger';
88
+ trigger.setAttribute('role', 'combobox');
89
+ trigger.setAttribute('aria-haspopup', 'listbox');
90
+ trigger.setAttribute('aria-expanded', 'false');
91
+ trigger.setAttribute('aria-controls', menuId);
92
+ trigger.setAttribute('aria-label', 'Font family');
93
+ const labelSpan = doc.createElement('span');
94
+ labelSpan.className = 'rtif-font-family-picker-label';
95
+ const chevron = doc.createElement('span');
96
+ chevron.className = 'rtif-font-family-picker-chevron';
97
+ chevron.textContent = '\u25BE'; // ▾
98
+ trigger.appendChild(labelSpan);
99
+ trigger.appendChild(chevron);
100
+ // Menu
101
+ const menu = doc.createElement('div');
102
+ menu.id = menuId;
103
+ menu.className = 'rtif-font-family-picker-menu';
104
+ menu.setAttribute('role', 'listbox');
105
+ menu.setAttribute('aria-label', 'Font families');
106
+ menu.hidden = true;
107
+ // "Default" option (removes font family)
108
+ const defaultOption = doc.createElement('div');
109
+ defaultOption.className = 'rtif-font-family-picker-option rtif-font-family-picker-default';
110
+ defaultOption.id = `rtif-font-family-opt-${id}-default`;
111
+ defaultOption.setAttribute('role', 'option');
112
+ defaultOption.setAttribute('aria-selected', 'false');
113
+ defaultOption.textContent = 'Default';
114
+ defaultOption.addEventListener('mousedown', (e) => {
115
+ e.preventDefault();
116
+ selectDefault();
117
+ });
118
+ menu.appendChild(defaultOption);
119
+ // Separator
120
+ const sep = doc.createElement('div');
121
+ sep.className = 'rtif-font-family-picker-separator';
122
+ sep.setAttribute('role', 'separator');
123
+ menu.appendChild(sep);
124
+ // Family options
125
+ const optionEls = [];
126
+ for (let i = 0; i < families.length; i++) {
127
+ const family = families[i];
128
+ const option = doc.createElement('div');
129
+ option.className = 'rtif-font-family-picker-option';
130
+ option.id = `rtif-font-family-opt-${id}-${i}`;
131
+ option.setAttribute('role', 'option');
132
+ option.setAttribute('aria-selected', 'false');
133
+ option.setAttribute('data-family', family.value);
134
+ option.textContent = family.label;
135
+ option.style.fontFamily = family.stack;
136
+ option.addEventListener('mousedown', (e) => {
137
+ e.preventDefault();
138
+ selectFamily(family.value);
139
+ });
140
+ menu.appendChild(option);
141
+ optionEls.push(option);
142
+ }
143
+ wrapper.appendChild(trigger);
144
+ wrapper.appendChild(menu);
145
+ container.appendChild(wrapper);
146
+ // -----------------------------------------------------------------------
147
+ // Active state detection
148
+ // -----------------------------------------------------------------------
149
+ function findFamilyOption(value) {
150
+ return families.find((f) => f.value === value);
151
+ }
152
+ function updateDisplay() {
153
+ const { value, isMixed } = getMarkValueAtSelection(engine, 'fontFamily');
154
+ if (isMixed) {
155
+ labelSpan.textContent = '\u2014'; // em dash
156
+ labelSpan.style.fontFamily = '';
157
+ }
158
+ else if (value != null) {
159
+ const familyStr = value;
160
+ const matched = findFamilyOption(familyStr);
161
+ labelSpan.textContent = matched?.label ?? familyStr;
162
+ labelSpan.style.fontFamily = matched?.stack ?? familyStr;
163
+ }
164
+ else {
165
+ labelSpan.textContent = 'Default';
166
+ labelSpan.style.fontFamily = '';
167
+ }
168
+ // Update option selection
169
+ defaultOption.setAttribute('aria-selected', value == null && !isMixed ? 'true' : 'false');
170
+ for (const opt of optionEls) {
171
+ const optFamily = opt.getAttribute('data-family');
172
+ opt.setAttribute('aria-selected', !isMixed && value === optFamily ? 'true' : 'false');
173
+ }
174
+ }
175
+ // -----------------------------------------------------------------------
176
+ // Open / close
177
+ // -----------------------------------------------------------------------
178
+ function openMenu() {
179
+ if (isOpen)
180
+ return;
181
+ isOpen = true;
182
+ highlightIndex = -1;
183
+ menu.hidden = false;
184
+ trigger.setAttribute('aria-expanded', 'true');
185
+ trigger.removeAttribute('aria-activedescendant');
186
+ doc.addEventListener('mousedown', onDocumentMouseDown);
187
+ }
188
+ function closeMenu() {
189
+ if (!isOpen)
190
+ return;
191
+ isOpen = false;
192
+ highlightIndex = -1;
193
+ menu.hidden = true;
194
+ trigger.setAttribute('aria-expanded', 'false');
195
+ trigger.removeAttribute('aria-activedescendant');
196
+ // Clear highlight
197
+ defaultOption.classList.remove('rtif-font-family-picker-option-highlighted');
198
+ for (const el of optionEls) {
199
+ el.classList.remove('rtif-font-family-picker-option-highlighted');
200
+ }
201
+ doc.removeEventListener('mousedown', onDocumentMouseDown);
202
+ }
203
+ // -----------------------------------------------------------------------
204
+ // Selection
205
+ // -----------------------------------------------------------------------
206
+ function selectDefault() {
207
+ bus.execute('removeFontFamily');
208
+ closeMenu();
209
+ }
210
+ function selectFamily(family) {
211
+ bus.execute('setFontFamily', { family });
212
+ closeMenu();
213
+ }
214
+ // -----------------------------------------------------------------------
215
+ // Keyboard navigation
216
+ // -----------------------------------------------------------------------
217
+ function getOptionId(index) {
218
+ if (index === 0)
219
+ return defaultOption.id;
220
+ return optionEls[index - 1]?.id ?? '';
221
+ }
222
+ function onKeyDown(e) {
223
+ if (!isOpen)
224
+ return;
225
+ switch (e.key) {
226
+ case 'ArrowDown': {
227
+ e.preventDefault();
228
+ highlightIndex = (highlightIndex + 1) % totalItems;
229
+ updateHighlight();
230
+ break;
231
+ }
232
+ case 'ArrowUp': {
233
+ e.preventDefault();
234
+ highlightIndex = highlightIndex <= 0
235
+ ? totalItems - 1
236
+ : highlightIndex - 1;
237
+ updateHighlight();
238
+ break;
239
+ }
240
+ case 'Enter': {
241
+ e.preventDefault();
242
+ if (highlightIndex === 0) {
243
+ selectDefault();
244
+ }
245
+ else if (highlightIndex > 0 && highlightIndex <= families.length) {
246
+ selectFamily(families[highlightIndex - 1].value);
247
+ }
248
+ break;
249
+ }
250
+ case 'Escape': {
251
+ e.preventDefault();
252
+ closeMenu();
253
+ break;
254
+ }
255
+ }
256
+ }
257
+ function updateHighlight() {
258
+ defaultOption.classList.toggle('rtif-font-family-picker-option-highlighted', highlightIndex === 0);
259
+ for (let i = 0; i < optionEls.length; i++) {
260
+ optionEls[i].classList.toggle('rtif-font-family-picker-option-highlighted', highlightIndex === i + 1);
261
+ }
262
+ if (highlightIndex >= 0 && highlightIndex < totalItems) {
263
+ trigger.setAttribute('aria-activedescendant', getOptionId(highlightIndex));
264
+ }
265
+ else {
266
+ trigger.removeAttribute('aria-activedescendant');
267
+ }
268
+ }
269
+ // -----------------------------------------------------------------------
270
+ // Event handlers
271
+ // -----------------------------------------------------------------------
272
+ function onTriggerMouseDown(e) {
273
+ e.preventDefault();
274
+ }
275
+ function onTriggerClick() {
276
+ if (isOpen) {
277
+ closeMenu();
278
+ }
279
+ else {
280
+ openMenu();
281
+ }
282
+ }
283
+ function onDocumentMouseDown(e) {
284
+ if (!wrapper.contains(e.target)) {
285
+ closeMenu();
286
+ }
287
+ }
288
+ trigger.addEventListener('mousedown', onTriggerMouseDown);
289
+ trigger.addEventListener('click', onTriggerClick);
290
+ trigger.addEventListener('keydown', onKeyDown);
291
+ // -----------------------------------------------------------------------
292
+ // Bus subscription
293
+ // -----------------------------------------------------------------------
294
+ updateDisplay();
295
+ const unsubscribe = bus.subscribe(() => updateDisplay());
296
+ // -----------------------------------------------------------------------
297
+ // Handle
298
+ // -----------------------------------------------------------------------
299
+ return {
300
+ element: wrapper,
301
+ destroy() {
302
+ if (destroyed)
303
+ return;
304
+ destroyed = true;
305
+ unsubscribe();
306
+ closeMenu();
307
+ trigger.removeEventListener('mousedown', onTriggerMouseDown);
308
+ trigger.removeEventListener('click', onTriggerClick);
309
+ trigger.removeEventListener('keydown', onKeyDown);
310
+ wrapper.remove();
311
+ },
312
+ };
313
+ }
314
+ //# sourceMappingURL=font-family-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-family-picker.js","sourceRoot":"","sources":["../src/font-family-picker.ts"],"names":[],"mappings":"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"}