@a3s-lab/office 0.33.0 → 0.34.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.
@@ -0,0 +1,20 @@
1
+ import type { WorkSlide, WorkSlideAnimation, WorkSlideAnimationEffect } from './work-types';
2
+ export { WORK_SLIDE_ANIMATION_LIMIT, WORK_SLIDE_ANIMATION_MAX_DELAY_MS, WORK_SLIDE_ANIMATION_MAX_DURATION_MS, WORK_SLIDE_ANIMATION_MIN_DURATION_MS, } from './work-presentation-animation-constraints';
3
+ export interface WorkSlideAnimationCueItem {
4
+ animation: WorkSlideAnimation;
5
+ endOffsetMs: number;
6
+ startOffsetMs: number;
7
+ }
8
+ export interface WorkSlideAnimationCue {
9
+ automatic: boolean;
10
+ items: WorkSlideAnimationCueItem[];
11
+ totalDurationMs: number;
12
+ }
13
+ export declare function createWorkSlideAnimation(elementId: string, effect: WorkSlideAnimationEffect, previous?: WorkSlideAnimation): WorkSlideAnimation;
14
+ export declare function normalizeWorkSlideAnimation(animation: WorkSlideAnimation): WorkSlideAnimation;
15
+ export declare function workSlideAnimationForElement(slide: WorkSlide, elementId: string | undefined): WorkSlideAnimation | undefined;
16
+ export declare function workSlideAnimationIndex(slide: WorkSlide, animationId: string | undefined): number;
17
+ export declare function workSlideAnimationCues(animations: readonly WorkSlideAnimation[] | undefined): WorkSlideAnimationCue[];
18
+ export declare function initialWorkSlideAnimationCueIndex(cues: readonly WorkSlideAnimationCue[]): number;
19
+ export declare function removeWorkSlideAnimationsForElements(slide: WorkSlide, elementIds: ReadonlySet<string>): WorkSlide;
20
+ export declare function remapWorkSlideAnimations(animations: readonly WorkSlideAnimation[] | undefined, elementIds: ReadonlyMap<string, string>): WorkSlideAnimation[] | undefined;
@@ -1,11 +1,13 @@
1
- import type { WorkSlide, WorkSlideElement } from './work-types';
1
+ import type { WorkSlide, WorkSlideAnimation, WorkSlideElement } from './work-types';
2
2
  export declare const PRESENTATION_OBJECT_OFFSET_STEP = 5;
