@pie-players/pie-assessment-toolkit 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -8
- package/dist/components/ItemToolBar.custom-element.js +539 -94
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/PNPToolResolver.d.ts.map +1 -1
- package/dist/services/PNPToolResolver.js +4 -1
- package/dist/services/PNPToolResolver.js.map +1 -1
- package/dist/services/ToolRegistry.d.ts +51 -0
- package/dist/services/ToolRegistry.d.ts.map +1 -1
- package/dist/services/ToolRegistry.js +60 -0
- package/dist/services/ToolRegistry.js.map +1 -1
- package/dist/services/ToolkitCoordinator.d.ts +12 -24
- package/dist/services/ToolkitCoordinator.d.ts.map +1 -1
- package/dist/services/ToolkitCoordinator.js +60 -99
- package/dist/services/ToolkitCoordinator.js.map +1 -1
- package/dist/services/defaultPersonalNeedsProfile.js +2 -2
- package/dist/services/defaultPersonalNeedsProfile.js.map +1 -1
- package/dist/services/interfaces.d.ts +8 -3
- package/dist/services/interfaces.d.ts.map +1 -1
- package/dist/services/interfaces.js.map +1 -1
- package/dist/services/pnp-standard-features.d.ts +1 -1
- package/dist/services/tool-providers/index.d.ts +0 -4
- package/dist/services/tool-providers/index.d.ts.map +1 -1
- package/dist/services/tool-providers/index.js +0 -2
- package/dist/services/tool-providers/index.js.map +1 -1
- package/dist/services/tools-config-normalizer.d.ts +40 -3
- package/dist/services/tools-config-normalizer.d.ts.map +1 -1
- package/dist/services/tools-config-normalizer.js +86 -11
- package/dist/services/tools-config-normalizer.js.map +1 -1
- package/dist/tools/client.d.ts +0 -2
- package/dist/tools/client.d.ts.map +1 -1
- package/dist/tools/client.js +0 -4
- package/dist/tools/client.js.map +1 -1
- package/dist/tools/default-tool-module-loaders.js +1 -1
- package/dist/tools/default-tool-module-loaders.js.map +1 -1
- package/dist/tools/library-loader.d.ts.map +1 -1
- package/dist/tools/library-loader.js +0 -35
- package/dist/tools/library-loader.js.map +1 -1
- package/dist/tools/registrations/accessibility-tools.d.ts.map +1 -1
- package/dist/tools/registrations/accessibility-tools.js +44 -41
- package/dist/tools/registrations/accessibility-tools.js.map +1 -1
- package/dist/tools/registrations/calculator.d.ts.map +1 -1
- package/dist/tools/registrations/calculator.js +50 -2
- package/dist/tools/registrations/calculator.js.map +1 -1
- package/dist/tools/registrations/interaction-tools.d.ts.map +1 -1
- package/dist/tools/registrations/interaction-tools.js +22 -28
- package/dist/tools/registrations/interaction-tools.js.map +1 -1
- package/dist/tools/registrations/measurement-tools.d.ts.map +1 -1
- package/dist/tools/registrations/measurement-tools.js +19 -27
- package/dist/tools/registrations/measurement-tools.js.map +1 -1
- package/dist/tools/registrations/subject-specific-tools.d.ts.map +1 -1
- package/dist/tools/registrations/subject-specific-tools.js +53 -37
- package/dist/tools/registrations/subject-specific-tools.js.map +1 -1
- package/dist/tools/registrations/toolbar-registration-helpers.d.ts +15 -0
- package/dist/tools/registrations/toolbar-registration-helpers.d.ts.map +1 -0
- package/dist/tools/registrations/toolbar-registration-helpers.js +22 -0
- package/dist/tools/registrations/toolbar-registration-helpers.js.map +1 -0
- package/dist/tools/registrations/tts.d.ts.map +1 -1
- package/dist/tools/registrations/tts.js +49 -0
- package/dist/tools/registrations/tts.js.map +1 -1
- package/dist/tools/types.d.ts +1 -1
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/package.json +8 -10
- package/dist/services/tool-providers/MathJsToolProvider.d.ts +0 -25
- package/dist/services/tool-providers/MathJsToolProvider.d.ts.map +0 -1
- package/dist/services/tool-providers/MathJsToolProvider.js +0 -53
- package/dist/services/tool-providers/MathJsToolProvider.js.map +0 -1
- package/dist/services/tool-providers/TIToolProvider.d.ts +0 -107
- package/dist/services/tool-providers/TIToolProvider.d.ts.map +0 -1
- package/dist/services/tool-providers/TIToolProvider.js +0 -123
- package/dist/services/tool-providers/TIToolProvider.js.map +0 -1
- package/dist/tools/calculators/mathjs-provider.d.ts +0 -50
- package/dist/tools/calculators/mathjs-provider.d.ts.map +0 -1
- package/dist/tools/calculators/mathjs-provider.js +0 -660
- package/dist/tools/calculators/mathjs-provider.js.map +0 -1
- package/dist/tools/calculators/ti-provider.d.ts +0 -122
- package/dist/tools/calculators/ti-provider.d.ts.map +0 -1
- package/dist/tools/calculators/ti-provider.js +0 -710
- package/dist/tools/calculators/ti-provider.js.map +0 -1
- package/src/README.md +0 -626
- package/src/components/ItemToolBar.svelte +0 -717
- package/src/components/PieAssessmentToolkit.svelte +0 -681
- package/src/components/SectionToolBar.svelte +0 -58
- package/src/components/ToolButton.svelte +0 -146
- package/src/components/ToolButtonGroup.svelte +0 -125
- package/src/tools/README.md +0 -617
- package/src/tools/calculators/README.md +0 -395
|
@@ -1,717 +0,0 @@
|
|
|
1
|
-
<svelte:options
|
|
2
|
-
customElement={{
|
|
3
|
-
tag: 'pie-item-toolbar',
|
|
4
|
-
shadow: 'open',
|
|
5
|
-
props: {
|
|
6
|
-
level: { type: 'String', attribute: 'level' },
|
|
7
|
-
scopeId: { type: 'String', attribute: 'scope-id' },
|
|
8
|
-
itemId: { type: 'String', attribute: 'item-id' },
|
|
9
|
-
sectionId: { type: 'String', attribute: 'section-id' },
|
|
10
|
-
catalogId: { type: 'String', attribute: 'catalog-id' },
|
|
11
|
-
tools: { type: 'String', attribute: 'tools' },
|
|
12
|
-
contentKind: { type: 'String', attribute: 'content-kind' },
|
|
13
|
-
position: { type: 'String', attribute: 'position' },
|
|
14
|
-
size: { type: 'String', attribute: 'size' },
|
|
15
|
-
language: { type: 'String', attribute: 'language' },
|
|
16
|
-
|
|
17
|
-
// Local runtime contracts (passed as JS properties, not attributes)
|
|
18
|
-
scopeElement: { type: 'Object', reflect: false },
|
|
19
|
-
toolRegistry: { type: 'Object', reflect: false },
|
|
20
|
-
pnpResolver: { type: 'Object', reflect: false },
|
|
21
|
-
assessment: { type: 'Object', reflect: false },
|
|
22
|
-
itemRef: { type: 'Object', reflect: false },
|
|
23
|
-
item: { type: 'Object', reflect: false },
|
|
24
|
-
hostButtons: { type: 'Object', reflect: false }
|
|
25
|
-
}
|
|
26
|
-
}}
|
|
27
|
-
/>
|
|
28
|
-
|
|
29
|
-
<!--
|
|
30
|
-
ItemToolBar - Inline toolbar for item/passage headers
|
|
31
|
-
|
|
32
|
-
Button visibility is always registry-driven:
|
|
33
|
-
- Pass 1: allowed tool IDs (PNP resolver when available, otherwise explicit tools prop)
|
|
34
|
-
- Pass 2: tool-owned isVisibleInContext(context)
|
|
35
|
-
-->
|
|
36
|
-
<script lang="ts">
|
|
37
|
-
import {
|
|
38
|
-
type AssessmentToolkitShellContext,
|
|
39
|
-
type AssessmentToolkitRuntimeContext
|
|
40
|
-
} from '../context/assessment-toolkit-context.js';
|
|
41
|
-
import {
|
|
42
|
-
connectAssessmentToolkitRuntimeContext,
|
|
43
|
-
connectAssessmentToolkitShellContext,
|
|
44
|
-
} from '../context/runtime-context-consumer.js';
|
|
45
|
-
import type { ToolRegistry, ToolToolbarRenderResult, ToolbarContext } from '../services/ToolRegistry.js';
|
|
46
|
-
import {
|
|
47
|
-
isExternalIconUrl,
|
|
48
|
-
isInlineSvgIcon,
|
|
49
|
-
isToolbarLinkItem,
|
|
50
|
-
type ToolbarItem
|
|
51
|
-
} from '../services/toolbar-items.js';
|
|
52
|
-
import type { PNPToolResolver } from '../services/PNPToolResolver.js';
|
|
53
|
-
import { createPackagedToolRegistry } from '../services/createDefaultToolRegistry.js';
|
|
54
|
-
import { DEFAULT_TOOL_MODULE_LOADERS } from '../tools/default-tool-module-loaders.js';
|
|
55
|
-
import {
|
|
56
|
-
normalizeToolsConfig,
|
|
57
|
-
parseToolList,
|
|
58
|
-
resolveToolsForLevel,
|
|
59
|
-
} from '../services/tools-config-normalizer.js';
|
|
60
|
-
import { createScopedToolId, parseScopedToolId } from '../services/tool-instance-id.js';
|
|
61
|
-
import type { AssessmentEntity, AssessmentItemRef, ItemEntity } from '@pie-players/pie-players-shared/types';
|
|
62
|
-
import type { ElementToolContext, ItemToolContext, ToolLevel, ToolContext } from '../services/tool-context.js';
|
|
63
|
-
|
|
64
|
-
const isBrowser = typeof window !== 'undefined';
|
|
65
|
-
const fallbackToolRegistry = createPackagedToolRegistry({
|
|
66
|
-
toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
let activeToolState = $state<Record<string, boolean>>({});
|
|
70
|
-
|
|
71
|
-
// Props
|
|
72
|
-
let {
|
|
73
|
-
level = 'item' as ToolLevel,
|
|
74
|
-
scopeId = '',
|
|
75
|
-
itemId = '',
|
|
76
|
-
sectionId = '',
|
|
77
|
-
catalogId = '',
|
|
78
|
-
tools = 'calculator,textToSpeech,answerEliminator',
|
|
79
|
-
contentKind = 'assessment-item',
|
|
80
|
-
position = 'bottom' as 'top' | 'right' | 'bottom' | 'left' | 'none',
|
|
81
|
-
scopeElement = null,
|
|
82
|
-
toolRegistry = null,
|
|
83
|
-
pnpResolver = null,
|
|
84
|
-
assessment = null,
|
|
85
|
-
itemRef = null,
|
|
86
|
-
item = null,
|
|
87
|
-
hostButtons = [] as ToolbarItem[],
|
|
88
|
-
class: className = '',
|
|
89
|
-
size = 'md' as 'sm' | 'md' | 'lg',
|
|
90
|
-
language = 'en-US'
|
|
91
|
-
}: {
|
|
92
|
-
level?: ToolLevel;
|
|
93
|
-
scopeId?: string;
|
|
94
|
-
itemId?: string;
|
|
95
|
-
sectionId?: string;
|
|
96
|
-
catalogId?: string;
|
|
97
|
-
tools?: string;
|
|
98
|
-
contentKind?: string;
|
|
99
|
-
position?: 'top' | 'right' | 'bottom' | 'left' | 'none';
|
|
100
|
-
scopeElement?: HTMLElement | null;
|
|
101
|
-
toolRegistry?: ToolRegistry | null;
|
|
102
|
-
pnpResolver?: PNPToolResolver | null;
|
|
103
|
-
assessment?: AssessmentEntity | null;
|
|
104
|
-
itemRef?: AssessmentItemRef | null;
|
|
105
|
-
item?: ItemEntity | null;
|
|
106
|
-
hostButtons?: ToolbarItem[];
|
|
107
|
-
class?: string;
|
|
108
|
-
size?: 'sm' | 'md' | 'lg';
|
|
109
|
-
language?: string;
|
|
110
|
-
} = $props();
|
|
111
|
-
|
|
112
|
-
let toolbarRootElement = $state<HTMLDivElement | null>(null);
|
|
113
|
-
let runtimeContext = $state<AssessmentToolkitRuntimeContext | null>(null);
|
|
114
|
-
let shellContext = $state<AssessmentToolkitShellContext | null>(null);
|
|
115
|
-
let moduleLoadVersion = $state(0);
|
|
116
|
-
|
|
117
|
-
$effect(() => {
|
|
118
|
-
if (!toolbarRootElement) return;
|
|
119
|
-
return connectAssessmentToolkitRuntimeContext(toolbarRootElement, (value) => {
|
|
120
|
-
runtimeContext = value;
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
$effect(() => {
|
|
125
|
-
if (!toolbarRootElement) return;
|
|
126
|
-
return connectAssessmentToolkitShellContext(toolbarRootElement, (value) => {
|
|
127
|
-
shellContext = value;
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const effectiveToolCoordinator = $derived(runtimeContext?.toolCoordinator);
|
|
132
|
-
const effectiveTTSService = $derived(runtimeContext?.ttsService);
|
|
133
|
-
const effectiveElementToolStateStore = $derived(runtimeContext?.elementToolStateStore);
|
|
134
|
-
const effectiveAssessmentId = $derived(runtimeContext?.assessmentId ?? '');
|
|
135
|
-
const effectiveItemId = $derived(itemId || shellContext?.itemId || '');
|
|
136
|
-
const effectiveCanonicalItemId = $derived(shellContext?.canonicalItemId || effectiveItemId);
|
|
137
|
-
const effectiveSectionId = $derived(sectionId || runtimeContext?.sectionId || '');
|
|
138
|
-
const effectiveContentKind = $derived(contentKind || shellContext?.contentKind || (level === 'section' ? 'section' : 'assessment-item'));
|
|
139
|
-
const effectiveLevel = $derived.by((): ToolLevel => {
|
|
140
|
-
if (level && level !== 'item') return level;
|
|
141
|
-
if (effectiveContentKind === 'rubric-block-stimulus') return 'passage';
|
|
142
|
-
return 'item';
|
|
143
|
-
});
|
|
144
|
-
const effectiveScopeId = $derived.by(() => {
|
|
145
|
-
if (scopeId) return scopeId;
|
|
146
|
-
if (effectiveLevel === 'section') return effectiveSectionId || runtimeContext?.sectionId || 'default-section';
|
|
147
|
-
if (effectiveLevel === 'assessment') return runtimeContext?.assessmentId || 'default-assessment';
|
|
148
|
-
return effectiveCanonicalItemId || effectiveItemId || 'default-item';
|
|
149
|
-
});
|
|
150
|
-
const effectiveCatalogId = $derived(catalogId || effectiveScopeId);
|
|
151
|
-
const effectiveItem = $derived((item as ItemEntity | null) || (shellContext?.item as ItemEntity | null) || null);
|
|
152
|
-
|
|
153
|
-
// Effective registry for visibility and metadata ownership.
|
|
154
|
-
const effectiveToolRegistry = $derived(toolRegistry || fallbackToolRegistry);
|
|
155
|
-
|
|
156
|
-
const explicitTools = $derived(parseToolList(tools));
|
|
157
|
-
const normalizedExplicitTools = $derived(
|
|
158
|
-
effectiveToolRegistry.normalizeToolIds(explicitTools).filter(Boolean)
|
|
159
|
-
);
|
|
160
|
-
const effectiveToolsConfig = $derived.by(() => {
|
|
161
|
-
const coordinatorConfig = runtimeContext?.toolkitCoordinator?.config?.tools as any;
|
|
162
|
-
return normalizeToolsConfig(coordinatorConfig || {});
|
|
163
|
-
});
|
|
164
|
-
const placementLevel = $derived.by(() => {
|
|
165
|
-
if (effectiveLevel === 'section') return 'section';
|
|
166
|
-
if (effectiveLevel === 'passage' || effectiveContentKind === 'rubric-block-stimulus') return 'passage';
|
|
167
|
-
return 'item';
|
|
168
|
-
});
|
|
169
|
-
const placementAllowedToolIds = $derived.by(() => {
|
|
170
|
-
const resolved = resolveToolsForLevel(effectiveToolsConfig, placementLevel);
|
|
171
|
-
return effectiveToolRegistry.normalizeToolIds(resolved).filter(Boolean);
|
|
172
|
-
});
|
|
173
|
-
const providerConfig = $derived(
|
|
174
|
-
(effectiveToolsConfig?.providers || {}) as Record<
|
|
175
|
-
string,
|
|
176
|
-
{ enabled?: boolean } | undefined
|
|
177
|
-
>
|
|
178
|
-
);
|
|
179
|
-
const isProviderEnabled = (toolId: string): boolean =>
|
|
180
|
-
providerConfig[toolId]?.enabled !== false;
|
|
181
|
-
|
|
182
|
-
// Pass 1: determine allowed tools
|
|
183
|
-
const allowedToolIds = $derived.by(() => {
|
|
184
|
-
const configuredTools =
|
|
185
|
-
normalizedExplicitTools.length > 0 ? normalizedExplicitTools : placementAllowedToolIds;
|
|
186
|
-
const providerEnabledConfiguredTools = configuredTools.filter((toolId) =>
|
|
187
|
-
isProviderEnabled(toolId)
|
|
188
|
-
);
|
|
189
|
-
if (pnpResolver && assessment && itemRef) {
|
|
190
|
-
const allowedByPnp = pnpResolver.getAllowedToolIds(assessment, itemRef);
|
|
191
|
-
if (providerEnabledConfiguredTools.length === 0) return allowedByPnp;
|
|
192
|
-
return allowedByPnp.filter((toolId) =>
|
|
193
|
-
providerEnabledConfiguredTools.includes(toolId)
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
return providerEnabledConfiguredTools;
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
const contentReady = $derived.by(() => {
|
|
200
|
-
if (effectiveLevel === 'section' || effectiveLevel === 'assessment') return true;
|
|
201
|
-
const config = (effectiveItem as ItemEntity | null)?.config;
|
|
202
|
-
return !!(effectiveItem && config && typeof config === 'object');
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
const toolContext = $derived.by((): ItemToolContext | null => {
|
|
206
|
-
if (effectiveLevel === 'section') {
|
|
207
|
-
return {
|
|
208
|
-
level: 'section',
|
|
209
|
-
assessment: (assessment || {}) as AssessmentEntity,
|
|
210
|
-
section: {} as any
|
|
211
|
-
} as ToolContext as ItemToolContext;
|
|
212
|
-
}
|
|
213
|
-
if (effectiveLevel === 'assessment') {
|
|
214
|
-
return {
|
|
215
|
-
level: 'assessment',
|
|
216
|
-
assessment: (assessment || {}) as AssessmentEntity
|
|
217
|
-
} as ToolContext as ItemToolContext;
|
|
218
|
-
}
|
|
219
|
-
if (!contentReady || !effectiveItem) {
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
if (effectiveLevel === 'passage') {
|
|
223
|
-
return {
|
|
224
|
-
level: 'passage',
|
|
225
|
-
assessment: (assessment || {}) as AssessmentEntity,
|
|
226
|
-
itemRef: (itemRef || ({ id: effectiveCanonicalItemId } as AssessmentItemRef)) as AssessmentItemRef,
|
|
227
|
-
passage: effectiveItem as any
|
|
228
|
-
} as ToolContext as ItemToolContext;
|
|
229
|
-
}
|
|
230
|
-
return {
|
|
231
|
-
level: 'item',
|
|
232
|
-
assessment: (assessment || {}) as AssessmentEntity,
|
|
233
|
-
itemRef: (itemRef || ({ id: effectiveCanonicalItemId } as AssessmentItemRef)) as AssessmentItemRef,
|
|
234
|
-
item: effectiveItem as ItemEntity
|
|
235
|
-
} as ToolContext as ItemToolContext;
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
const renderContext = $derived.by((): ToolContext => {
|
|
239
|
-
if (toolContext) return toolContext;
|
|
240
|
-
return {
|
|
241
|
-
level: 'item',
|
|
242
|
-
assessment: (assessment || {}) as AssessmentEntity,
|
|
243
|
-
itemRef: (itemRef || ({ id: effectiveCanonicalItemId } as AssessmentItemRef)) as AssessmentItemRef,
|
|
244
|
-
item: ((effectiveItem as ItemEntity | null) || ({ id: effectiveCanonicalItemId, config: {} } as ItemEntity)) as ItemEntity
|
|
245
|
-
} as ToolContext;
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
const elementContexts = $derived.by((): ElementToolContext[] => {
|
|
249
|
-
if (effectiveLevel !== 'item' && effectiveLevel !== 'passage') return [];
|
|
250
|
-
if (!contentReady || !effectiveItem) return [];
|
|
251
|
-
const modelsRaw = (effectiveItem as any)?.config?.models;
|
|
252
|
-
const models = Array.isArray(modelsRaw)
|
|
253
|
-
? modelsRaw
|
|
254
|
-
: modelsRaw && typeof modelsRaw === 'object'
|
|
255
|
-
? Object.values(modelsRaw)
|
|
256
|
-
: [];
|
|
257
|
-
return models
|
|
258
|
-
.filter((model: any) => model && typeof model === 'object' && typeof model.id === 'string')
|
|
259
|
-
.map((model: any) => ({
|
|
260
|
-
level: 'element' as const,
|
|
261
|
-
assessment: (assessment || {}) as AssessmentEntity,
|
|
262
|
-
itemRef: (itemRef || ({ id: effectiveCanonicalItemId } as AssessmentItemRef)) as AssessmentItemRef,
|
|
263
|
-
item: effectiveItem as ItemEntity,
|
|
264
|
-
elementId: model.id as string
|
|
265
|
-
}));
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
// Pass 2: tool-owned context filtering (item + element aggregation)
|
|
269
|
-
const visibleToolIds = $derived.by(() => {
|
|
270
|
-
const levelCompatibleToolIds = allowedToolIds.filter((toolId) => {
|
|
271
|
-
const registration = effectiveToolRegistry.get(toolId);
|
|
272
|
-
return registration ? registration.supportedLevels.includes(effectiveLevel) : false;
|
|
273
|
-
});
|
|
274
|
-
if (effectiveLevel === 'section') {
|
|
275
|
-
// Section toolbars are orchestrator-driven. Tool-level relevance is often
|
|
276
|
-
// item-content dependent. Keep pass-1 as the source of truth here.
|
|
277
|
-
return allowedToolIds;
|
|
278
|
-
}
|
|
279
|
-
if (!contentReady) {
|
|
280
|
-
// Stage 1: orchestrator-level allow-list only.
|
|
281
|
-
return levelCompatibleToolIds;
|
|
282
|
-
}
|
|
283
|
-
if (!toolContext && elementContexts.length === 0) {
|
|
284
|
-
return levelCompatibleToolIds;
|
|
285
|
-
}
|
|
286
|
-
const visible = new Set<string>();
|
|
287
|
-
if (toolContext) {
|
|
288
|
-
effectiveToolRegistry
|
|
289
|
-
.filterVisibleInContext(levelCompatibleToolIds, toolContext)
|
|
290
|
-
.forEach((tool) => visible.add(tool.toolId));
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
for (const context of elementContexts) {
|
|
294
|
-
effectiveToolRegistry
|
|
295
|
-
.filterVisibleInContext(levelCompatibleToolIds, context)
|
|
296
|
-
.forEach((tool) => visible.add(tool.toolId));
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return Array.from(visible);
|
|
300
|
-
});
|
|
301
|
-
const toolbarVisibleToolIds = $derived.by(() =>
|
|
302
|
-
effectiveToolRegistry.filterToolIdsByActivation(visibleToolIds, 'toolbar-toggle')
|
|
303
|
-
);
|
|
304
|
-
|
|
305
|
-
// Dynamically load whatever tools are currently visible.
|
|
306
|
-
// The registry owns module loader configuration by toolId.
|
|
307
|
-
$effect(() => {
|
|
308
|
-
if (!isBrowser) return;
|
|
309
|
-
let cancelled = false;
|
|
310
|
-
void effectiveToolRegistry
|
|
311
|
-
.ensureToolModulesLoaded(toolbarVisibleToolIds)
|
|
312
|
-
.then(() => {
|
|
313
|
-
if (!cancelled) moduleLoadVersion += 1;
|
|
314
|
-
})
|
|
315
|
-
.catch((error: unknown) => {
|
|
316
|
-
console.error('[ItemToolBar] Failed to load one or more tool modules:', error);
|
|
317
|
-
});
|
|
318
|
-
return () => {
|
|
319
|
-
cancelled = true;
|
|
320
|
-
};
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
function resolveScopeElement(): HTMLElement | null {
|
|
324
|
-
if (scopeElement) return scopeElement;
|
|
325
|
-
return shellContext?.scopeElement || null;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function resolveGlobalElementId(): string | null {
|
|
329
|
-
if (!effectiveElementToolStateStore || !effectiveAssessmentId || !effectiveSectionId || !effectiveCanonicalItemId)
|
|
330
|
-
return null;
|
|
331
|
-
return effectiveElementToolStateStore.getGlobalElementId(
|
|
332
|
-
effectiveAssessmentId,
|
|
333
|
-
effectiveSectionId,
|
|
334
|
-
effectiveCanonicalItemId,
|
|
335
|
-
effectiveCanonicalItemId
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const toolbarContext = $derived.by((): ToolbarContext => {
|
|
340
|
-
const toolkitCoordinator = runtimeContext?.toolkitCoordinator || null;
|
|
341
|
-
const toInstanceToolId = (toolId: string): string => {
|
|
342
|
-
// Accept either base tool IDs (e.g. "calculator") or already-scoped IDs.
|
|
343
|
-
// This keeps toolbar contracts stable across mixed registration implementations.
|
|
344
|
-
return parseScopedToolId(toolId)
|
|
345
|
-
? toolId
|
|
346
|
-
: createScopedToolId(toolId, effectiveLevel, effectiveScopeId);
|
|
347
|
-
};
|
|
348
|
-
return {
|
|
349
|
-
scope: {
|
|
350
|
-
level: effectiveLevel,
|
|
351
|
-
scopeId: effectiveScopeId,
|
|
352
|
-
assessmentId: runtimeContext?.assessmentId,
|
|
353
|
-
sectionId: effectiveSectionId,
|
|
354
|
-
itemId: effectiveItemId,
|
|
355
|
-
canonicalItemId: effectiveCanonicalItemId,
|
|
356
|
-
contentKind: effectiveContentKind
|
|
357
|
-
},
|
|
358
|
-
itemId: effectiveScopeId,
|
|
359
|
-
catalogId: effectiveCatalogId,
|
|
360
|
-
language,
|
|
361
|
-
ui: {
|
|
362
|
-
size
|
|
363
|
-
},
|
|
364
|
-
getScopeElement: resolveScopeElement,
|
|
365
|
-
getGlobalElementId: resolveGlobalElementId,
|
|
366
|
-
toolCoordinator: effectiveToolCoordinator || null,
|
|
367
|
-
toolkitCoordinator,
|
|
368
|
-
ttsService: effectiveTTSService || null,
|
|
369
|
-
elementToolStateStore: effectiveElementToolStateStore || null,
|
|
370
|
-
toggleTool: (toolId: string) => {
|
|
371
|
-
if (!effectiveToolCoordinator) return;
|
|
372
|
-
const instanceToolId = toInstanceToolId(toolId);
|
|
373
|
-
// Some tools only self-register after first visibility sync.
|
|
374
|
-
// Seed a placeholder registration so first toggle always works.
|
|
375
|
-
if (!effectiveToolCoordinator.getToolState(instanceToolId)) {
|
|
376
|
-
effectiveToolCoordinator.registerTool(instanceToolId, toolId);
|
|
377
|
-
}
|
|
378
|
-
effectiveToolCoordinator.toggleTool(instanceToolId);
|
|
379
|
-
},
|
|
380
|
-
isToolVisible: (toolId: string) => {
|
|
381
|
-
if (!effectiveToolCoordinator) return false;
|
|
382
|
-
return effectiveToolCoordinator.isToolVisible(toInstanceToolId(toolId));
|
|
383
|
-
},
|
|
384
|
-
subscribeVisibility: effectiveToolCoordinator
|
|
385
|
-
? (listener: () => void) => effectiveToolCoordinator.subscribe(listener)
|
|
386
|
-
: null,
|
|
387
|
-
ensureTTSReady: toolkitCoordinator ? () => toolkitCoordinator.ensureTTSReady() : null
|
|
388
|
-
};
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
let renderedTools = $derived.by((): ToolToolbarRenderResult[] => {
|
|
392
|
-
if (!isBrowser) return [];
|
|
393
|
-
moduleLoadVersion;
|
|
394
|
-
|
|
395
|
-
const rendered: ToolToolbarRenderResult[] = [];
|
|
396
|
-
for (const toolId of toolbarVisibleToolIds) {
|
|
397
|
-
const result = effectiveToolRegistry.renderForToolbar(toolId, renderContext, toolbarContext);
|
|
398
|
-
if (result) {
|
|
399
|
-
rendered.push(result);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return rendered;
|
|
403
|
-
});
|
|
404
|
-
const normalizedHostButtons = $derived.by((): ToolbarItem[] =>
|
|
405
|
-
Array.isArray(hostButtons)
|
|
406
|
-
? hostButtons.filter((item): item is ToolbarItem => !!item && typeof item.id === 'string')
|
|
407
|
-
: []
|
|
408
|
-
);
|
|
409
|
-
const nativeToolbarItems = $derived.by((): ToolbarItem[] =>
|
|
410
|
-
renderedTools
|
|
411
|
-
.filter((renderedTool) => !!renderedTool.button)
|
|
412
|
-
.map((renderedTool) => {
|
|
413
|
-
const button = renderedTool.button!;
|
|
414
|
-
return {
|
|
415
|
-
id: renderedTool.toolId,
|
|
416
|
-
label: button.label,
|
|
417
|
-
ariaLabel: button.ariaLabel || button.label,
|
|
418
|
-
icon: button.icon,
|
|
419
|
-
tooltip: button.tooltip || button.label,
|
|
420
|
-
disabled: button.disabled,
|
|
421
|
-
active: activeToolState[renderedTool.toolId] ?? button.active ?? false,
|
|
422
|
-
onClick: () => {
|
|
423
|
-
button.onClick();
|
|
424
|
-
syncRenderedToolsState();
|
|
425
|
-
},
|
|
426
|
-
} satisfies ToolbarItem;
|
|
427
|
-
})
|
|
428
|
-
);
|
|
429
|
-
const toolbarItems = $derived.by((): ToolbarItem[] => [
|
|
430
|
-
...nativeToolbarItems,
|
|
431
|
-
...normalizedHostButtons
|
|
432
|
-
]);
|
|
433
|
-
const mountedElementsBeforeButtons = $derived.by(() =>
|
|
434
|
-
renderedTools.flatMap((renderedTool) =>
|
|
435
|
-
(renderedTool.elements || [])
|
|
436
|
-
.filter((entry) => entry.mount === 'before-buttons')
|
|
437
|
-
.map((entry) => entry.element)
|
|
438
|
-
.filter((entry): entry is HTMLElement => Boolean(entry))
|
|
439
|
-
)
|
|
440
|
-
);
|
|
441
|
-
const mountedElementsAfterButtons = $derived.by(() =>
|
|
442
|
-
renderedTools.flatMap((renderedTool) =>
|
|
443
|
-
(renderedTool.elements || [])
|
|
444
|
-
.filter((entry) => entry.mount === 'after-buttons')
|
|
445
|
-
.map((entry) => entry.element)
|
|
446
|
-
.filter((entry): entry is HTMLElement => Boolean(entry))
|
|
447
|
-
)
|
|
448
|
-
);
|
|
449
|
-
|
|
450
|
-
function isToolbarItemActive(item: ToolbarItem): boolean {
|
|
451
|
-
if (item.id in activeToolState) {
|
|
452
|
-
return activeToolState[item.id] === true;
|
|
453
|
-
}
|
|
454
|
-
return item.active === true;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
function getFallbackIconSvg(iconName: string): string | null {
|
|
458
|
-
const iconMap: Record<string, string> = {
|
|
459
|
-
calculator:
|
|
460
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 2h10a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2Zm0 2v4h10V4H7Zm0 6v2h2v-2H7Zm4 0v2h2v-2h-2Zm4 0v2h2v-2h-2Zm-8 4v2h2v-2H7Zm4 0v2h2v-2h-2Zm4 0v2h2v-2h-2Zm-8 4v2h2v-2H7Zm4 0v2h2v-2h-2Zm4 0v2h2v-2h-2Z" fill="currentColor"/></svg>',
|
|
461
|
-
'volume-up':
|
|
462
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14 3.23v2.06A7.002 7.002 0 0 1 19 12a7 7 0 0 1-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77Zm-2 17.75V3L7 8H3v8h4l5 5Zm4.5-9a4.5 4.5 0 0 0-2.5-4.03v8.05A4.5 4.5 0 0 0 16.5 12Z" fill="currentColor"/></svg>',
|
|
463
|
-
swatch:
|
|
464
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 3a9 9 0 1 0 9 9c0-.55-.45-1-1-1h-2.5a1.5 1.5 0 0 1 0-3H20a1 1 0 0 0 1-1 8.99 8.99 0 0 0-9-4Zm-5.5 9A1.5 1.5 0 1 1 8 13.5 1.5 1.5 0 0 1 6.5 12Zm3-4A1.5 1.5 0 1 1 11 9.5 1.5 1.5 0 0 1 9.5 8Zm5 0A1.5 1.5 0 1 1 16 9.5 1.5 1.5 0 0 1 14.5 8Z" fill="currentColor"/></svg>',
|
|
465
|
-
'chart-bar':
|
|
466
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4.75 5a.76.76 0 0 1 .75.75v11c0 .438.313.75.75.75h13a.76.76 0 0 1 .696 1.039.74.74 0 0 1-.696.461h-13C5 19 4 18 4 16.75v-11A.74.74 0 0 1 4.75 5ZM8 8.25a.74.74 0 0 1 .75-.75h6.5a.76.76 0 0 1 .696 1.039.74.74 0 0 1-.696.461h-6.5A.722.722 0 0 1 8 8.25Zm.75 2.25h4.5a.76.76 0 0 1 .696 1.039.74.74 0 0 1-.696.461h-4.5a.723.723 0 0 1-.75-.75.74.74 0 0 1 .75-.75Zm0 3h8.5a.76.76 0 0 1 .696 1.039.74.74 0 0 1-.696.461h-8.5a.723.723 0 0 1-.75-.75.74.74 0 0 1 .75-.75Z" fill="currentColor"/></svg>',
|
|
467
|
-
beaker:
|
|
468
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 21c-.85 0-1.454-.38-1.813-1.137-.358-.759-.27-1.463.263-2.113L9 11V5H8a.968.968 0 0 1-.713-.287A.968.968 0 0 1 7 4c0-.283.096-.52.287-.712A.968.968 0 0 1 8 3h8c.283 0 .52.096.712.288.192.191.288.429.288.712s-.096.52-.288.713A.968.968 0 0 1 16 5h-1v6l5.55 6.75c.533.65.62 1.354.262 2.113C20.454 20.62 19.85 21 19 21H5Zm2-3h10l-3.4-4h-3.2L7 18Zm-2 1h14l-6-7.3V5h-2v6.7L5 19Z" fill="currentColor"/></svg>',
|
|
469
|
-
protractor:
|
|
470
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m6.75 21-.25-2.2 2.85-7.85a3.95 3.95 0 0 0 1.75.95l-2.75 7.55L6.75 21Zm10.5 0-1.6-1.55-2.75-7.55a3.948 3.948 0 0 0 1.75-.95l2.85 7.85-.25 2.2ZM12 11a2.893 2.893 0 0 1-2.125-.875A2.893 2.893 0 0 1 9 8c0-.65.188-1.23.563-1.737A2.935 2.935 0 0 1 11 5.2V3h2v2.2c.583.2 1.063.554 1.438 1.063C14.812 6.77 15 7.35 15 8c0 .833-.292 1.542-.875 2.125A2.893 2.893 0 0 1 12 11Zm0-2c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Z" fill="currentColor"/></svg>',
|
|
471
|
-
'bars-3':
|
|
472
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.85 15c.517 0 .98-.15 1.388-.45.408-.3.695-.692.862-1.175l.375-1.15c.267-.8.2-1.537-.2-2.213C8.875 9.337 8.3 9 7.55 9H4.025l.475 3.925c.083.583.346 1.075.787 1.475.442.4.963.6 1.563.6Zm10.3 0c.6 0 1.12-.2 1.563-.6.441-.4.704-.892.787-1.475L19.975 9h-3.5c-.75 0-1.325.342-1.725 1.025-.4.683-.467 1.425-.2 2.225l.35 1.125c.167.483.454.875.862 1.175.409.3.871.45 1.388.45Zm-10.3 2c-1.1 0-2.063-.363-2.887-1.088a4.198 4.198 0 0 1-1.438-2.737L2 9H1V7h6.55c.733 0 1.404.18 2.013.537A3.906 3.906 0 0 1 11 9h2.025c.35-.617.83-1.104 1.438-1.463A3.892 3.892 0 0 1 16.474 7H23v2h-1l-.525 4.175a4.198 4.198 0 0 1-1.438 2.737A4.238 4.238 0 0 1 17.15 17c-.95 0-1.804-.27-2.562-.813A4.234 4.234 0 0 1 13 14.026l-.375-1.125a21.35 21.35 0 0 1-.1-.363 4.926 4.926 0 0 1-.1-.537h-.85c-.033.2-.067.363-.1.488a21.35 21.35 0 0 1-.1.362L11 14a4.3 4.3 0 0 1-1.588 2.175A4.258 4.258 0 0 1 6.85 17Z" fill="currentColor"/></svg>',
|
|
473
|
-
ruler:
|
|
474
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m8.8 10.95 2.15-2.175-1.4-1.425-1.1 1.1-1.4-1.4 1.075-1.1L7 4.825 4.825 7 8.8 10.95Zm8.2 8.225L19.175 17l-1.125-1.125-1.1 1.075-1.4-1.4 1.075-1.1-1.425-1.4-2.15 2.15L17 19.175ZM7.25 21H3v-4.25l4.375-4.375L2 7l5-5 5.4 5.4 3.775-3.8c.2-.2.425-.35.675-.45a2.068 2.068 0 0 1 1.55 0c.25.1.475.25.675.45L20.4 4.95c.2.2.35.425.45.675.1.25.15.508.15.775a1.975 1.975 0 0 1-.6 1.425l-3.775 3.8L22 17l-5 5-5.375-5.375L7.25 21ZM5 19h1.4l9.8-9.775L14.775 7.8 5 17.6V19Z" fill="currentColor"/></svg>'
|
|
475
|
-
};
|
|
476
|
-
return iconMap[iconName] || null;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function syncRenderedToolsState() {
|
|
480
|
-
const nextActiveState: Record<string, boolean> = {};
|
|
481
|
-
for (const renderedTool of renderedTools) {
|
|
482
|
-
renderedTool.sync?.();
|
|
483
|
-
if (renderedTool.button) {
|
|
484
|
-
nextActiveState[renderedTool.toolId] = renderedTool.button.active ?? false;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
activeToolState = nextActiveState;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
$effect(() => {
|
|
491
|
-
const unsubs: Array<() => void> = [];
|
|
492
|
-
syncRenderedToolsState();
|
|
493
|
-
for (const renderedTool of renderedTools) {
|
|
494
|
-
if (renderedTool.subscribeActive) {
|
|
495
|
-
const unsubscribe = renderedTool.subscribeActive((active) => {
|
|
496
|
-
activeToolState = {
|
|
497
|
-
...activeToolState,
|
|
498
|
-
[renderedTool.toolId]: active
|
|
499
|
-
};
|
|
500
|
-
renderedTool.sync?.();
|
|
501
|
-
});
|
|
502
|
-
unsubs.push(unsubscribe);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
return () => {
|
|
507
|
-
unsubs.forEach((unsub) => unsub());
|
|
508
|
-
};
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
$effect(() => {
|
|
512
|
-
if (!toolbarContext.subscribeVisibility) return;
|
|
513
|
-
return toolbarContext.subscribeVisibility(() => {
|
|
514
|
-
syncRenderedToolsState();
|
|
515
|
-
});
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
function mountElement(node: HTMLSpanElement, element: HTMLElement | null) {
|
|
519
|
-
let mountedElement: HTMLElement | null = null;
|
|
520
|
-
const updateMountedElement = (nextElement: HTMLElement | null) => {
|
|
521
|
-
if (mountedElement === nextElement) return;
|
|
522
|
-
if (mountedElement && mountedElement.parentNode === node) {
|
|
523
|
-
node.removeChild(mountedElement);
|
|
524
|
-
}
|
|
525
|
-
mountedElement = nextElement;
|
|
526
|
-
if (mountedElement) {
|
|
527
|
-
if (mountedElement.parentNode && mountedElement.parentNode !== node) {
|
|
528
|
-
mountedElement.parentNode.removeChild(mountedElement);
|
|
529
|
-
}
|
|
530
|
-
node.appendChild(mountedElement);
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
updateMountedElement(element);
|
|
534
|
-
return {
|
|
535
|
-
update(nextElement: HTMLElement | null) {
|
|
536
|
-
updateMountedElement(nextElement);
|
|
537
|
-
},
|
|
538
|
-
destroy() {
|
|
539
|
-
updateMountedElement(null);
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
</script>
|
|
544
|
-
|
|
545
|
-
{#if isBrowser}
|
|
546
|
-
<div
|
|
547
|
-
class="item-toolbar {className} item-toolbar--{size}"
|
|
548
|
-
class:item-toolbar--top={position === 'top'}
|
|
549
|
-
class:item-toolbar--right={position === 'right'}
|
|
550
|
-
class:item-toolbar--bottom={position === 'bottom'}
|
|
551
|
-
class:item-toolbar--left={position === 'left'}
|
|
552
|
-
data-content-kind={effectiveContentKind}
|
|
553
|
-
data-level={effectiveLevel}
|
|
554
|
-
bind:this={toolbarRootElement}
|
|
555
|
-
>
|
|
556
|
-
{#each mountedElementsBeforeButtons as mountedElement (`before-${mountedElement.tagName}-${mountedElement.getAttribute('tool-id') || ''}`)}
|
|
557
|
-
<span class="item-toolbar__element-host" use:mountElement={mountedElement}></span>
|
|
558
|
-
{/each}
|
|
559
|
-
|
|
560
|
-
{#each toolbarItems as item (item.id)}
|
|
561
|
-
{#if isToolbarLinkItem(item)}
|
|
562
|
-
<a
|
|
563
|
-
class="item-toolbar__button"
|
|
564
|
-
class:item-toolbar__button--active={isToolbarItemActive(item)}
|
|
565
|
-
href={item.disabled ? undefined : item.href}
|
|
566
|
-
target={item.target}
|
|
567
|
-
rel={item.rel}
|
|
568
|
-
aria-label={item.ariaLabel || item.label}
|
|
569
|
-
title={item.tooltip || item.label}
|
|
570
|
-
aria-disabled={item.disabled ? 'true' : undefined}
|
|
571
|
-
onclick={(event) => {
|
|
572
|
-
if (item.disabled) {
|
|
573
|
-
event.preventDefault();
|
|
574
|
-
}
|
|
575
|
-
}}
|
|
576
|
-
>
|
|
577
|
-
{#if item.icon}
|
|
578
|
-
{#if isInlineSvgIcon(item.icon)}
|
|
579
|
-
{@html item.icon}
|
|
580
|
-
{:else if isExternalIconUrl(item.icon)}
|
|
581
|
-
<img class="item-toolbar__icon-image" src={item.icon} alt="" />
|
|
582
|
-
{:else}
|
|
583
|
-
{@const fallbackIcon = getFallbackIconSvg(item.icon)}
|
|
584
|
-
{#if fallbackIcon}
|
|
585
|
-
{@html fallbackIcon}
|
|
586
|
-
{:else}
|
|
587
|
-
<i class={`icon icon-${item.icon}`} aria-hidden="true"></i>
|
|
588
|
-
{/if}
|
|
589
|
-
{/if}
|
|
590
|
-
{/if}
|
|
591
|
-
</a>
|
|
592
|
-
{:else}
|
|
593
|
-
<button
|
|
594
|
-
type="button"
|
|
595
|
-
class="item-toolbar__button"
|
|
596
|
-
class:item-toolbar__button--active={isToolbarItemActive(item)}
|
|
597
|
-
onclick={item.onClick}
|
|
598
|
-
aria-label={item.ariaLabel || item.label}
|
|
599
|
-
aria-pressed={isToolbarItemActive(item)}
|
|
600
|
-
title={item.tooltip || item.label}
|
|
601
|
-
disabled={item.disabled}
|
|
602
|
-
>
|
|
603
|
-
{#if item.icon}
|
|
604
|
-
{#if isInlineSvgIcon(item.icon)}
|
|
605
|
-
{@html item.icon}
|
|
606
|
-
{:else if isExternalIconUrl(item.icon)}
|
|
607
|
-
<img class="item-toolbar__icon-image" src={item.icon} alt="" />
|
|
608
|
-
{:else}
|
|
609
|
-
{@const fallbackIcon = getFallbackIconSvg(item.icon)}
|
|
610
|
-
{#if fallbackIcon}
|
|
611
|
-
{@html fallbackIcon}
|
|
612
|
-
{:else}
|
|
613
|
-
<i class={`icon icon-${item.icon}`} aria-hidden="true"></i>
|
|
614
|
-
{/if}
|
|
615
|
-
{/if}
|
|
616
|
-
{/if}
|
|
617
|
-
</button>
|
|
618
|
-
{/if}
|
|
619
|
-
{/each}
|
|
620
|
-
|
|
621
|
-
{#each mountedElementsAfterButtons as mountedElement (`after-${mountedElement.tagName}-${mountedElement.getAttribute('tool-id') || ''}`)}
|
|
622
|
-
<span class="item-toolbar__element-host" use:mountElement={mountedElement}></span>
|
|
623
|
-
{/each}
|
|
624
|
-
</div>
|
|
625
|
-
{/if}
|
|
626
|
-
|
|
627
|
-
<style>
|
|
628
|
-
.item-toolbar {
|
|
629
|
-
display: flex;
|
|
630
|
-
align-items: center;
|
|
631
|
-
gap: 0.5rem;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
.item-toolbar--top,
|
|
635
|
-
.item-toolbar--bottom {
|
|
636
|
-
flex-direction: row;
|
|
637
|
-
flex-wrap: wrap;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
.item-toolbar--left,
|
|
641
|
-
.item-toolbar--right {
|
|
642
|
-
flex-direction: column;
|
|
643
|
-
align-items: stretch;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
.item-toolbar__button {
|
|
647
|
-
display: flex;
|
|
648
|
-
align-items: center;
|
|
649
|
-
justify-content: center;
|
|
650
|
-
width: 2rem;
|
|
651
|
-
height: 2rem;
|
|
652
|
-
padding: 0.25rem;
|
|
653
|
-
border: 1px solid var(--pie-border, #ccc);
|
|
654
|
-
border-radius: 0.25rem;
|
|
655
|
-
background-color: var(--pie-background, white);
|
|
656
|
-
color: var(--pie-text, #333);
|
|
657
|
-
cursor: pointer;
|
|
658
|
-
transition: all 0.15s ease;
|
|
659
|
-
text-decoration: none;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
.item-toolbar__element-host {
|
|
663
|
-
display: contents;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
.item-toolbar--sm .item-toolbar__button {
|
|
667
|
-
width: 2.75rem;
|
|
668
|
-
height: 2.75rem;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
.item-toolbar--lg .item-toolbar__button {
|
|
672
|
-
width: 2.5rem;
|
|
673
|
-
height: 2.5rem;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
.item-toolbar__button:hover:not(:disabled) {
|
|
677
|
-
background-color: var(--pie-secondary-background, #f5f5f5);
|
|
678
|
-
transform: translateY(-1px);
|
|
679
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
.item-toolbar__button:active:not(:disabled) {
|
|
683
|
-
transform: translateY(0);
|
|
684
|
-
box-shadow: none;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
.item-toolbar__button--active {
|
|
688
|
-
background-color: var(--pie-primary, #1976d2);
|
|
689
|
-
color: white;
|
|
690
|
-
border-color: var(--pie-primary, #1976d2);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
.item-toolbar__button--active:hover:not(:disabled) {
|
|
694
|
-
background-color: var(--pie-primary-dark, #1565c0);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
.item-toolbar__button:focus-visible {
|
|
698
|
-
outline: 2px solid var(--pie-primary, #1976d2);
|
|
699
|
-
outline-offset: 2px;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
.item-toolbar__button:disabled {
|
|
703
|
-
opacity: 0.5;
|
|
704
|
-
cursor: not-allowed;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
.item-toolbar__button :global(svg) {
|
|
708
|
-
width: 100%;
|
|
709
|
-
height: 100%;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
.item-toolbar__icon-image {
|
|
713
|
-
width: 100%;
|
|
714
|
-
height: 100%;
|
|
715
|
-
object-fit: contain;
|
|
716
|
-
}
|
|
717
|
-
</style>
|