@pie-players/pie-section-tools-toolbar 0.2.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.
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/section-tools-toolbar.js +2957 -0
- package/dist/section-tools-toolbar.js.map +1 -0
- package/index.ts +8 -0
- package/package.json +67 -0
- package/section-tools-toolbar.svelte +386 -0
package/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pie-section-tools-toolbar - PIE Assessment Section Tools Toolbar
|
|
3
|
+
*
|
|
4
|
+
* This package exports a web component built from Svelte.
|
|
5
|
+
* Import the built version for CDN usage, or the .svelte source for Svelte projects.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Re-export any TypeScript types defined in the package
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pie-players/pie-section-tools-toolbar",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Section-level tools toolbar for PIE assessment player",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/pie-framework/pie-players.git"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"pie",
|
|
15
|
+
"assessment",
|
|
16
|
+
"tools",
|
|
17
|
+
"toolbar",
|
|
18
|
+
"calculator",
|
|
19
|
+
"accessibility"
|
|
20
|
+
],
|
|
21
|
+
"svelte": "./section-tools-toolbar.svelte",
|
|
22
|
+
"main": "./dist/section-tools-toolbar.js",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/section-tools-toolbar.js",
|
|
27
|
+
"svelte": "./section-tools-toolbar.svelte"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"section-tools-toolbar.svelte",
|
|
33
|
+
"index.ts",
|
|
34
|
+
"package.json"
|
|
35
|
+
],
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"svelte": "^5.0.0"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"unpkg": "./dist/section-tools-toolbar.js",
|
|
41
|
+
"jsdelivr": "./dist/section-tools-toolbar.js",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@pie-players/pie-assessment-toolkit": "workspace:*",
|
|
44
|
+
"@pie-players/pie-players-shared": "workspace:*",
|
|
45
|
+
"@pie-players/pie-tool-graph": "workspace:*",
|
|
46
|
+
"@pie-players/pie-tool-periodic-table": "workspace:*",
|
|
47
|
+
"@pie-players/pie-tool-protractor": "workspace:*",
|
|
48
|
+
"@pie-players/pie-tool-line-reader": "workspace:*",
|
|
49
|
+
"@pie-players/pie-tool-magnifier": "workspace:*",
|
|
50
|
+
"@pie-players/pie-calculator-mathjs": "workspace:*"
|
|
51
|
+
},
|
|
52
|
+
"types": "./dist/index.d.ts",
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "vite build",
|
|
55
|
+
"dev": "vite build --watch",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"lint": "biome check ."
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@biomejs/biome": "^2.3.10",
|
|
61
|
+
"@sveltejs/vite-plugin-svelte": "^6.1.4",
|
|
62
|
+
"svelte": "^5.16.1",
|
|
63
|
+
"typescript": "^5.7.0",
|
|
64
|
+
"vite": "^7.0.8",
|
|
65
|
+
"vite-plugin-dts": "^4.5.3"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
<svelte:options
|
|
2
|
+
customElement={{
|
|
3
|
+
tag: 'pie-section-tools-toolbar',
|
|
4
|
+
shadow: 'none',
|
|
5
|
+
props: {
|
|
6
|
+
enabledTools: { type: 'String', attribute: 'enabled-tools' },
|
|
7
|
+
// Services passed as JS properties (not attributes)
|
|
8
|
+
toolCoordinator: { type: 'Object', reflect: false },
|
|
9
|
+
toolProviderRegistry: { type: 'Object', reflect: false }
|
|
10
|
+
}
|
|
11
|
+
}}
|
|
12
|
+
/>
|
|
13
|
+
|
|
14
|
+
<!--
|
|
15
|
+
SectionToolsToolbar - Section-level floating tools toolbar
|
|
16
|
+
|
|
17
|
+
Displays tool buttons (calculator, graph, periodic table, etc.) in a toolbar
|
|
18
|
+
positioned at the bottom of the section layout. Tools appear as floating
|
|
19
|
+
overlays managed by the ToolCoordinator.
|
|
20
|
+
|
|
21
|
+
Similar to SchoolCity pattern - section-wide tools independent of item navigation.
|
|
22
|
+
-->
|
|
23
|
+
<script lang="ts">
|
|
24
|
+
import type {
|
|
25
|
+
IToolCoordinator,
|
|
26
|
+
ToolProviderRegistry,
|
|
27
|
+
} from '@pie-players/pie-assessment-toolkit';
|
|
28
|
+
import { onDestroy, onMount } from 'svelte';
|
|
29
|
+
|
|
30
|
+
// Import tool web components to register them
|
|
31
|
+
import '@pie-players/pie-tool-calculator';
|
|
32
|
+
import '@pie-players/pie-tool-graph';
|
|
33
|
+
import '@pie-players/pie-tool-periodic-table';
|
|
34
|
+
import '@pie-players/pie-tool-protractor';
|
|
35
|
+
import '@pie-players/pie-tool-line-reader';
|
|
36
|
+
import '@pie-players/pie-tool-magnifier';
|
|
37
|
+
import '@pie-players/pie-tool-ruler';
|
|
38
|
+
|
|
39
|
+
const isBrowser = typeof window !== 'undefined';
|
|
40
|
+
|
|
41
|
+
// Props
|
|
42
|
+
let {
|
|
43
|
+
enabledTools = 'calculator,graph,periodicTable,protractor,lineReader,magnifier,ruler',
|
|
44
|
+
toolCoordinator,
|
|
45
|
+
toolProviderRegistry
|
|
46
|
+
}: {
|
|
47
|
+
enabledTools?: string;
|
|
48
|
+
toolCoordinator?: IToolCoordinator;
|
|
49
|
+
toolProviderRegistry?: ToolProviderRegistry;
|
|
50
|
+
} = $props();
|
|
51
|
+
|
|
52
|
+
// Parse enabled tools from comma-separated string
|
|
53
|
+
let enabledToolsList = $derived(
|
|
54
|
+
enabledTools
|
|
55
|
+
.split(',')
|
|
56
|
+
.map((t) => t.trim())
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Tool visibility state (reactive to coordinator changes)
|
|
61
|
+
let showCalculator = $state(false);
|
|
62
|
+
let showGraph = $state(false);
|
|
63
|
+
let showPeriodicTable = $state(false);
|
|
64
|
+
let showProtractor = $state(false);
|
|
65
|
+
let showLineReader = $state(false);
|
|
66
|
+
let showMagnifier = $state(false);
|
|
67
|
+
let showRuler = $state(false);
|
|
68
|
+
|
|
69
|
+
// Update visibility state from coordinator
|
|
70
|
+
function updateToolVisibility() {
|
|
71
|
+
if (!toolCoordinator) return;
|
|
72
|
+
showCalculator = toolCoordinator.isToolVisible('calculator');
|
|
73
|
+
showGraph = toolCoordinator.isToolVisible('graph');
|
|
74
|
+
showPeriodicTable = toolCoordinator.isToolVisible('periodicTable');
|
|
75
|
+
showProtractor = toolCoordinator.isToolVisible('protractor');
|
|
76
|
+
showLineReader = toolCoordinator.isToolVisible('lineReader');
|
|
77
|
+
showMagnifier = toolCoordinator.isToolVisible('magnifier');
|
|
78
|
+
showRuler = toolCoordinator.isToolVisible('ruler');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Toggle tool visibility
|
|
82
|
+
function toggleTool(toolId: string) {
|
|
83
|
+
if (!toolCoordinator) return;
|
|
84
|
+
toolCoordinator.toggleTool(toolId);
|
|
85
|
+
updateToolVisibility();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Subscribe to tool coordinator changes
|
|
89
|
+
let unsubscribe: (() => void) | null = null;
|
|
90
|
+
|
|
91
|
+
onMount(() => {
|
|
92
|
+
if (toolCoordinator) {
|
|
93
|
+
updateToolVisibility();
|
|
94
|
+
unsubscribe = toolCoordinator.subscribe(() => {
|
|
95
|
+
updateToolVisibility();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
onDestroy(() => {
|
|
101
|
+
unsubscribe?.();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Tool button definitions
|
|
105
|
+
const toolButtons = $derived([
|
|
106
|
+
{
|
|
107
|
+
id: 'calculator',
|
|
108
|
+
label: 'Calculator',
|
|
109
|
+
icon: '🔢',
|
|
110
|
+
ariaLabel: 'Scientific calculator',
|
|
111
|
+
visible: showCalculator,
|
|
112
|
+
enabled: enabledToolsList.includes('calculator')
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'graph',
|
|
116
|
+
label: 'Graph',
|
|
117
|
+
icon: '📈',
|
|
118
|
+
ariaLabel: 'Graphing tool',
|
|
119
|
+
visible: showGraph,
|
|
120
|
+
enabled: enabledToolsList.includes('graph')
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'periodicTable',
|
|
124
|
+
label: 'Periodic Table',
|
|
125
|
+
icon: '⚛️',
|
|
126
|
+
ariaLabel: 'Periodic table of elements',
|
|
127
|
+
visible: showPeriodicTable,
|
|
128
|
+
enabled: enabledToolsList.includes('periodicTable')
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: 'protractor',
|
|
132
|
+
label: 'Protractor',
|
|
133
|
+
icon: '📐',
|
|
134
|
+
ariaLabel: 'Angle measurement tool',
|
|
135
|
+
visible: showProtractor,
|
|
136
|
+
enabled: enabledToolsList.includes('protractor')
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'lineReader',
|
|
140
|
+
label: 'Line Reader',
|
|
141
|
+
icon: '📏',
|
|
142
|
+
ariaLabel: 'Line reading guide',
|
|
143
|
+
visible: showLineReader,
|
|
144
|
+
enabled: enabledToolsList.includes('lineReader')
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'magnifier',
|
|
148
|
+
label: 'Magnifier',
|
|
149
|
+
icon: '🔍',
|
|
150
|
+
ariaLabel: 'Text magnification tool',
|
|
151
|
+
visible: showMagnifier,
|
|
152
|
+
enabled: enabledToolsList.includes('magnifier')
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'ruler',
|
|
156
|
+
label: 'Ruler',
|
|
157
|
+
icon: '📏',
|
|
158
|
+
ariaLabel: 'Measurement ruler',
|
|
159
|
+
visible: showRuler,
|
|
160
|
+
enabled: enabledToolsList.includes('ruler')
|
|
161
|
+
}
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
// Tool element references for service binding
|
|
165
|
+
let calculatorElement = $state<HTMLElement | null>(null);
|
|
166
|
+
let graphElement = $state<HTMLElement | null>(null);
|
|
167
|
+
let periodicTableElement = $state<HTMLElement | null>(null);
|
|
168
|
+
let protractorElement = $state<HTMLElement | null>(null);
|
|
169
|
+
let lineReaderElement = $state<HTMLElement | null>(null);
|
|
170
|
+
let magnifierElement = $state<HTMLElement | null>(null);
|
|
171
|
+
let rulerElement = $state<HTMLElement | null>(null);
|
|
172
|
+
|
|
173
|
+
// Bind coordinator to tool elements
|
|
174
|
+
$effect(() => {
|
|
175
|
+
if (toolCoordinator) {
|
|
176
|
+
if (calculatorElement) {
|
|
177
|
+
(calculatorElement as any).coordinator = toolCoordinator;
|
|
178
|
+
}
|
|
179
|
+
if (graphElement) {
|
|
180
|
+
(graphElement as any).coordinator = toolCoordinator;
|
|
181
|
+
}
|
|
182
|
+
if (periodicTableElement) {
|
|
183
|
+
(periodicTableElement as any).coordinator = toolCoordinator;
|
|
184
|
+
}
|
|
185
|
+
if (protractorElement) {
|
|
186
|
+
(protractorElement as any).coordinator = toolCoordinator;
|
|
187
|
+
}
|
|
188
|
+
if (lineReaderElement) {
|
|
189
|
+
(lineReaderElement as any).coordinator = toolCoordinator;
|
|
190
|
+
}
|
|
191
|
+
if (magnifierElement) {
|
|
192
|
+
(magnifierElement as any).coordinator = toolCoordinator;
|
|
193
|
+
}
|
|
194
|
+
if (rulerElement) {
|
|
195
|
+
(rulerElement as any).coordinator = toolCoordinator;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Initialize calculator provider if needed
|
|
201
|
+
$effect(() => {
|
|
202
|
+
if (
|
|
203
|
+
isBrowser &&
|
|
204
|
+
toolProviderRegistry &&
|
|
205
|
+
enabledToolsList.includes('calculator')
|
|
206
|
+
) {
|
|
207
|
+
// Pre-initialize calculator provider on toolbar mount
|
|
208
|
+
toolProviderRegistry.getProvider('calculator-desmos').catch((err) => {
|
|
209
|
+
console.warn(
|
|
210
|
+
'[SectionToolsToolbar] Calculator provider not available:',
|
|
211
|
+
err
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
</script>
|
|
217
|
+
|
|
218
|
+
{#if isBrowser}
|
|
219
|
+
<div class="section-tools-toolbar" role="toolbar" aria-label="Assessment tools">
|
|
220
|
+
<div class="tools-buttons">
|
|
221
|
+
{#each toolButtons as tool (tool.id)}
|
|
222
|
+
{#if tool.enabled}
|
|
223
|
+
<button
|
|
224
|
+
type="button"
|
|
225
|
+
class="tool-button"
|
|
226
|
+
class:active={tool.visible}
|
|
227
|
+
onclick={() => toggleTool(tool.id)}
|
|
228
|
+
title={tool.ariaLabel}
|
|
229
|
+
aria-label={tool.ariaLabel}
|
|
230
|
+
aria-pressed={tool.visible}
|
|
231
|
+
>
|
|
232
|
+
<span class="tool-icon" aria-hidden="true">{tool.icon}</span>
|
|
233
|
+
<span class="tool-label">{tool.label}</span>
|
|
234
|
+
</button>
|
|
235
|
+
{/if}
|
|
236
|
+
{/each}
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<!-- Tool Instances - Rendered outside toolbar for floating overlays -->
|
|
241
|
+
<!-- These are managed by ToolCoordinator with z-index layering -->
|
|
242
|
+
|
|
243
|
+
{#if enabledToolsList.includes('calculator')}
|
|
244
|
+
<pie-tool-calculator
|
|
245
|
+
bind:this={calculatorElement}
|
|
246
|
+
visible={showCalculator}
|
|
247
|
+
coordinator={toolProviderRegistry ? { getToolProvider: (id) => toolProviderRegistry.getProvider(id) } : undefined}
|
|
248
|
+
|
|
249
|
+
tool-id="calculator"
|
|
250
|
+
></pie-tool-calculator>
|
|
251
|
+
{/if}
|
|
252
|
+
|
|
253
|
+
{#if enabledToolsList.includes('graph')}
|
|
254
|
+
<pie-tool-graph
|
|
255
|
+
bind:this={graphElement}
|
|
256
|
+
visible={showGraph}
|
|
257
|
+
tool-id="graph"
|
|
258
|
+
></pie-tool-graph>
|
|
259
|
+
{/if}
|
|
260
|
+
|
|
261
|
+
{#if enabledToolsList.includes('periodicTable')}
|
|
262
|
+
<pie-tool-periodic-table
|
|
263
|
+
bind:this={periodicTableElement}
|
|
264
|
+
visible={showPeriodicTable}
|
|
265
|
+
tool-id="periodicTable"
|
|
266
|
+
></pie-tool-periodic-table>
|
|
267
|
+
{/if}
|
|
268
|
+
|
|
269
|
+
{#if enabledToolsList.includes('protractor')}
|
|
270
|
+
<pie-tool-protractor
|
|
271
|
+
bind:this={protractorElement}
|
|
272
|
+
visible={showProtractor}
|
|
273
|
+
tool-id="protractor"
|
|
274
|
+
></pie-tool-protractor>
|
|
275
|
+
{/if}
|
|
276
|
+
|
|
277
|
+
{#if enabledToolsList.includes('lineReader')}
|
|
278
|
+
<pie-tool-line-reader
|
|
279
|
+
bind:this={lineReaderElement}
|
|
280
|
+
visible={showLineReader}
|
|
281
|
+
tool-id="lineReader"
|
|
282
|
+
></pie-tool-line-reader>
|
|
283
|
+
{/if}
|
|
284
|
+
|
|
285
|
+
{#if enabledToolsList.includes('magnifier')}
|
|
286
|
+
<pie-tool-magnifier
|
|
287
|
+
bind:this={magnifierElement}
|
|
288
|
+
visible={showMagnifier}
|
|
289
|
+
tool-id="magnifier"
|
|
290
|
+
></pie-tool-magnifier>
|
|
291
|
+
{/if}
|
|
292
|
+
|
|
293
|
+
{#if enabledToolsList.includes('ruler')}
|
|
294
|
+
<pie-tool-ruler
|
|
295
|
+
bind:this={rulerElement}
|
|
296
|
+
visible={showRuler}
|
|
297
|
+
tool-id="ruler"
|
|
298
|
+
></pie-tool-ruler>
|
|
299
|
+
{/if}
|
|
300
|
+
{/if}
|
|
301
|
+
|
|
302
|
+
<style>
|
|
303
|
+
.section-tools-toolbar {
|
|
304
|
+
display: flex;
|
|
305
|
+
align-items: center;
|
|
306
|
+
padding: 0.75rem 1rem;
|
|
307
|
+
background-color: var(--pie-background, #ffffff);
|
|
308
|
+
border-top: 1px solid var(--pie-border, #e0e0e0);
|
|
309
|
+
min-height: 60px;
|
|
310
|
+
gap: 1rem;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.tools-buttons {
|
|
314
|
+
display: flex;
|
|
315
|
+
gap: 0.5rem;
|
|
316
|
+
flex-wrap: wrap;
|
|
317
|
+
align-items: center;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.tool-button {
|
|
321
|
+
display: flex;
|
|
322
|
+
align-items: center;
|
|
323
|
+
gap: 0.5rem;
|
|
324
|
+
padding: 0.5rem 1rem;
|
|
325
|
+
background-color: var(--pie-background, #ffffff);
|
|
326
|
+
border: 1px solid var(--pie-border, #d0d0d0);
|
|
327
|
+
border-radius: 4px;
|
|
328
|
+
cursor: pointer;
|
|
329
|
+
font-size: 0.875rem;
|
|
330
|
+
color: var(--pie-text, #333);
|
|
331
|
+
white-space: nowrap;
|
|
332
|
+
transition: all 0.15s ease;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.tool-button:hover:not(:disabled) {
|
|
336
|
+
background-color: var(--pie-secondary-background, #f5f5f5);
|
|
337
|
+
transform: translateY(-1px);
|
|
338
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.tool-button:active:not(:disabled) {
|
|
342
|
+
transform: translateY(0);
|
|
343
|
+
box-shadow: none;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.tool-button.active {
|
|
347
|
+
background-color: var(--pie-primary, #1976d2);
|
|
348
|
+
color: white;
|
|
349
|
+
border-color: var(--pie-primary, #1976d2);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.tool-button.active:hover:not(:disabled) {
|
|
353
|
+
background-color: var(--pie-primary-dark, #1565c0);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.tool-button:focus-visible {
|
|
357
|
+
outline: 2px solid var(--pie-primary, #1976d2);
|
|
358
|
+
outline-offset: 2px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.tool-button:disabled {
|
|
362
|
+
opacity: 0.5;
|
|
363
|
+
cursor: not-allowed;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.tool-icon {
|
|
367
|
+
font-size: 1.25rem;
|
|
368
|
+
line-height: 1;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.tool-label {
|
|
372
|
+
font-size: 0.875rem;
|
|
373
|
+
font-weight: 500;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* Responsive: Hide labels on narrow screens */
|
|
377
|
+
@media (max-width: 640px) {
|
|
378
|
+
.tool-label {
|
|
379
|
+
display: none;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.tool-button {
|
|
383
|
+
padding: 0.5rem;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
</style>
|