3
3
  export type WorkPresentationClipboardPayload = {
4
4
  kind: 'element';
5
5
  element: WorkSlideElement;
6
+ animation?: WorkSlideAnimation;
6
7
  } | {
7
8
  kind: 'elements';
8
9
  elements: WorkSlideElement[];
10
+ animations?: WorkSlideAnimation[];
9
11
  } | {
10
12
  kind: 'slide';
11
13
  slide: WorkSlide;
@@ -14,13 +16,18 @@ export interface WorkPresentationClipboardRead {
14
16
  payload: WorkPresentationClipboardPayload;
15
17
  offset: number;
16
18
  }
17
- export declare function copyPresentationElement(element: WorkSlideElement): void;
18
- export declare function copyPresentationElements(elements: readonly WorkSlideElement[]): void;
19
+ export interface WorkPresentationElementPaste {
20
+ animations?: WorkSlideAnimation[];
21
+ elements: WorkSlideElement[];
22
+ }
23
+ export declare function copyPresentationElement(element: WorkSlideElement, animation?: WorkSlideAnimation): void;
24
+ export declare function copyPresentationElements(elements: readonly WorkSlideElement[], animations?: readonly WorkSlideAnimation[]): void;
19
25
  export declare function copyPresentationSlide(slide: WorkSlide): void;
20
26
  export declare function takePresentationClipboard(): WorkPresentationClipboardRead | null;
21
27
  export declare function hasPresentationClipboard(): boolean;
22
28
  export declare function clearPresentationClipboard(): void;
23
29
  export declare function clonePresentationElementForPaste(element: WorkSlideElement, offset: number): WorkSlideElement;
24
30
  export declare function clonePresentationElementsForPaste(elements: readonly WorkSlideElement[], offset: number): WorkSlideElement[];
31
+ export declare function clonePresentationElementsAndAnimationsForPaste(elements: readonly WorkSlideElement[], animations: readonly WorkSlideAnimation[], offset: number): WorkPresentationElementPaste;
25
32
  export declare function clonePresentationSlideForPaste(slide: WorkSlide, existingSlideNames?: readonly string[]): WorkSlide;
26
33
  export declare function nextPresentationSlideCopyName(sourceName: string, existingSlideNames: readonly string[]): string;
@@ -543,6 +543,22 @@ export interface WorkSlideTransition {
543
543
  advanceOnClick: boolean;
544
544
  advanceAfterMs?: number;
545
545
  }
546
+ export type WorkSlideAnimationEffect = 'appear' | 'fade' | 'fly-in' | 'zoom';
547
+ export type WorkSlideAnimationTrigger = 'on-click' | 'with-previous' | 'after-previous';
548
+ export type WorkSlideAnimationDirection = 'left' | 'right' | 'up' | 'down';
549
+ /**
550
+ * One bounded entrance animation applied to one slide-owned element.
551
+ * Array order on `WorkSlide.animations` is the canonical playback order.
552
+ */
553
+ export interface WorkSlideAnimation {
554
+ id: string;
555
+ elementId: string;
556
+ effect: WorkSlideAnimationEffect;
557
+ trigger: WorkSlideAnimationTrigger;
558
+ durationMs: number;
559
+ delayMs: number;
560
+ direction?: WorkSlideAnimationDirection;
561
+ }
546
562
  export interface WorkSlideElement {
547
563
  id: string;
548
564
  type: WorkSlideElementType;
@@ -587,6 +603,7 @@ export interface WorkSlide {
587
603
  notes?: string;
588
604
  comments?: WorkSlideComment[];
589
605
  transition?: WorkSlideTransition;
606
+ animations?: WorkSlideAnimation[];
590
607
  }
591
608
  export interface WorkSlideComment {
592
609
  id: string;
Binary file
package/dist/styles.css CHANGED
@@ -20175,6 +20175,129 @@ sub [data-office-character-position-half-points], sup [data-office-character-pos
20175
20175
  }
20176
20176
  }
20177
20177
 
20178
+ .work-presentation-animation-options, .work-presentation-animation-timing {
20179
+ border: 0;
20180
+ align-items: center;
20181
+ gap: 6px;
20182
+ min-width: 0;
20183
+ margin: 0;
20184
+ padding: 0;
20185
+ display: flex;
20186
+ }
20187
+
20188
+ .work-presentation-animation-options .work-office-field, .work-presentation-animation-timing .work-office-field {
20189
+ flex: none;
20190
+ }
20191
+
20192
+ .work-presentation-animation-options .work-office-field.effect {
20193
+ width: 88px;
20194
+ }
20195
+
20196
+ .work-presentation-animation-options .work-office-field.trigger {
20197
+ width: 120px;
20198
+ }
20199
+
20200
+ .work-presentation-animation-options .work-office-field.direction {
20201
+ width: 82px;
20202
+ }
20203
+
20204
+ .work-presentation-animation-timing .work-office-field.seconds {
20205
+ width: 64px;
20206
+ }
20207
+
20208
+ .work-presentation-player .work-slide-element[data-slide-animation-state="pending"] {
20209
+ visibility: hidden;
20210
+ opacity: 0;
20211
+ }
20212
+
20213
+ .work-presentation-player .work-slide-element[data-slide-animation-state="playing"] {
20214
+ animation-delay: var(--work-slide-animation-delay, 0s);
20215
+ animation-duration: var(--work-slide-animation-duration, .5s);
20216
+ animation-timing-function: cubic-bezier(.16, 1, .3, 1);
20217
+ animation-fill-mode: both;
20218
+ }
20219
+
20220
+ .work-presentation-player .work-slide-element[data-slide-animation-state="finished"] {
20221
+ visibility: visible;
20222
+ opacity: var(--work-slide-element-opacity, 1);
20223
+ translate: 0;
20224
+ scale: 1;
20225
+ }
20226
+
20227
+ .work-presentation-player .work-slide-element[data-slide-animation-effect="appear"][data-slide-animation-state="playing"] {
20228
+ animation-name: work-slide-animation-appear;
20229
+ animation-timing-function: step-start;
20230
+ }
20231
+
20232
+ .work-presentation-player .work-slide-element[data-slide-animation-effect="fade"][data-slide-animation-state="playing"] {
20233
+ animation-name: work-slide-animation-fade;
20234
+ }
20235
+
20236
+ .work-presentation-player .work-slide-element[data-slide-animation-effect="fly-in"][data-slide-animation-state="playing"] {
20237
+ animation-name: work-slide-animation-fly-in;
20238
+ }
20239
+
20240
+ .work-presentation-player .work-slide-element[data-slide-animation-effect="zoom"][data-slide-animation-state="playing"] {
20241
+ animation-name: work-slide-animation-zoom;
20242
+ }
20243
+
20244
+ @keyframes work-slide-animation-appear {
20245
+ from {
20246
+ visibility: hidden;
20247
+ opacity: 0;
20248
+ }
20249
+
20250
+ to {
20251
+ visibility: visible;
20252
+ opacity: var(--work-slide-element-opacity, 1);
20253
+ }
20254
+ }
20255
+
20256
+ @keyframes work-slide-animation-fade {
20257
+ from {
20258
+ opacity: 0;
20259
+ }
20260
+
20261
+ to {
20262
+ opacity: var(--work-slide-element-opacity, 1);
20263
+ }
20264
+ }
20265
+
20266
+ @keyframes work-slide-animation-fly-in {
20267
+ from {
20268
+ opacity: 0;
20269
+ translate: var(--work-slide-animation-translate-x, -18%)
20270
+ var(--work-slide-animation-translate-y, 0);
20271
+ }
20272
+
20273
+ to {
20274
+ opacity: var(--work-slide-element-opacity, 1);
20275
+ translate: 0;
20276
+ }
20277
+ }
20278
+
20279
+ @keyframes work-slide-animation-zoom {
20280
+ from {
20281
+ opacity: 0;
20282
+ scale: .72;
20283
+ }
20284
+
20285
+ to {
20286
+ opacity: var(--work-slide-element-opacity, 1);
20287
+ scale: 1;
20288
+ }
20289
+ }
20290
+
20291
+ @media (prefers-reduced-motion: reduce) {
20292
+ .work-presentation-player .work-slide-element[data-slide-animation-state="playing"] {
20293
+ visibility: visible;
20294
+ opacity: var(--work-slide-element-opacity, 1);
20295
+ animation: none;
20296
+ translate: 0;
20297
+ scale: 1;
20298
+ }
20299
+ }
20300
+
20178
20301
  .work-presentation-comments-panel {
20179
20302
  border-bottom: 1px solid var(--a3s-line);
20180
20303
  background: var(--a3s-panel);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a3s-lab/office",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
4
4
  "description": "Open-source collaborative browser editors for documents, Markdown, spreadsheets, presentations, and PDFs.",
5
5
  "keywords": [
6
6
  "office",
@@ -114,6 +114,7 @@
114
114
  "playground:visual:spreadsheet-rich-text": "playwright test visual-tests/spreadsheet-rich-text.functional.spec.ts --config playwright.config.ts",
115
115
  "playground:visual:spreadsheet-underline": "playwright test visual-tests/spreadsheet-underline.functional.spec.ts --config playwright.config.ts",
116
116
  "playground:visual:spreadsheet-table": "playwright test visual-tests/spreadsheet-table.functional.spec.ts --config playwright.config.ts",
117
+ "playground:visual:presentation-animation": "playwright test visual-tests/presentation-animation.functional.spec.ts --config playwright.config.ts",
117
118
  "playground:visual:pdf-page-organization": "playwright test visual-tests/pdf-page-organization.functional.spec.ts --config playwright.config.ts",
118
119
  "playground:visual:update": "playwright test --config playwright.config.ts --update-snapshots",
119
120
  "performance:fixtures": "bun .a3s-test/performance/generate-fixtures.ts",
@@ -133,6 +134,8 @@
133
134
  "test:e2e:large-pdf:check": "a3s-test check tests/e2e/pdf-large-windowing.acl --json",
134
135
  "test:e2e:pdf-page-organization": "bun run test:e2e:fixtures && A3S_TEST_SUITE=tests/e2e/pdf-page-organization.acl bash scripts/run-a3s-test-web-gate.sh",
135
136
  "test:e2e:pdf-page-organization:check": "a3s-test check tests/e2e/pdf-page-organization.acl --json",
137
+ "test:e2e:presentation-animation": "A3S_TEST_SUITE=tests/e2e/presentation-animation.acl bash scripts/run-a3s-test-web-gate.sh",
138
+ "test:e2e:presentation-animation:check": "a3s-test check tests/e2e/presentation-animation.acl --json",
136
139
  "test:e2e:large-presentation": "bun .a3s-test/performance/generate-fixtures.ts --presentations-only && a3s-test run tests/e2e/presentation-large-windowing.acl --json",
137
140
  "test:e2e:large-presentation:check": "a3s-test check tests/e2e/presentation-large-windowing.acl --json",
138
141
  "test:e2e:initial-focus": "a3s-test run tests/e2e/office-editor-initial-focus.acl --json",
@@ -1,38 +0,0 @@
1
- const CARDINAL_DIRECTIONS = new Set([
2
- 'left',
3
- 'right',
4
- 'up',
5
- 'down'
6
- ]);
7
- const SPLIT_DIRECTIONS = new Set([
8
- 'in',
9
- 'out'
10
- ]);
11
- function createWorkSlideTransition(type, previous) {
12
- const common = {
13
- type,
14
- speed: previous?.speed ?? 'medium',
15
- advanceOnClick: previous?.advanceOnClick ?? true,
16
- advanceAfterMs: previous?.advanceAfterMs
17
- };
18
- if ('push' === type || 'wipe' === type) return {
19
- ...common,
20
- direction: previous?.direction && CARDINAL_DIRECTIONS.has(previous.direction) ? previous.direction : 'left'
21
- };
22
- if ('split' === type) return {
23
- ...common,
24
- direction: previous?.direction && SPLIT_DIRECTIONS.has(previous.direction) ? previous.direction : 'out',
25
- orientation: previous?.orientation ?? 'horizontal'
26
- };
27
- return common;
28
- }
29
- function slideTransitionDurationMilliseconds(transition) {
30
- if (transition?.speed === 'fast') return 500;
31
- if (transition?.speed === 'slow') return 2000;
32
- return 1000;
33
- }
34
- function workSlideTransitionsEqual(left, right) {
35
- if (!left || !right) return left === right;
36
- return left.type === right.type && left.speed === right.speed && left.direction === right.direction && left.orientation === right.orientation && left.advanceOnClick === right.advanceOnClick && left.advanceAfterMs === right.advanceAfterMs;
37
- }
38
- export { createWorkSlideTransition, slideTransitionDurationMilliseconds, workSlideTransitionsEqual };