@vue-skuilder/common-ui 0.1.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.
Files changed (68) hide show
  1. package/dist/assets/index.css +10 -0
  2. package/dist/common-ui.es.js +16404 -0
  3. package/dist/common-ui.es.js.map +1 -0
  4. package/dist/common-ui.umd.js +9 -0
  5. package/dist/common-ui.umd.js.map +1 -0
  6. package/dist/components/HeatMap.types.d.ts +13 -0
  7. package/dist/components/PaginatingToolbar.types.d.ts +40 -0
  8. package/dist/components/SkMouseTrap.types.d.ts +3 -0
  9. package/dist/components/SkMouseTrapToolTip.types.d.ts +35 -0
  10. package/dist/components/SnackbarService.d.ts +11 -0
  11. package/dist/components/StudySession.types.d.ts +6 -0
  12. package/dist/components/auth/index.d.ts +4 -0
  13. package/dist/components/cardRendering/MarkdownRendererHelpers.d.ts +22 -0
  14. package/dist/components/studentInputs/BaseUserInput.d.ts +16 -0
  15. package/dist/components/studentInputs/RadioMultipleChoice.types.d.ts +5 -0
  16. package/dist/composables/CompositionViewable.d.ts +33 -0
  17. package/dist/composables/Displayable.d.ts +47 -0
  18. package/dist/composables/index.d.ts +2 -0
  19. package/dist/index.d.ts +36 -0
  20. package/dist/plugins/pinia.d.ts +5 -0
  21. package/dist/stores/useAuthStore.d.ts +225 -0
  22. package/dist/stores/useCardPreviewModeStore.d.ts +8 -0
  23. package/dist/stores/useConfigStore.d.ts +11 -0
  24. package/dist/utils/SkldrMouseTrap.d.ts +32 -0
  25. package/package.json +67 -0
  26. package/src/components/HeatMap.types.ts +15 -0
  27. package/src/components/HeatMap.vue +354 -0
  28. package/src/components/PaginatingToolbar.types.ts +48 -0
  29. package/src/components/PaginatingToolbar.vue +75 -0
  30. package/src/components/SkMouseTrap.types.ts +3 -0
  31. package/src/components/SkMouseTrap.vue +70 -0
  32. package/src/components/SkMouseTrapToolTip.types.ts +41 -0
  33. package/src/components/SkMouseTrapToolTip.vue +316 -0
  34. package/src/components/SnackbarService.ts +39 -0
  35. package/src/components/SnackbarService.vue +71 -0
  36. package/src/components/StudySession.types.ts +6 -0
  37. package/src/components/StudySession.vue +670 -0
  38. package/src/components/StudySessionTimer.vue +121 -0
  39. package/src/components/auth/UserChip.vue +106 -0
  40. package/src/components/auth/UserLogin.vue +141 -0
  41. package/src/components/auth/UserLoginAndRegistrationContainer.vue +85 -0
  42. package/src/components/auth/UserRegistration.vue +181 -0
  43. package/src/components/auth/index.ts +4 -0
  44. package/src/components/cardRendering/AudioAutoPlayer.vue +131 -0
  45. package/src/components/cardRendering/CardLoader.vue +123 -0
  46. package/src/components/cardRendering/CardViewer.vue +101 -0
  47. package/src/components/cardRendering/CodeBlockRenderer.vue +81 -0
  48. package/src/components/cardRendering/MarkdownRenderer.vue +46 -0
  49. package/src/components/cardRendering/MarkdownRendererHelpers.ts +114 -0
  50. package/src/components/cardRendering/MdTokenRenderer.vue +244 -0
  51. package/src/components/studentInputs/BaseUserInput.ts +71 -0
  52. package/src/components/studentInputs/MultipleChoiceOption.vue +127 -0
  53. package/src/components/studentInputs/RadioMultipleChoice.types.ts +6 -0
  54. package/src/components/studentInputs/RadioMultipleChoice.vue +168 -0
  55. package/src/components/studentInputs/TrueFalse.vue +27 -0
  56. package/src/components/studentInputs/UserInputNumber.vue +63 -0
  57. package/src/components/studentInputs/UserInputString.vue +89 -0
  58. package/src/components/studentInputs/fillInInput.vue +71 -0
  59. package/src/composables/CompositionViewable.ts +180 -0
  60. package/src/composables/Displayable.ts +133 -0
  61. package/src/composables/index.ts +2 -0
  62. package/src/index.ts +79 -0
  63. package/src/plugins/pinia.ts +24 -0
  64. package/src/stores/useAuthStore.ts +92 -0
  65. package/src/stores/useCardPreviewModeStore.ts +32 -0
  66. package/src/stores/useConfigStore.ts +60 -0
  67. package/src/utils/SkldrMouseTrap.ts +141 -0
  68. package/src/vue-shims.d.ts +5 -0
@@ -0,0 +1,15 @@
1
+ export interface DayData {
2
+ date: string;
3
+ count: number;
4
+ }
5
+
6
+ export interface Color {
7
+ h: number;
8
+ s: number;
9
+ l: number;
10
+ }
11
+
12
+ export interface ActivityRecord {
13
+ timeStamp: number | string;
14
+ [key: string]: any;
15
+ }
@@ -0,0 +1,354 @@
1
+ <template>
2
+ <div>
3
+ <svg :width="width" :height="height">
4
+ <g
5
+ v-for="(week, weekIndex) in weeks"
6
+ :key="weekIndex"
7
+ :transform="`translate(${weekIndex * (cellSize + cellMargin)}, 0)`"
8
+ >
9
+ <rect
10
+ v-for="(day, dayIndex) in week"
11
+ :key="day.date"
12
+ :x="0"
13
+ :y="dayIndex * (cellSize + cellMargin)"
14
+ :width="cellSize"
15
+ :height="cellSize"
16
+ :fill="getColor(day.count)"
17
+ @mouseover="showTooltip(day, $event)"
18
+ @mouseout="hideTooltip"
19
+ />
20
+ </g>
21
+ </svg>
22
+ <div v-if="tooltipData" class="tooltip" :style="tooltipStyle">
23
+ {{ tooltipData.count }} review{{ tooltipData.count !== 1 ? 's' : '' }} on {{ toDateString(tooltipData.date) }}
24
+ </div>
25
+ </div>
26
+ </template>
27
+
28
+ <script lang="ts">
29
+ import { defineComponent, PropType } from 'vue';
30
+ import moment from 'moment';
31
+ import { DayData, Color, ActivityRecord } from './HeatMap.types';
32
+
33
+ export default defineComponent({
34
+ name: 'HeatMap',
35
+
36
+ props: {
37
+ // Accept activity records directly as a prop
38
+ activityRecords: {
39
+ type: Array as PropType<ActivityRecord[]>,
40
+ default: () => [],
41
+ },
42
+ // Accept a function that can retrieve activity records
43
+ activityRecordsGetter: {
44
+ type: Function as PropType<() => Promise<ActivityRecord[]>>,
45
+ default: null,
46
+ },
47
+ // Customize colors
48
+ inactiveColor: {
49
+ type: Object as PropType<Color>,
50
+ default: () => ({ h: 0, s: 0, l: 0.9 }),
51
+ },
52
+ activeColor: {
53
+ type: Object as PropType<Color>,
54
+ default: () => ({ h: 155, s: 1, l: 0.5 }),
55
+ },
56
+ // Customize size
57
+ cellSize: {
58
+ type: Number,
59
+ default: 12,
60
+ },
61
+ cellMargin: {
62
+ type: Number,
63
+ default: 3,
64
+ },
65
+ // Enable/disable seasonal colors
66
+ enableSeasonalColors: {
67
+ type: Boolean,
68
+ default: true,
69
+ },
70
+ },
71
+
72
+ data() {
73
+ return {
74
+ isLoading: false,
75
+ localActivityRecords: [] as ActivityRecord[],
76
+ heatmapData: {} as { [key: string]: number },
77
+ weeks: [] as DayData[][],
78
+ tooltipData: null as DayData | null,
79
+ tooltipStyle: {} as { [key: string]: string },
80
+ maxInRange: 0,
81
+ };
82
+ },
83
+
84
+ computed: {
85
+ width(): number {
86
+ return 53 * (this.cellSize + this.cellMargin);
87
+ },
88
+ height(): number {
89
+ return 7 * (this.cellSize + this.cellMargin);
90
+ },
91
+ effectiveActivityRecords(): ActivityRecord[] {
92
+ const useLocal = Array.isArray(this.localActivityRecords) && this.localActivityRecords.length > 0;
93
+ const records = useLocal ? this.localActivityRecords : this.activityRecords || [];
94
+ console.log('Using effectiveActivityRecords, count:', records.length, 'source:', useLocal ? 'local' : 'prop');
95
+ return records;
96
+ },
97
+ },
98
+
99
+ watch: {
100
+ activityRecords: {
101
+ handler() {
102
+ this.processRecords();
103
+ this.createWeeksData();
104
+ },
105
+ immediate: true,
106
+ },
107
+ },
108
+
109
+ async created() {
110
+ if (this.activityRecordsGetter) {
111
+ try {
112
+ this.isLoading = true;
113
+ console.log('Fetching activity records using getter...');
114
+
115
+ // Ensure the getter is called safely with proper error handling
116
+ let result = await this.activityRecordsGetter();
117
+
118
+ // Handle the result - ensure it's an array of activity records
119
+ if (Array.isArray(result)) {
120
+ // Filter out records with invalid timestamps before processing
121
+ this.localActivityRecords = result.filter(record => {
122
+ if (!record || !record.timeStamp) return false;
123
+
124
+ // Basic validation check for timestamps
125
+ try {
126
+ const m = moment(record.timeStamp);
127
+ return m.isValid() && m.year() > 2000 && m.year() < 2100;
128
+ } catch (e) {
129
+ return false;
130
+ }
131
+ });
132
+
133
+ console.log(`Received ${result.length} records, ${this.localActivityRecords.length} valid after filtering`);
134
+
135
+ // Process the loaded records
136
+ this.processRecords();
137
+ this.createWeeksData();
138
+ } else {
139
+ console.error('Activity records getter did not return an array:', result);
140
+ this.localActivityRecords = [];
141
+ }
142
+ } catch (error) {
143
+ console.error('Error fetching activity records:', error);
144
+ this.localActivityRecords = [];
145
+ } finally {
146
+ this.isLoading = false;
147
+ }
148
+ } else {
149
+ console.log('No activityRecordsGetter provided, using direct activityRecords prop');
150
+ }
151
+ },
152
+
153
+ methods: {
154
+ toDateString(d: string): string {
155
+ const m = moment(d);
156
+ return moment.months()[m.month()] + ' ' + m.date();
157
+ },
158
+
159
+ processRecords() {
160
+ const records = this.effectiveActivityRecords || [];
161
+ console.log(`Processing ${records.length} records`);
162
+
163
+ const data: { [key: string]: number } = {};
164
+
165
+ if (records.length === 0) {
166
+ console.log('No records to process');
167
+ this.heatmapData = data;
168
+ return;
169
+ }
170
+
171
+ // Sample logging of a few records to understand structure without flooding console
172
+ const uniqueDates = new Set<string>();
173
+ const dateDistribution: Record<string, number> = {};
174
+ let validCount = 0;
175
+ let invalidCount = 0;
176
+
177
+ for (let i = 0; i < records.length; i++) {
178
+ const record = records[i];
179
+
180
+ if (!record || typeof record !== 'object' || !record.timeStamp) {
181
+ invalidCount++;
182
+ continue;
183
+ }
184
+
185
+ try {
186
+ // Attempt to normalize the timestamp
187
+ let normalizedDate: string;
188
+
189
+ if (typeof record.timeStamp === 'string') {
190
+ // For ISO strings, parse directly with moment
191
+ normalizedDate = moment(record.timeStamp).format('YYYY-MM-DD');
192
+ } else if (typeof record.timeStamp === 'number') {
193
+ // For numeric timestamps, use Date constructor then moment
194
+ normalizedDate = moment(new Date(record.timeStamp)).format('YYYY-MM-DD');
195
+ } else if (typeof record.timeStamp === 'object') {
196
+ // For objects (like Moment), try toString() or direct parsing
197
+ if (typeof record.timeStamp.format === 'function') {
198
+ // It's likely a Moment object
199
+ normalizedDate = record.timeStamp.format('YYYY-MM-DD');
200
+ } else if (record.timeStamp instanceof Date) {
201
+ normalizedDate = moment(record.timeStamp).format('YYYY-MM-DD');
202
+ } else {
203
+ // Try to parse it as a string representation
204
+ normalizedDate = moment(String(record.timeStamp)).format('YYYY-MM-DD');
205
+ }
206
+ } else {
207
+ // Unhandled type
208
+ invalidCount++;
209
+ continue;
210
+ }
211
+
212
+ // Verify the date is valid before using it
213
+ if (moment(normalizedDate, 'YYYY-MM-DD', true).isValid()) {
214
+ data[normalizedDate] = (data[normalizedDate] || 0) + 1;
215
+ uniqueDates.add(normalizedDate);
216
+
217
+ // Track distribution by month for debugging
218
+ const month = normalizedDate.substring(0, 7); // YYYY-MM
219
+ dateDistribution[month] = (dateDistribution[month] || 0) + 1;
220
+
221
+ validCount++;
222
+ } else {
223
+ invalidCount++;
224
+ }
225
+ } catch (e) {
226
+ invalidCount++;
227
+ }
228
+ }
229
+
230
+ // Log summary statistics
231
+ console.log(`Processed ${validCount} valid dates, ${invalidCount} invalid dates`);
232
+ console.log(`Found ${uniqueDates.size} unique dates`);
233
+ console.log('Date distribution by month:', dateDistribution);
234
+
235
+ this.heatmapData = data;
236
+ },
237
+
238
+ createWeeksData() {
239
+ // Reset weeks and max count
240
+ this.weeks = [];
241
+ this.maxInRange = 0;
242
+
243
+ const end = moment();
244
+ const start = end.clone().subtract(52, 'weeks');
245
+ const day = start.clone().startOf('week');
246
+
247
+ console.log('Creating weeks data from', start.format('YYYY-MM-DD'), 'to', end.format('YYYY-MM-DD'));
248
+
249
+ // Ensure we have data to display
250
+ if (Object.keys(this.heatmapData).length === 0) {
251
+ console.log('No heatmap data available to display');
252
+ }
253
+
254
+ // For debugging, log some sample dates from the heatmap data
255
+ const sampleDates = Object.keys(this.heatmapData).slice(0, 5);
256
+ console.log('Sample dates in heatmap data:', sampleDates);
257
+
258
+ // Build the week data structure
259
+ while (day.isSameOrBefore(end)) {
260
+ const weekData: DayData[] = [];
261
+ for (let i = 0; i < 7; i++) {
262
+ const date = day.format('YYYY-MM-DD');
263
+ const count = this.heatmapData[date] || 0;
264
+ const dayData: DayData = {
265
+ date,
266
+ count,
267
+ };
268
+ weekData.push(dayData);
269
+ if (dayData.count > this.maxInRange) {
270
+ this.maxInRange = dayData.count;
271
+ }
272
+
273
+ day.add(1, 'day');
274
+ }
275
+ this.weeks.push(weekData);
276
+ }
277
+
278
+ console.log('Weeks data created, maxInRange:', this.maxInRange);
279
+
280
+ // Calculate summary stats for display
281
+ let totalDaysWithActivity = 0;
282
+ let totalActivity = 0;
283
+
284
+ Object.values(this.heatmapData).forEach(count => {
285
+ totalDaysWithActivity++;
286
+ totalActivity += count;
287
+ });
288
+
289
+ console.log(`Activity summary: ${totalActivity} activities across ${totalDaysWithActivity} days`);
290
+ },
291
+
292
+ getColor(count: number): string {
293
+ if (this.maxInRange === 0) return this.hslToString(this.inactiveColor);
294
+
295
+ const t = count === 0 ? 0 : Math.min((2 * count) / this.maxInRange, 1);
296
+
297
+ let seasonalColor: Color = this.activeColor;
298
+
299
+ if (this.enableSeasonalColors) {
300
+ const now = moment();
301
+ if (now.month() === 11 && now.date() >= 5) {
302
+ // Christmas colors
303
+ seasonalColor = Math.random() > 0.5 ? { h: 350, s: 0.8, l: 0.5 } : { h: 135, s: 0.8, l: 0.4 };
304
+ } else if (now.month() === 9 && now.date() >= 25) {
305
+ // Halloween colors
306
+ seasonalColor =
307
+ Math.random() > 0.5
308
+ ? { h: 0, s: 0, l: 0 }
309
+ : Math.random() > 0.5
310
+ ? { h: 30, s: 1, l: 0.5 }
311
+ : { h: 270, s: 1, l: 0.5 };
312
+ }
313
+ }
314
+
315
+ const h = seasonalColor.h;
316
+ const s = this.interpolate(this.inactiveColor.s, seasonalColor.s, t);
317
+ const l = this.interpolate(this.inactiveColor.l, seasonalColor.l, t);
318
+
319
+ return this.hslToString({ h, s, l });
320
+ },
321
+
322
+ interpolate(start: number, end: number, t: number): number {
323
+ return start + (end - start) * t;
324
+ },
325
+
326
+ hslToString(color: Color): string {
327
+ return `hsl(${color.h}, ${color.s * 100}%, ${color.l * 100}%)`;
328
+ },
329
+
330
+ showTooltip(day: DayData, event: MouseEvent) {
331
+ this.tooltipData = day;
332
+ this.tooltipStyle = {
333
+ position: 'absolute',
334
+ left: `${event.pageX + 10}px`,
335
+ top: `${event.pageY + 10}px`,
336
+ };
337
+ },
338
+
339
+ hideTooltip() {
340
+ this.tooltipData = null;
341
+ },
342
+ },
343
+ });
344
+ </script>
345
+
346
+ <style scoped>
347
+ .tooltip {
348
+ background-color: rgba(0, 0, 0, 0.8);
349
+ color: white;
350
+ padding: 5px;
351
+ border-radius: 3px;
352
+ font-size: 12px;
353
+ }
354
+ </style>
@@ -0,0 +1,48 @@
1
+ export interface PaginatingToolbarProps {
2
+ /**
3
+ * Array of page numbers
4
+ */
5
+ pages: number[];
6
+
7
+ /**
8
+ * Current active page
9
+ */
10
+ page: number;
11
+
12
+ /**
13
+ * Main title displayed in the toolbar
14
+ */
15
+ title?: string;
16
+
17
+ /**
18
+ * Secondary text displayed next to the title
19
+ */
20
+ subtitle?: string;
21
+ }
22
+
23
+ export interface PaginatingToolbarEvents {
24
+ /**
25
+ * Navigate to the first page
26
+ */
27
+ (e: 'first'): void;
28
+
29
+ /**
30
+ * Navigate to the previous page
31
+ */
32
+ (e: 'prev'): void;
33
+
34
+ /**
35
+ * Navigate to the next page
36
+ */
37
+ (e: 'next'): void;
38
+
39
+ /**
40
+ * Navigate to the last page
41
+ */
42
+ (e: 'last'): void;
43
+
44
+ /**
45
+ * Set the page to a specific number
46
+ */
47
+ (e: 'set-page', page: number): void;
48
+ }
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <v-toolbar density="compact">
3
+ <v-toolbar-title>
4
+ <span>{{ title }}</span>
5
+ <span v-if="subtitle" class="ms-2 text-subtitle-2">{{ subtitle }}</span>
6
+ </v-toolbar-title>
7
+ <v-spacer></v-spacer>
8
+ <v-btn variant="text" icon color="secondary" :disabled="page == 1" @click="$emit('first')">
9
+ <v-icon>mdi-page-first</v-icon>
10
+ </v-btn>
11
+ <v-btn variant="text" icon color="secondary" :disabled="page == 1" @click="$emit('prev')">
12
+ <v-icon>mdi-chevron-left</v-icon>
13
+ </v-btn>
14
+
15
+ <v-select
16
+ :model-value="page"
17
+ :items="pages"
18
+ class="pageSelect"
19
+ density="compact"
20
+ hide-details
21
+ :return-object="false"
22
+ variant="outlined"
23
+ @update:model-value="(val: unknown) => $emit('set-page', val)"
24
+ >
25
+ <template #selection="{ item }">
26
+ {{ item.value }}
27
+ </template>
28
+ </v-select>
29
+
30
+ <v-btn variant="text" icon color="secondary" :disabled="page == pages.length" @click="$emit('next')">
31
+ <v-icon>mdi-chevron-right</v-icon>
32
+ </v-btn>
33
+ <v-btn variant="text" icon color="secondary" :disabled="page == pages.length" @click="$emit('last')">
34
+ <v-icon>mdi-page-last</v-icon>
35
+ </v-btn>
36
+ </v-toolbar>
37
+ </template>
38
+
39
+ <script lang="ts">
40
+ import { defineComponent, PropType } from 'vue';
41
+ import type { PaginatingToolbarProps, PaginatingToolbarEvents } from './PaginatingToolbar.types';
42
+
43
+ export default defineComponent({
44
+ name: 'PaginatingToolbar',
45
+
46
+ props: {
47
+ pages: {
48
+ type: Array as PropType<number[]>,
49
+ required: true,
50
+ },
51
+ page: {
52
+ type: Number,
53
+ required: true,
54
+ },
55
+ title: {
56
+ type: String,
57
+ required: false,
58
+ default: '',
59
+ },
60
+ subtitle: {
61
+ type: String,
62
+ required: false,
63
+ default: '',
64
+ },
65
+ } as const,
66
+
67
+ emits: ['first', 'prev', 'next', 'last', 'set-page'],
68
+ });
69
+ </script>
70
+
71
+ <style scoped>
72
+ .pageSelect {
73
+ max-width: 60px;
74
+ }
75
+ </style>
@@ -0,0 +1,3 @@
1
+ export interface SkMouseTrapProps {
2
+ refreshInterval?: number;
3
+ }
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <v-dialog v-if="display" max-width="500px" transition="dialog-transition">
3
+ <template #activator="{ props }">
4
+ <v-btn icon color="primary" v-bind="props">
5
+ <v-icon>mdi-keyboard</v-icon>
6
+ </v-btn>
7
+ </template>
8
+
9
+ <v-card>
10
+ <v-toolbar color="teal" dark dense>
11
+ <v-toolbar-title class="text-subtitle-1">Shortcut keys:</v-toolbar-title>
12
+ </v-toolbar>
13
+ <v-list dense>
14
+ <v-list-item
15
+ v-for="hk in commands"
16
+ :key="Array.isArray(hk.hotkey) ? hk.hotkey.join(',') : hk.hotkey"
17
+ class="py-1"
18
+ >
19
+ <v-btn variant="outlined" color="primary" class="text-white" size="small">
20
+ {{ Array.isArray(hk.hotkey) ? hk.hotkey[0] : hk.hotkey }}
21
+ </v-btn>
22
+ <v-spacer></v-spacer>
23
+ <span class="text-caption ml-2">{{ hk.command }}</span>
24
+ </v-list-item>
25
+ </v-list>
26
+ </v-card>
27
+ </v-dialog>
28
+ </template>
29
+
30
+ <script lang="ts">
31
+ import { defineComponent, PropType } from 'vue';
32
+ import { SkldrMouseTrap, HotKeyMetaData } from '../utils/SkldrMouseTrap';
33
+ import { SkMouseTrapProps } from './SkMouseTrap.types';
34
+
35
+ export default defineComponent({
36
+ name: 'SkMouseTrap',
37
+
38
+ props: {
39
+ refreshInterval: {
40
+ type: Number as PropType<SkMouseTrapProps['refreshInterval']>,
41
+ default: 500,
42
+ },
43
+ },
44
+
45
+ data() {
46
+ return {
47
+ commands: [] as HotKeyMetaData[],
48
+ display: false,
49
+ intervalId: null as number | null,
50
+ };
51
+ },
52
+
53
+ created() {
54
+ this.intervalId = window.setInterval(this.refreshState, this.refreshInterval);
55
+ },
56
+
57
+ beforeUnmount() {
58
+ if (this.intervalId !== null) {
59
+ clearInterval(this.intervalId);
60
+ }
61
+ },
62
+
63
+ methods: {
64
+ refreshState() {
65
+ this.commands = SkldrMouseTrap.commands;
66
+ this.display = this.commands.length > 0;
67
+ },
68
+ },
69
+ });
70
+ </script>
@@ -0,0 +1,41 @@
1
+ export interface SkMouseTrapToolTipProps {
2
+ /**
3
+ * The keyboard shortcut(s) to bind to this tooltip.
4
+ * Can be a single string like "ctrl+s" or an array of strings.
5
+ */
6
+ hotkey: string | string[];
7
+
8
+ /**
9
+ * Descriptive name for the keyboard shortcut.
10
+ * Will be displayed in the shortcut dialog.
11
+ */
12
+ command: string;
13
+
14
+ /**
15
+ * Whether the shortcut is currently disabled.
16
+ * @default false
17
+ */
18
+ disabled?: boolean;
19
+
20
+ /**
21
+ * The position of the tooltip relative to the wrapped element.
22
+ * @default 'top'
23
+ */
24
+ position?: 'top' | 'bottom' | 'left' | 'right';
25
+
26
+ /**
27
+ * Whether to show the tooltip when the Ctrl key is pressed.
28
+ * @default true
29
+ */
30
+ showTooltip?: boolean;
31
+
32
+ /**
33
+ * The visual effect to apply to the wrapped element when Ctrl is pressed.
34
+ * @default 'glow'
35
+ */
36
+ highlightEffect?: 'glow' | 'scale' | 'border' | 'none';
37
+ }
38
+
39
+ export interface SkMouseTrapToolTipEmits {
40
+ (event: 'hotkey-triggered', hotkey: string | string[]): void;
41
+ }