@greese/ui 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +55 -2
- package/bin/greese-utilities.mjs +56 -0
- package/dist/_layers.scss +2 -0
- package/dist/_maps.scss +1884 -0
- package/dist/_tokens-base.scss +174 -0
- package/dist/_tokens.scss +2140 -0
- package/dist/base.css +67 -0
- package/dist/colorScheme.d.ts +16 -0
- package/dist/colorScheme.js +13 -0
- package/dist/contrast.d.ts +15 -0
- package/dist/contrast.js +43 -0
- package/dist/css/Accordion.css +9 -0
- package/dist/css/ActionSelect.css +6 -0
- package/dist/css/Alert.css +84 -0
- package/dist/css/AppBar.css +35 -0
- package/dist/css/AspectRatio.css +40 -0
- package/dist/css/AutoLayout.css +508 -0
- package/dist/css/Avatar.css +88 -0
- package/dist/css/Badge.css +66 -0
- package/dist/css/Box.css +640 -0
- package/dist/css/Breadcrumbs.css +57 -0
- package/dist/css/Button.css +275 -0
- package/dist/css/ButtonGroup.css +40 -0
- package/dist/css/Card.css +101 -0
- package/dist/css/Carousel.css +170 -0
- package/dist/css/Center.css +11 -0
- package/dist/css/ChatBubble.css +128 -0
- package/dist/css/Checkbox.css +65 -0
- package/dist/css/Chip.css +124 -0
- package/dist/css/ChoiceGroup.css +57 -0
- package/dist/css/Code.css +29 -0
- package/dist/css/Collapsible.css +126 -0
- package/dist/css/ColorPicker.css +191 -0
- package/dist/css/Combobox.css +90 -0
- package/dist/css/Container.css +30 -0
- package/dist/css/Countdown.css +25 -0
- package/dist/css/DateField.css +15 -0
- package/dist/css/Dialog.css +640 -0
- package/dist/css/Diff.css +103 -0
- package/dist/css/Divider.css +19 -0
- package/dist/css/Dock.css +91 -0
- package/dist/css/Field.css +33 -0
- package/dist/css/Fieldset.css +54 -0
- package/dist/css/FileInput.css +65 -0
- package/dist/css/Footer.css +72 -0
- package/dist/css/Grid.css +998 -0
- package/dist/css/Header.css +89 -0
- package/dist/css/Heading.css +6 -0
- package/dist/css/Icon.css +12 -0
- package/dist/css/IconButton.css +7 -0
- package/dist/css/Image.css +69 -0
- package/dist/css/Indicator.css +41 -0
- package/dist/css/Input.css +115 -0
- package/dist/css/Key.css +42 -0
- package/dist/css/Link.css +72 -0
- package/dist/css/List.css +52 -0
- package/dist/css/Media.css +8 -0
- package/dist/css/Menu.css +161 -0
- package/dist/css/Motion.css +1356 -0
- package/dist/css/NumberField.css +49 -0
- package/dist/css/OtpField.css +90 -0
- package/dist/css/PageTransition.css +979 -0
- package/dist/css/Pagination.css +43 -0
- package/dist/css/Popover.css +93 -0
- package/dist/css/Progress.css +101 -0
- package/dist/css/RadialProgress.css +44 -0
- package/dist/css/Radio.css +41 -0
- package/dist/css/Rating.css +100 -0
- package/dist/css/ScrollSpy.css +6 -0
- package/dist/css/SegmentedControl.css +101 -0
- package/dist/css/Select.css +115 -0
- package/dist/css/Sidebar.css +207 -0
- package/dist/css/Skeleton.css +48 -0
- package/dist/css/Slider.css +75 -0
- package/dist/css/Spacer.css +34 -0
- package/dist/css/SpeedDial.css +165 -0
- package/dist/css/Spinner.css +44 -0
- package/dist/css/Stack.css +528 -0
- package/dist/css/Stat.css +107 -0
- package/dist/css/Status.css +86 -0
- package/dist/css/Steps.css +201 -0
- package/dist/css/Swap.css +49 -0
- package/dist/css/Switch.css +66 -0
- package/dist/css/Table.css +269 -0
- package/dist/css/Tabs.css +119 -0
- package/dist/css/Text.css +60 -0
- package/dist/css/TextRotate.css +147 -0
- package/dist/css/Textarea.css +107 -0
- package/dist/css/ThemeProvider.css +6 -0
- package/dist/css/Tile.css +18 -0
- package/dist/css/Toast.css +110 -0
- package/dist/css/ToggleButton.css +20 -0
- package/dist/css/Tooltip.css +94 -0
- package/dist/css/Type.css +211 -0
- package/dist/css/Visibility.css +78 -0
- package/dist/css/VisuallyHidden.css +15 -0
- package/dist/css/core.css +251 -0
- package/dist/css.d.ts +29 -0
- package/dist/css.js +28 -0
- package/dist/effect-classes.d.ts +8 -0
- package/dist/effect-classes.js +5 -0
- package/dist/effects/css.d.ts +71 -0
- package/dist/effects/css.js +184 -0
- package/dist/effects/definitions.d.ts +555 -0
- package/dist/effects/definitions.js +379 -0
- package/dist/effects/frames.d.ts +18 -0
- package/dist/effects/frames.js +68 -0
- package/dist/effects/sequence.d.ts +33 -0
- package/dist/effects/sequence.js +106 -0
- package/dist/effects.d.ts +30 -0
- package/dist/effects.js +30 -0
- package/dist/greese.css +11907 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/kebab.d.ts +2 -0
- package/dist/kebab.js +2 -0
- package/dist/palette.css +249 -0
- package/dist/palette.d.ts +599 -0
- package/dist/palette.js +334 -0
- package/dist/shapes.d.ts +193 -0
- package/dist/shapes.js +852 -0
- package/dist/storage.d.ts +47 -0
- package/dist/storage.js +118 -0
- package/dist/style-manifest.json +3010 -0
- package/dist/theme.d.ts +71 -0
- package/dist/theme.js +180 -0
- package/dist/tokens.css +253 -0
- package/dist/tokens.d.ts +343 -0
- package/dist/tokens.js +179 -0
- package/dist/typography.d.ts +55 -0
- package/dist/typography.js +144 -0
- package/dist/utilities.css +6975 -0
- package/dist/utilities.d.ts +82 -0
- package/dist/utilities.js +636 -0
- package/dist/utilitiesFiles.d.ts +18 -0
- package/dist/utilitiesFiles.js +26 -0
- package/dist/vite.d.ts +40 -0
- package/dist/vite.js +43 -0
- package/package.json +131 -4
- package/src/components/Accordion/Accordion.scss +9 -0
- package/src/components/ActionSelect/ActionSelect.scss +6 -0
- package/src/components/Alert/Alert.scss +71 -0
- package/src/components/AppBar/AppBar.scss +43 -0
- package/src/components/AspectRatio/AspectRatio.scss +22 -0
- package/src/components/AutoLayout/AutoLayout.scss +90 -0
- package/src/components/Avatar/Avatar.scss +91 -0
- package/src/components/Badge/Badge.scss +52 -0
- package/src/components/Box/Box.scss +63 -0
- package/src/components/Breadcrumbs/Breadcrumbs.scss +59 -0
- package/src/components/Button/Button.scss +231 -0
- package/src/components/ButtonGroup/ButtonGroup.scss +56 -0
- package/src/components/Card/Card.scss +99 -0
- package/src/components/Carousel/Carousel.scss +161 -0
- package/src/components/Center/Center.scss +11 -0
- package/src/components/ChatBubble/ChatBubble.scss +119 -0
- package/src/components/Checkbox/Checkbox.scss +66 -0
- package/src/components/Chip/Chip.scss +119 -0
- package/src/components/ChoiceGroup/ChoiceGroup.scss +69 -0
- package/src/components/Code/Code.scss +28 -0
- package/src/components/Collapsible/Collapsible.scss +136 -0
- package/src/components/ColorPicker/ColorPicker.scss +235 -0
- package/src/components/Combobox/Combobox.scss +83 -0
- package/src/components/Container/Container.scss +28 -0
- package/src/components/Countdown/Countdown.scss +28 -0
- package/src/components/DateField/DateField.scss +19 -0
- package/src/components/Dialog/Dialog.scss +268 -0
- package/src/components/Diff/Diff.scss +125 -0
- package/src/components/Divider/Divider.scss +22 -0
- package/src/components/Dock/Dock.scss +86 -0
- package/src/components/Field/Field.scss +38 -0
- package/src/components/Fieldset/Fieldset.scss +63 -0
- package/src/components/FileInput/FileInput.scss +66 -0
- package/src/components/Footer/Footer.scss +79 -0
- package/src/components/Grid/Grid.scss +86 -0
- package/src/components/Header/Header.scss +111 -0
- package/src/components/Heading/Heading.scss +7 -0
- package/src/components/Icon/Icon.scss +12 -0
- package/src/components/IconButton/IconButton.scss +6 -0
- package/src/components/Image/Image.scss +46 -0
- package/src/components/Indicator/Indicator.scss +48 -0
- package/src/components/Input/Input.scss +58 -0
- package/src/components/Key/Key.scss +48 -0
- package/src/components/Link/Link.scss +50 -0
- package/src/components/List/List.scss +62 -0
- package/src/components/Media/Media.scss +7 -0
- package/src/components/Menu/Menu.scss +91 -0
- package/src/components/Motion/Motion.scss +106 -0
- package/src/components/NumberField/NumberField.scss +44 -0
- package/src/components/OtpField/OtpField.scss +91 -0
- package/src/components/PageTransition/PageTransition.scss +168 -0
- package/src/components/Pagination/Pagination.scss +55 -0
- package/src/components/Popover/Popover.scss +13 -0
- package/src/components/Progress/Progress.scss +126 -0
- package/src/components/RadialProgress/RadialProgress.scss +65 -0
- package/src/components/Radio/Radio.scss +36 -0
- package/src/components/Rating/Rating.scss +121 -0
- package/src/components/ScrollSpy/ScrollSpy.scss +6 -0
- package/src/components/SegmentedControl/SegmentedControl.scss +107 -0
- package/src/components/Select/Select.scss +59 -0
- package/src/components/Sidebar/Sidebar.scss +88 -0
- package/src/components/Skeleton/Skeleton.scss +60 -0
- package/src/components/Slider/Slider.scss +96 -0
- package/src/components/Spacer/Spacer.scss +15 -0
- package/src/components/SpeedDial/SpeedDial.scss +109 -0
- package/src/components/Spinner/Spinner.scss +43 -0
- package/src/components/Stack/Stack.scss +57 -0
- package/src/components/Stat/Stat.scss +96 -0
- package/src/components/Status/Status.scss +60 -0
- package/src/components/Steps/Steps.scss +216 -0
- package/src/components/Swap/Swap.scss +62 -0
- package/src/components/Switch/Switch.scss +63 -0
- package/src/components/Table/Table.scss +183 -0
- package/src/components/Tabs/Tabs.scss +108 -0
- package/src/components/Text/Text.scss +34 -0
- package/src/components/TextRotate/TextRotate.scss +51 -0
- package/src/components/Textarea/Textarea.scss +56 -0
- package/src/components/ThemeProvider/ThemeProvider.scss +7 -0
- package/src/components/Tile/Tile.scss +23 -0
- package/src/components/Toast/Toast.scss +93 -0
- package/src/components/ToggleButton/ToggleButton.scss +23 -0
- package/src/components/Tooltip/Tooltip.scss +21 -0
- package/src/components/Type/Type.scss +35 -0
- package/src/components/Visibility/Visibility.scss +39 -0
- package/src/components/VisuallyHidden/VisuallyHidden.scss +17 -0
- package/src/styles/_breakpoint-vars.scss +16 -0
- package/src/styles/_config.scss +48 -0
- package/src/styles/_controls.scss +88 -0
- package/src/styles/_core.scss +4 -0
- package/src/styles/_effect.scss +51 -0
- package/src/styles/_floating.scss +141 -0
- package/src/styles/_kit.scss +1548 -0
- package/src/styles/_manifest.scss +670 -0
- package/src/styles/_mixins.scss +261 -0
- package/src/styles/_tones.scss +63 -0
- package/src/styles/_type-emit.scss +76 -0
- package/src/styles/_typography-vars.scss +8 -0
- package/src/styles/base.scss +80 -0
- package/src/styles/breakpoints.test.ts +73 -0
- package/src/styles/entryPoints.test.ts +68 -0
- package/src/styles/index.scss +93 -0
- package/src/styles/kit.test.ts +754 -0
- package/src/styles/public/_controls.scss +3 -0
- package/src/styles/public/_floating.scss +3 -0
- package/src/styles/public/_mixins.scss +5 -0
- package/src/styles/public/_tones.scss +4 -0
- package/themes/bevel.css +1077 -0
- package/themes/bevel.scss +1166 -0
- package/themes/depth.css +123 -0
- package/themes/depth.scss +46 -0
- package/themes/extrude.css +219 -0
- package/themes/extrude.scss +255 -0
- package/themes/folio.css +986 -0
- package/themes/folio.scss +1047 -0
- package/themes/gentle.css +66 -0
- package/themes/gentle.scss +39 -0
- package/themes/outline.css +127 -0
- package/themes/outline.scss +126 -0
- package/themes/pebble.css +65 -0
- package/themes/pebble.scss +69 -0
- package/themes/pocket.css +137 -0
- package/themes/pocket.scss +45 -0
- package/themes/ridge.css +927 -0
- package/themes/ridge.scss +998 -0
- package/themes/snappy.css +82 -0
- package/themes/snappy.scss +40 -0
- package/themes/soft.css +79 -0
- package/themes/soft.scss +84 -0
- package/themes/ticket.css +960 -0
- package/themes/ticket.scss +1031 -0
- package/themes/tilt.css +148 -0
- package/themes/tilt.scss +169 -0
- package/themes/wipe.css +186 -0
- package/themes/wipe.scss +46 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/** The built-in effects' definitions: base effects, combos, and how far they reach. */
|
|
2
|
+
/** Keys every call accepts, in the order positional arguments fill them. */
|
|
3
|
+
export const timingKeys = ['duration', 'offset', 'ease'];
|
|
4
|
+
export const base = (definition) => definition;
|
|
5
|
+
export const px = (fn) => (value) => `${fn}(${typeof value === 'number' ? `${value}px` : value})`;
|
|
6
|
+
export const plain = (fn) => (value) => `${fn}(${value})`;
|
|
7
|
+
export const ZERO = '0px';
|
|
8
|
+
/**
|
|
9
|
+
* Wipes and pockets: a mask that shows everything, anchored at one side (or
|
|
10
|
+
* the middle), sized from `start` to `end`. A mask, not `clip-path`, because
|
|
11
|
+
* the clip belongs to the element's shape and a mask composes with it (ADR
|
|
12
|
+
* 0024); it exists only while the effect plays.
|
|
13
|
+
*/
|
|
14
|
+
// Colors by name, not `#000`: greese.js interpolates the numbers in a value,
|
|
15
|
+
// and would write `#000` back as `#0`, dropping the mask.
|
|
16
|
+
export const SOLID = 'linear-gradient(black, black)';
|
|
17
|
+
export const WHOLE = '100% 100%';
|
|
18
|
+
export const masked = (position, start, end) => base({
|
|
19
|
+
property: 'maskSize',
|
|
20
|
+
params: { start, end },
|
|
21
|
+
fixed: { maskImage: SOLID, maskRepeat: 'no-repeat', maskPosition: position },
|
|
22
|
+
});
|
|
23
|
+
/** The directions wipes and pockets go in (physical, like the slides): x, y. */
|
|
24
|
+
export const DIRECTIONS = {
|
|
25
|
+
Right: [1, 0],
|
|
26
|
+
Left: [-1, 0],
|
|
27
|
+
Up: [0, -1],
|
|
28
|
+
Down: [0, 1],
|
|
29
|
+
DownRight: [1, 1],
|
|
30
|
+
DownLeft: [-1, 1],
|
|
31
|
+
UpRight: [1, -1],
|
|
32
|
+
UpLeft: [-1, -1],
|
|
33
|
+
};
|
|
34
|
+
/** The directions wipes and pockets go in, by name: `Right`, `DownLeft`… */
|
|
35
|
+
export const effectDirections = Object.keys(DIRECTIONS);
|
|
36
|
+
/** `50% 0%` for a side: where a direction comes from (`-1`) or goes to (`1`). */
|
|
37
|
+
export const side = (dx, dy, toward) => `${50 + 50 * dx * toward}% ${50 + 50 * dy * toward}%`;
|
|
38
|
+
/** The mask's size with the moving axes at `amount`. */
|
|
39
|
+
export const sized = (dx, dy, amount) => `${dx ? amount : '100%'} ${dy ? amount : '100%'}`;
|
|
40
|
+
/**
|
|
41
|
+
* A wipe in `direction`: arriving, the edge sweeps that way and reveals the
|
|
42
|
+
* element from the side it leaves; leaving, it sweeps that way and covers it.
|
|
43
|
+
*/
|
|
44
|
+
export const wipes = Object.fromEntries(Object.entries(DIRECTIONS).flatMap(([name, [dx, dy]]) => [
|
|
45
|
+
[`wipe${name}`, masked(side(dx, dy, -1), sized(dx, dy, '0%'), WHOLE)],
|
|
46
|
+
[`wipeOut${name}`, masked(side(dx, dy, 1), WHOLE, sized(dx, dy, '0%'))],
|
|
47
|
+
]));
|
|
48
|
+
/**
|
|
49
|
+
* An iris: an opening the element's shape (an ellipse in its proportions)
|
|
50
|
+
* that grows from, or shrinks to, a point: the middle, a side or a corner. The
|
|
51
|
+
* mask is anchored at that point, so it stays put while the opening changes
|
|
52
|
+
* size. `IRIS_OPEN` is where it just covers the farthest corner from any of
|
|
53
|
+
* those points, so the whole run is visible change, in and out alike.
|
|
54
|
+
*/
|
|
55
|
+
export const iris = (at, start, end) => base({
|
|
56
|
+
property: 'maskSize',
|
|
57
|
+
params: { start, end },
|
|
58
|
+
fixed: {
|
|
59
|
+
maskImage: `radial-gradient(${at === '50% 50%' ? 'closest-side' : `farthest-side at ${at}`}, black 98%, transparent)`,
|
|
60
|
+
maskRepeat: 'no-repeat',
|
|
61
|
+
maskPosition: at,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
// √2: an ellipse in the element's proportions reaches its far corner at 141.4%.
|
|
65
|
+
export const OPEN = '142% 142%';
|
|
66
|
+
export const SHUT = '0% 0%';
|
|
67
|
+
/**
|
|
68
|
+
* Leaving, the opening closes toward the side it goes to; arriving, it plays
|
|
69
|
+
* that backwards, opening from the same side, so an entrance is its exit
|
|
70
|
+
* reversed.
|
|
71
|
+
*/
|
|
72
|
+
export const irises = Object.fromEntries(Object.entries(DIRECTIONS).flatMap(([name, [dx, dy]]) => [
|
|
73
|
+
[`iris${name}`, iris(side(dx, dy, 1), SHUT, OPEN)],
|
|
74
|
+
[`irisOut${name}`, iris(side(dx, dy, 1), OPEN, SHUT)],
|
|
75
|
+
]));
|
|
76
|
+
/**
|
|
77
|
+
* Three-dimensional turns and depth: `transform`, with its own perspective, so
|
|
78
|
+
* they foreshorten wherever they play, with no `perspective` on a parent. Not
|
|
79
|
+
* the individual `translate`/`rotate`/`scale` the other effects animate, so
|
|
80
|
+
* they compose with those, and never with a shape's clip-path (applied before
|
|
81
|
+
* the transform, it turns with the element).
|
|
82
|
+
*/
|
|
83
|
+
export const PERSPECTIVE = 'perspective(800px)';
|
|
84
|
+
export const turn = (axis, start, end, origin) => base({
|
|
85
|
+
property: 'transform',
|
|
86
|
+
params: { start, end },
|
|
87
|
+
format: (v) => `${PERSPECTIVE} rotate${axis}(${v}deg)`,
|
|
88
|
+
...(origin && { fixed: { transformOrigin: origin } }),
|
|
89
|
+
});
|
|
90
|
+
/** The flips' directions: the axis each turns about, and which way. */
|
|
91
|
+
export const FLIPS = {
|
|
92
|
+
Up: ['X', 1],
|
|
93
|
+
Down: ['X', -1],
|
|
94
|
+
Left: ['Y', 1],
|
|
95
|
+
Right: ['Y', -1],
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* A flip over: a panel turned half a turn about its middle, its two faces
|
|
99
|
+
* the old and the new: arriving, it turns from its back (180 degrees) to its
|
|
100
|
+
* front; leaving, from its front to its back, the same way, so a pair played
|
|
101
|
+
* together turns as one panel. The back face is hidden, so each shows only
|
|
102
|
+
* while it faces the viewer, and the switch is at the quarter turn. A longer
|
|
103
|
+
* perspective than a flip's, so a panel as wide as the screen doesn't swing
|
|
104
|
+
* through the viewer at the quarter turn.
|
|
105
|
+
*/
|
|
106
|
+
export const FLIP_OVER_PERSPECTIVE = 'perspective(2000px)';
|
|
107
|
+
const flipOver = (axis, start, end) => base({
|
|
108
|
+
property: 'transform',
|
|
109
|
+
params: { start, end },
|
|
110
|
+
format: (v) => `${FLIP_OVER_PERSPECTIVE} rotate${axis}(${v}deg)`,
|
|
111
|
+
fixed: { backfaceVisibility: 'hidden' },
|
|
112
|
+
});
|
|
113
|
+
export const flipOvers = Object.fromEntries(Object.entries(FLIPS).flatMap(([name, [axis, sign]]) => [
|
|
114
|
+
[`flipOver${name}`, flipOver(axis, sign * 180, 0)],
|
|
115
|
+
[`flipOverOut${name}`, flipOver(axis, 0, sign * 180)],
|
|
116
|
+
]));
|
|
117
|
+
export const baseEffects = {
|
|
118
|
+
fadeIn: base({ property: 'opacity', params: { start: 0, end: 1 } }),
|
|
119
|
+
fadeOut: base({ property: 'opacity', params: { start: 1, end: 0 } }),
|
|
120
|
+
scaleIn: base({ property: 'scale', params: { start: 0, end: 1 } }),
|
|
121
|
+
scaleOut: base({ property: 'scale', params: { start: 1, end: 0 } }),
|
|
122
|
+
blurIn: base({ property: 'filter', params: { start: 8, end: 0 }, format: px('blur') }),
|
|
123
|
+
blurOut: base({ property: 'filter', params: { start: 0, end: 8 }, format: px('blur') }),
|
|
124
|
+
slideUp: base({ property: 'y', params: { start: '100%', end: '0%' } }),
|
|
125
|
+
slideDown: base({ property: 'y', params: { start: '-100%', end: '0%' } }),
|
|
126
|
+
// Right and left are physical: the direction the element moves.
|
|
127
|
+
slideRight: base({ property: 'x', params: { start: '-100%', end: '0%' } }),
|
|
128
|
+
slideLeft: base({ property: 'x', params: { start: '100%', end: '0%' } }),
|
|
129
|
+
// A springy scale: past `end` by `peak` at `peakAt` (a share of the effect).
|
|
130
|
+
pop: base({
|
|
131
|
+
property: 'scale',
|
|
132
|
+
params: { start: 1, end: 1, peak: 1.075, peakAt: 0.6 },
|
|
133
|
+
stops: (p) => [
|
|
134
|
+
[0, p.start],
|
|
135
|
+
[Number(p.peakAt), p.peak],
|
|
136
|
+
[1, p.end],
|
|
137
|
+
],
|
|
138
|
+
}),
|
|
139
|
+
squish: base({ property: 'scale', params: { start: 1, end: 0.96 } }),
|
|
140
|
+
tilt: base({ property: 'rotate', params: { start: 0, end: -2 } }),
|
|
141
|
+
glow: base({
|
|
142
|
+
property: 'filter',
|
|
143
|
+
params: { start: 1, end: 1.12 },
|
|
144
|
+
format: plain('brightness'),
|
|
145
|
+
}),
|
|
146
|
+
// Wipes, every direction (`wipeRight`, `wipeOutUp`, `wipeDownLeft`…),
|
|
147
|
+
// then from and to the middle.
|
|
148
|
+
...wipes,
|
|
149
|
+
wipeOpenX: masked('50% 50%', '0% 100%', WHOLE),
|
|
150
|
+
wipeOpenY: masked('50% 50%', '100% 0%', WHOLE),
|
|
151
|
+
wipeCloseX: masked('50% 50%', WHOLE, '0% 100%'),
|
|
152
|
+
wipeCloseY: masked('50% 50%', WHOLE, '100% 0%'),
|
|
153
|
+
// Irises, from and to the middle, then every direction (`irisRight`, `irisOutUpLeft`…).
|
|
154
|
+
irisIn: iris('50% 50%', SHUT, OPEN),
|
|
155
|
+
irisOut: iris('50% 50%', OPEN, SHUT),
|
|
156
|
+
...irises,
|
|
157
|
+
// In three dimensions: a turn about the horizontal (`turnX`) or vertical
|
|
158
|
+
// (`turnY`) axis through the middle, and a move along the line of sight
|
|
159
|
+
// (`depth`, in px: negative is away).
|
|
160
|
+
turnX: turn('X', 0, 0),
|
|
161
|
+
turnY: turn('Y', 0, 0),
|
|
162
|
+
depth: base({
|
|
163
|
+
property: 'transform',
|
|
164
|
+
params: { start: 0, end: 0 },
|
|
165
|
+
format: (v) => `${PERSPECTIVE} translateZ(${v}px)`,
|
|
166
|
+
}),
|
|
167
|
+
// A hinge along the top edge, like a sign swinging down into place.
|
|
168
|
+
hinge: turn('X', 0, 0, '50% 0%'),
|
|
169
|
+
// Half turns, a panel's two faces (`flipOverRight`, `flipOverOutLeft`…).
|
|
170
|
+
...flipOvers,
|
|
171
|
+
};
|
|
172
|
+
/** `effect` must be a base effect's name: a typo, or a combo, fails to compile. */
|
|
173
|
+
export const part = (effect, args = {}, at = [0, 1]) => ({ effect, args, at });
|
|
174
|
+
export const combo = (definition) => definition;
|
|
175
|
+
export const away = ({ distance }) => ({ distance });
|
|
176
|
+
/**
|
|
177
|
+
* A pocket: the element moves out of (or into) an invisible slot at one of
|
|
178
|
+
* its own edges. It slides `distance` (its whole size, by default, so it
|
|
179
|
+
* starts or ends out of sight) while a mask anchored where it's going grows or
|
|
180
|
+
* shrinks by the same amount, so the window it shows through stays where the
|
|
181
|
+
* element rests. Arriving, it moves `direction`; leaving, it moves on that
|
|
182
|
+
* way, into the slot on that side.
|
|
183
|
+
*/
|
|
184
|
+
export const pocket = (direction, leaving) => {
|
|
185
|
+
const [dx, dy] = DIRECTIONS[direction];
|
|
186
|
+
return {
|
|
187
|
+
params: () => ({ distance: '100%' }),
|
|
188
|
+
parts: ({ distance }) => {
|
|
189
|
+
const far = (sign) => sign > 0 ? String(distance) : negate(String(distance));
|
|
190
|
+
const slide = (axis, sign) => {
|
|
191
|
+
const travel = {
|
|
192
|
+
start: leaving ? ZERO : far(-sign),
|
|
193
|
+
end: leaving ? far(sign) : ZERO,
|
|
194
|
+
};
|
|
195
|
+
return axis === 'x' ? part('slideRight', travel) : part('slideDown', travel);
|
|
196
|
+
};
|
|
197
|
+
// A share of its size (the default, 100%) is plain percentages, which
|
|
198
|
+
// every engine transitions; any other length is calc() at both ends,
|
|
199
|
+
// the same shape, so greese.js interpolates them.
|
|
200
|
+
const share = /^(-?[\d.]+)%$/.exec(String(distance).trim());
|
|
201
|
+
const short = sized(dx, dy, share ? `${100 - Number(share[1])}%` : `calc(100% - ${distance})`);
|
|
202
|
+
const full = sized(dx, dy, share ? '100%' : 'calc(100% - 0px)');
|
|
203
|
+
return [
|
|
204
|
+
...(dx ? [slide('x', dx)] : []),
|
|
205
|
+
...(dy ? [slide('y', dy)] : []),
|
|
206
|
+
// Arriving, the window is anchored at the side it moves toward; leaving, the side it leaves.
|
|
207
|
+
leaving
|
|
208
|
+
? part(`wipe${direction}`, { start: full, end: short })
|
|
209
|
+
: part(`wipeOut${direction}`, { start: short, end: full }),
|
|
210
|
+
];
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
export const pockets = Object.fromEntries(Object.keys(DIRECTIONS).flatMap((name) => [
|
|
215
|
+
[`pocket${name}`, pocket(name, false)],
|
|
216
|
+
[`pocketOut${name}`, pocket(name, true)],
|
|
217
|
+
]));
|
|
218
|
+
/**
|
|
219
|
+
* A flip: the element turns into place about its middle, from `angle` degrees
|
|
220
|
+
* (75 by default) toward the direction it arrives in: `flipUp` rises, its
|
|
221
|
+
* bottom edge leading; `flipRight` swings in from the left. Leaving, it turns
|
|
222
|
+
* back out the same way.
|
|
223
|
+
*/
|
|
224
|
+
export const flips = Object.fromEntries(Object.entries(FLIPS).flatMap(([name, [axis, sign]]) => {
|
|
225
|
+
const effect = `turn${axis}`;
|
|
226
|
+
return [
|
|
227
|
+
[
|
|
228
|
+
`flip${name}`,
|
|
229
|
+
combo({
|
|
230
|
+
params: () => ({ angle: 75 }),
|
|
231
|
+
parts: (p) => [
|
|
232
|
+
part('fadeIn', {}, [0, 0.5]),
|
|
233
|
+
part(effect, { start: sign * Number(p.angle), end: 0 }),
|
|
234
|
+
],
|
|
235
|
+
}),
|
|
236
|
+
],
|
|
237
|
+
[
|
|
238
|
+
`flipOut${name}`,
|
|
239
|
+
combo({
|
|
240
|
+
params: () => ({ angle: 75 }),
|
|
241
|
+
parts: (p) => [
|
|
242
|
+
part('fadeOut', {}, [0.5, 1]),
|
|
243
|
+
part(effect, { start: 0, end: sign * Number(p.angle) }),
|
|
244
|
+
],
|
|
245
|
+
}),
|
|
246
|
+
],
|
|
247
|
+
];
|
|
248
|
+
}));
|
|
249
|
+
export const comboEffects = {
|
|
250
|
+
fadeUp: combo({
|
|
251
|
+
params: away,
|
|
252
|
+
parts: (p) => [part('fadeIn'), part('slideUp', { start: p.distance, end: ZERO })],
|
|
253
|
+
}),
|
|
254
|
+
fadeDown: combo({
|
|
255
|
+
params: away,
|
|
256
|
+
parts: (p, r) => [
|
|
257
|
+
part('fadeIn'),
|
|
258
|
+
part('slideDown', { start: r.negate(String(p.distance)), end: ZERO }),
|
|
259
|
+
],
|
|
260
|
+
}),
|
|
261
|
+
fadeStart: combo({
|
|
262
|
+
params: away,
|
|
263
|
+
parts: (p, r) => [
|
|
264
|
+
part('fadeIn'),
|
|
265
|
+
part('slideRight', { start: r.start(String(p.distance)), end: ZERO }),
|
|
266
|
+
],
|
|
267
|
+
}),
|
|
268
|
+
fadeEnd: combo({
|
|
269
|
+
params: away,
|
|
270
|
+
parts: (p, r) => [
|
|
271
|
+
part('fadeIn'),
|
|
272
|
+
part('slideLeft', { start: r.end(String(p.distance)), end: ZERO }),
|
|
273
|
+
],
|
|
274
|
+
}),
|
|
275
|
+
fadeOutUp: combo({
|
|
276
|
+
params: away,
|
|
277
|
+
parts: (p, r) => [
|
|
278
|
+
part('fadeOut'),
|
|
279
|
+
part('slideUp', { start: ZERO, end: r.negate(String(p.distance)) }),
|
|
280
|
+
],
|
|
281
|
+
}),
|
|
282
|
+
fadeOutDown: combo({
|
|
283
|
+
params: away,
|
|
284
|
+
parts: (p) => [part('fadeOut'), part('slideDown', { start: ZERO, end: p.distance })],
|
|
285
|
+
}),
|
|
286
|
+
popIn: combo({
|
|
287
|
+
params: () => ({ start: 0.8, peak: 1.04, peakAt: 0.6 }),
|
|
288
|
+
parts: (p) => [
|
|
289
|
+
part('pop', { start: p.start, peak: p.peak, peakAt: p.peakAt }),
|
|
290
|
+
part('fadeIn', {}, [0, Number(p.peakAt)]),
|
|
291
|
+
],
|
|
292
|
+
}),
|
|
293
|
+
popOut: combo({
|
|
294
|
+
params: () => ({ end: 0.8, peak: 1.04, peakAt: 0.4 }),
|
|
295
|
+
parts: (p) => [
|
|
296
|
+
part('pop', { start: 1, end: p.end, peak: p.peak, peakAt: p.peakAt }),
|
|
297
|
+
part('fadeOut', {}, [Number(p.peakAt), 1]),
|
|
298
|
+
],
|
|
299
|
+
}),
|
|
300
|
+
rise: combo({
|
|
301
|
+
params: () => ({ lift: -4, scale: 1.02 }),
|
|
302
|
+
parts: (p) => [
|
|
303
|
+
part('slideUp', { start: 0, end: p.lift }),
|
|
304
|
+
part('scaleIn', { start: 1, end: p.scale }),
|
|
305
|
+
],
|
|
306
|
+
}),
|
|
307
|
+
fall: combo({
|
|
308
|
+
params: () => ({ drop: 4, scale: 0.98 }),
|
|
309
|
+
parts: (p) => [
|
|
310
|
+
part('slideDown', { start: 0, end: p.drop }),
|
|
311
|
+
part('scaleOut', { start: 1, end: p.scale }),
|
|
312
|
+
],
|
|
313
|
+
}),
|
|
314
|
+
// Pockets, every direction, arriving and leaving.
|
|
315
|
+
...pockets,
|
|
316
|
+
// Flips, depth and swings: three-dimensional, each leaving as its arrival
|
|
317
|
+
// reversed (`flipOutUp` turns back down the way `flipUp` came).
|
|
318
|
+
...flips,
|
|
319
|
+
depthIn: combo({
|
|
320
|
+
params: () => ({ depth: -240 }),
|
|
321
|
+
parts: (p) => [
|
|
322
|
+
part('fadeIn', {}, [0, 0.6]),
|
|
323
|
+
part('depth', { start: p.depth, end: 0 }),
|
|
324
|
+
],
|
|
325
|
+
}),
|
|
326
|
+
depthOut: combo({
|
|
327
|
+
params: () => ({ depth: -240 }),
|
|
328
|
+
parts: (p) => [
|
|
329
|
+
part('fadeOut', {}, [0.4, 1]),
|
|
330
|
+
part('depth', { start: 0, end: p.depth }),
|
|
331
|
+
],
|
|
332
|
+
}),
|
|
333
|
+
swingIn: combo({
|
|
334
|
+
params: () => ({ angle: -80 }),
|
|
335
|
+
parts: (p) => [
|
|
336
|
+
part('fadeIn', {}, [0, 0.5]),
|
|
337
|
+
part('hinge', { start: p.angle, end: 0 }),
|
|
338
|
+
],
|
|
339
|
+
}),
|
|
340
|
+
swingOut: combo({
|
|
341
|
+
params: () => ({ angle: -80 }),
|
|
342
|
+
parts: (p) => [
|
|
343
|
+
part('fadeOut', {}, [0.5, 1]),
|
|
344
|
+
part('hinge', { start: 0, end: p.angle }),
|
|
345
|
+
],
|
|
346
|
+
}),
|
|
347
|
+
};
|
|
348
|
+
/** Base effects and combos together: every effect the library ships. */
|
|
349
|
+
export const builtInEffects = { ...baseEffects, ...comboEffects };
|
|
350
|
+
/** Every built-in effect name, base effects first. */
|
|
351
|
+
export const effectNames = Object.keys(builtInEffects);
|
|
352
|
+
/** Own keys only, so `constructor` is no effect. */
|
|
353
|
+
export const isBuiltInEffect = (name) => Object.hasOwn(builtInEffects, name);
|
|
354
|
+
export const isBase = (name) => Object.hasOwn(baseEffects, name);
|
|
355
|
+
export const PLAIN = /^-?[\d.]+[a-z%]*$/i;
|
|
356
|
+
/**
|
|
357
|
+
* A length the other way. Plain ones (`1rem`, `-12px`) flip textually,
|
|
358
|
+
* anything else (`calc(…)`, `var(…)`) through `calc()`.
|
|
359
|
+
*/
|
|
360
|
+
export const negate = (length) => PLAIN.test(length)
|
|
361
|
+
? length.startsWith('-')
|
|
362
|
+
? length.slice(1)
|
|
363
|
+
: `-${length}`
|
|
364
|
+
: `calc(-1 * ${length})`;
|
|
365
|
+
/** The reach for JavaScript: lengths, flipped when the element is right to left. */
|
|
366
|
+
export const jsReach = (distance, rtl) => ({
|
|
367
|
+
distance,
|
|
368
|
+
negate,
|
|
369
|
+
start: (length) => (rtl ? length : negate(length)),
|
|
370
|
+
end: (length) => (rtl ? negate(length) : length),
|
|
371
|
+
});
|
|
372
|
+
export const CSS_DIR = 'var(--greese-motion-dir, 1)';
|
|
373
|
+
/** The reach for CSS: `--greese-motion-dir` is 1, or -1 in right-to-left (Motion.scss). */
|
|
374
|
+
export const cssReach = {
|
|
375
|
+
distance: 'var(--greese-motion-distance, 1rem)',
|
|
376
|
+
negate,
|
|
377
|
+
start: (length) => `calc(-1 * ${CSS_DIR} * ${length})`,
|
|
378
|
+
end: (length) => `calc(${CSS_DIR} * ${length})`,
|
|
379
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** An effect call as greese.js keyframes: its parts' tracks, resolved for a reach. */
|
|
2
|
+
import { type BuiltInEffect, type EffectFrames, type Reach, type Stop, type Value } from './definitions.ts';
|
|
3
|
+
export type Track = {
|
|
4
|
+
property: string;
|
|
5
|
+
stops: Stop[];
|
|
6
|
+
};
|
|
7
|
+
export declare const round: (offset: number) => number;
|
|
8
|
+
export declare const tracksOf: (name: BuiltInEffect, args: Record<string, Value | undefined>, reach: Reach) => Track[];
|
|
9
|
+
/** Properties with evenly spaced stops are written as `{ property: [values] }`, else as a list of frames. */
|
|
10
|
+
export declare const framesOf: (tracks: Track[]) => EffectFrames;
|
|
11
|
+
/**
|
|
12
|
+
* An effect's keyframes for a reach and arguments (parameter names to values).
|
|
13
|
+
* Defaults to a left-to-right `1rem` and every parameter's default, which is
|
|
14
|
+
* what tools that show or edit the effects want.
|
|
15
|
+
*/
|
|
16
|
+
export declare const effectFrames: (name: BuiltInEffect, reach?: Reach, args?: Record<string, Value | undefined>) => EffectFrames;
|
|
17
|
+
/** The parameters an effect has, then the timing keys: the order positional arguments fill. */
|
|
18
|
+
export declare const effectParamNames: (name: string) => string[];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** An effect call as greese.js keyframes: its parts' tracks, resolved for a reach. */
|
|
2
|
+
import { baseEffects, builtInEffects, comboEffects, cssReach, isBase, isBuiltInEffect, jsReach, timingKeys, } from "./definitions.js";
|
|
3
|
+
export const round = (offset) => Math.round(offset * 10_000) / 10_000;
|
|
4
|
+
export const tracksOf = (name, args, reach) => {
|
|
5
|
+
const definition = builtInEffects[name];
|
|
6
|
+
const defaults = isBase(name)
|
|
7
|
+
? baseEffects[name].params
|
|
8
|
+
: definition.params(reach);
|
|
9
|
+
const params = { ...defaults };
|
|
10
|
+
for (const key of Object.keys(defaults))
|
|
11
|
+
if (args[key] !== undefined)
|
|
12
|
+
params[key] = args[key];
|
|
13
|
+
if (isBase(name)) {
|
|
14
|
+
const { stops, format, property, fixed } = baseEffects[name];
|
|
15
|
+
const at = stops
|
|
16
|
+
? stops(params)
|
|
17
|
+
: [
|
|
18
|
+
[0, params.start],
|
|
19
|
+
[1, params.end],
|
|
20
|
+
];
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
property,
|
|
24
|
+
stops: at.map(([offset, value]) => [offset, format ? format(value) : value]),
|
|
25
|
+
},
|
|
26
|
+
...Object.entries(fixed ?? {}).map(([prop, value]) => ({
|
|
27
|
+
property: prop,
|
|
28
|
+
stops: [
|
|
29
|
+
[0, value],
|
|
30
|
+
[1, value],
|
|
31
|
+
],
|
|
32
|
+
})),
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
return definition.parts(params, reach).flatMap((p) => tracksOf(p.effect, p.args, reach).map(({ property, stops }) => ({
|
|
36
|
+
property,
|
|
37
|
+
stops: stops.map(([offset, value]) => [round(p.at[0] + offset * (p.at[1] - p.at[0])), value]),
|
|
38
|
+
})));
|
|
39
|
+
};
|
|
40
|
+
/** Properties with evenly spaced stops are written as `{ property: [values] }`, else as a list of frames. */
|
|
41
|
+
export const framesOf = (tracks) => {
|
|
42
|
+
const even = tracks.every(({ stops }) => stops.length > 1 &&
|
|
43
|
+
stops.every(([offset], i) => round(offset) === round(i / (stops.length - 1))));
|
|
44
|
+
if (even)
|
|
45
|
+
return Object.fromEntries(tracks.map(({ property, stops }) => [property, stops.map(([, value]) => value)]));
|
|
46
|
+
const at = new Map();
|
|
47
|
+
for (const { property, stops } of tracks)
|
|
48
|
+
for (const [offset, value] of stops) {
|
|
49
|
+
const key = round(offset);
|
|
50
|
+
at.set(key, { ...at.get(key), [property]: value });
|
|
51
|
+
}
|
|
52
|
+
return [...at]
|
|
53
|
+
.sort(([a], [b]) => a - b)
|
|
54
|
+
.map(([offset, values]) => ({ ...values, offset }));
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* An effect's keyframes for a reach and arguments (parameter names to values).
|
|
58
|
+
* Defaults to a left-to-right `1rem` and every parameter's default, which is
|
|
59
|
+
* what tools that show or edit the effects want.
|
|
60
|
+
*/
|
|
61
|
+
export const effectFrames = (name, reach = jsReach('1rem', false), args = {}) => framesOf(tracksOf(name, args, reach));
|
|
62
|
+
/** The parameters an effect has, then the timing keys: the order positional arguments fill. */
|
|
63
|
+
export const effectParamNames = (name) => isBuiltInEffect(name)
|
|
64
|
+
? [
|
|
65
|
+
...Object.keys(isBase(name) ? baseEffects[name].params : comboEffects[name].params(cssReach)),
|
|
66
|
+
...timingKeys,
|
|
67
|
+
]
|
|
68
|
+
: [...timingKeys];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Sequence strings (`'fadeIn+scaleIn,+1,pop'`): steps, calls and their arguments. */
|
|
2
|
+
import { type Timing, type Value } from './definitions.ts';
|
|
3
|
+
/** One effect in a sequence, with the arguments written for it. */
|
|
4
|
+
export interface EffectCall {
|
|
5
|
+
name: string;
|
|
6
|
+
args: Record<string, Value>;
|
|
7
|
+
}
|
|
8
|
+
/** A step of a sequence: effects that play together, or a pause in seconds. */
|
|
9
|
+
export type EffectStep = EffectCall[] | number;
|
|
10
|
+
/** Splits at `separator` outside brackets, braces, parentheses and quotes. */
|
|
11
|
+
export declare const splitTop: (text: string, separator: string) => string[];
|
|
12
|
+
export declare const NUMBER: RegExp;
|
|
13
|
+
export declare const valueOf: (text: string) => Value;
|
|
14
|
+
export declare const NAMED: RegExp;
|
|
15
|
+
/**
|
|
16
|
+
* `0.25, 0.8, { duration: 1 }`: positional values fill the effect's parameters
|
|
17
|
+
* in order, `key: value` (or `key=value`) sets one by name, and an object sets
|
|
18
|
+
* several. Only what is written is set.
|
|
19
|
+
*/
|
|
20
|
+
export declare const parseArgs: (name: string, text: string) => Record<string, Value>;
|
|
21
|
+
export declare const CALL: RegExp;
|
|
22
|
+
/**
|
|
23
|
+
* Parses `'fadeIn(0.25)+scaleIn,+2,fadeOut({ duration: 1 })'`: commas separate
|
|
24
|
+
* steps that play in turn, `+` joins effects that play together, a number is a
|
|
25
|
+
* pause, and a negative one (`-0.2`) backs the next step up, so it overlaps
|
|
26
|
+
* the last. An effect may be followed by arguments in parentheses.
|
|
27
|
+
*/
|
|
28
|
+
export declare const parseSequence: (sequence: string) => EffectStep[];
|
|
29
|
+
/** A call's timing keys, apart from the effect's own parameters. */
|
|
30
|
+
export declare const splitTiming: (args: Record<string, Value>) => {
|
|
31
|
+
timing: Timing;
|
|
32
|
+
params: Record<string, Value>;
|
|
33
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/** Sequence strings (`'fadeIn+scaleIn,+1,pop'`): steps, calls and their arguments. */
|
|
2
|
+
import {} from "./definitions.js";
|
|
3
|
+
import { effectParamNames } from "./frames.js";
|
|
4
|
+
/** Splits at `separator` outside brackets, braces, parentheses and quotes. */
|
|
5
|
+
export const splitTop = (text, separator) => {
|
|
6
|
+
const parts = [];
|
|
7
|
+
let depth = 0;
|
|
8
|
+
let quote = '';
|
|
9
|
+
let start = 0;
|
|
10
|
+
for (let i = 0; i < text.length; i++) {
|
|
11
|
+
const c = text[i];
|
|
12
|
+
if (quote) {
|
|
13
|
+
if (c === quote)
|
|
14
|
+
quote = '';
|
|
15
|
+
}
|
|
16
|
+
else if (c === '"' || c === "'")
|
|
17
|
+
quote = c;
|
|
18
|
+
else if ('([{'.includes(c))
|
|
19
|
+
depth++;
|
|
20
|
+
else if (')]}'.includes(c))
|
|
21
|
+
depth--;
|
|
22
|
+
else if (c === separator && depth === 0) {
|
|
23
|
+
parts.push(text.slice(start, i));
|
|
24
|
+
start = i + 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
parts.push(text.slice(start));
|
|
28
|
+
return parts;
|
|
29
|
+
};
|
|
30
|
+
export const NUMBER = /^[+-]?(\d+\.?\d*|\.\d+)$/;
|
|
31
|
+
export const valueOf = (text) => {
|
|
32
|
+
const value = text.trim().replace(/^(['"])(.*)\1$/, '$2');
|
|
33
|
+
return NUMBER.test(value) ? Number(value) : value;
|
|
34
|
+
};
|
|
35
|
+
export const NAMED = /^([A-Za-z_$][\w$]*)\s*[:=]\s*([\s\S]+)$/;
|
|
36
|
+
/**
|
|
37
|
+
* `0.25, 0.8, { duration: 1 }`: positional values fill the effect's parameters
|
|
38
|
+
* in order, `key: value` (or `key=value`) sets one by name, and an object sets
|
|
39
|
+
* several. Only what is written is set.
|
|
40
|
+
*/
|
|
41
|
+
export const parseArgs = (name, text) => {
|
|
42
|
+
const names = effectParamNames(name);
|
|
43
|
+
const args = {};
|
|
44
|
+
let next = 0;
|
|
45
|
+
const set = (key, value) => {
|
|
46
|
+
args[key] = valueOf(value);
|
|
47
|
+
};
|
|
48
|
+
for (const raw of splitTop(text, ',')) {
|
|
49
|
+
const arg = raw.trim();
|
|
50
|
+
if (!arg)
|
|
51
|
+
continue;
|
|
52
|
+
if (arg.startsWith('{') && arg.endsWith('}')) {
|
|
53
|
+
for (const entry of splitTop(arg.slice(1, -1), ',')) {
|
|
54
|
+
const named = NAMED.exec(entry.trim());
|
|
55
|
+
if (named)
|
|
56
|
+
set(named[1], named[2]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const named = NAMED.exec(arg);
|
|
61
|
+
if (named)
|
|
62
|
+
set(named[1], named[2]);
|
|
63
|
+
else if (names[next])
|
|
64
|
+
set(names[next++], arg);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return args;
|
|
68
|
+
};
|
|
69
|
+
export const CALL = /^([A-Za-z_$][\w$]*)\s*(?:\(([\s\S]*)\))?$/;
|
|
70
|
+
/**
|
|
71
|
+
* Parses `'fadeIn(0.25)+scaleIn,+2,fadeOut({ duration: 1 })'`: commas separate
|
|
72
|
+
* steps that play in turn, `+` joins effects that play together, a number is a
|
|
73
|
+
* pause, and a negative one (`-0.2`) backs the next step up, so it overlaps
|
|
74
|
+
* the last. An effect may be followed by arguments in parentheses.
|
|
75
|
+
*/
|
|
76
|
+
export const parseSequence = (sequence) => {
|
|
77
|
+
const steps = [];
|
|
78
|
+
for (const raw of splitTop(sequence, ',')) {
|
|
79
|
+
const step = raw.trim();
|
|
80
|
+
if (NUMBER.test(step))
|
|
81
|
+
steps.push(Number(step));
|
|
82
|
+
else if (step)
|
|
83
|
+
steps.push(splitTop(step, '+')
|
|
84
|
+
.map((call) => call.trim())
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
.map((call) => {
|
|
87
|
+
const match = CALL.exec(call);
|
|
88
|
+
return match
|
|
89
|
+
? { name: match[1], args: parseArgs(match[1], match[2] ?? '') }
|
|
90
|
+
: { name: call, args: {} };
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
return steps;
|
|
94
|
+
};
|
|
95
|
+
/** A call's timing keys, apart from the effect's own parameters. */
|
|
96
|
+
export const splitTiming = (args) => {
|
|
97
|
+
const { duration, offset, ease, ...params } = args;
|
|
98
|
+
const timing = {};
|
|
99
|
+
if (duration !== undefined && Number.isFinite(Number(duration)))
|
|
100
|
+
timing.duration = Number(duration);
|
|
101
|
+
if (offset !== undefined && Number.isFinite(Number(offset)))
|
|
102
|
+
timing.offset = Number(offset);
|
|
103
|
+
if (ease !== undefined)
|
|
104
|
+
timing.ease = String(ease);
|
|
105
|
+
return { timing, params };
|
|
106
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The built-in effects of the `greese` prop: the one place they are defined.
|
|
3
|
+
*
|
|
4
|
+
* - **Base effects** animate one property each (opacity, scale, a translation,
|
|
5
|
+
* a filter…) and take optional parameters: `start`, `end` and whatever else
|
|
6
|
+
* they need, each with a default. `fadeIn` fades from 0 to 1; `fadeIn(0.25,
|
|
7
|
+
* 0.8)` or `fadeIn({ end: 0.8 })` changes that.
|
|
8
|
+
* - **Combo effects** are made of base effects, by name (typed against
|
|
9
|
+
* `baseEffects`), each placed in time as a share of the combo: `popIn` is
|
|
10
|
+
* `pop` plus a `fadeIn` over its first 60%.
|
|
11
|
+
*
|
|
12
|
+
* Every call also takes the timing keys `duration` and `offset` (seconds) and
|
|
13
|
+
* `ease`, after the effect's own parameters when given by position:
|
|
14
|
+
* `someEffect(start, end, duration, offset, ease)`.
|
|
15
|
+
*
|
|
16
|
+
* Nothing else lists the effects. The runtime plays what this file says; the
|
|
17
|
+
* build writes every effect that ends at the element's resting state as CSS
|
|
18
|
+
* (`@keyframes greese-<name>`) and every one that ends away from it as an
|
|
19
|
+
* overlay exit (`dist/_maps.scss`); the name types and Theme Studio's lists are
|
|
20
|
+
* derived; an effect Studio defines is turned into the same CSS by the same
|
|
21
|
+
* `cssPlan()`. To add an effect, add it here.
|
|
22
|
+
*
|
|
23
|
+
* Its modules (effects/) import nothing but each other and `kebab.ts` (which
|
|
24
|
+
* imports nothing, so the runtime chunk carries no tokens): the build script
|
|
25
|
+
* runs them directly under Node's type stripping.
|
|
26
|
+
*/
|
|
27
|
+
export { baseEffects, builtInEffects, comboEffects, cssReach, effectDirections, effectNames, isBuiltInEffect, jsReach, negate, timingKeys, type BaseArgs, type BaseEffect, type BuiltInEffect, type ComboEffect, type EffectFrames, type Reach, type Timing, } from './effects/definitions.ts';
|
|
28
|
+
export { effectFrames, effectParamNames } from './effects/frames.ts';
|
|
29
|
+
export { parseSequence, splitTiming, type EffectCall, type EffectStep, } from './effects/sequence.ts';
|
|
30
|
+
export { cssPlan, effectClass, effectCss, effectPlan, endsHidden, entranceNames, exitNames, toCssSteps, type CssPlan, type CssStep, } from './effects/css.ts';
|
package/dist/effects.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The built-in effects of the `greese` prop: the one place they are defined.
|
|
3
|
+
*
|
|
4
|
+
* - **Base effects** animate one property each (opacity, scale, a translation,
|
|
5
|
+
* a filter…) and take optional parameters: `start`, `end` and whatever else
|
|
6
|
+
* they need, each with a default. `fadeIn` fades from 0 to 1; `fadeIn(0.25,
|
|
7
|
+
* 0.8)` or `fadeIn({ end: 0.8 })` changes that.
|
|
8
|
+
* - **Combo effects** are made of base effects, by name (typed against
|
|
9
|
+
* `baseEffects`), each placed in time as a share of the combo: `popIn` is
|
|
10
|
+
* `pop` plus a `fadeIn` over its first 60%.
|
|
11
|
+
*
|
|
12
|
+
* Every call also takes the timing keys `duration` and `offset` (seconds) and
|
|
13
|
+
* `ease`, after the effect's own parameters when given by position:
|
|
14
|
+
* `someEffect(start, end, duration, offset, ease)`.
|
|
15
|
+
*
|
|
16
|
+
* Nothing else lists the effects. The runtime plays what this file says; the
|
|
17
|
+
* build writes every effect that ends at the element's resting state as CSS
|
|
18
|
+
* (`@keyframes greese-<name>`) and every one that ends away from it as an
|
|
19
|
+
* overlay exit (`dist/_maps.scss`); the name types and Theme Studio's lists are
|
|
20
|
+
* derived; an effect Studio defines is turned into the same CSS by the same
|
|
21
|
+
* `cssPlan()`. To add an effect, add it here.
|
|
22
|
+
*
|
|
23
|
+
* Its modules (effects/) import nothing but each other and `kebab.ts` (which
|
|
24
|
+
* imports nothing, so the runtime chunk carries no tokens): the build script
|
|
25
|
+
* runs them directly under Node's type stripping.
|
|
26
|
+
*/
|
|
27
|
+
export { baseEffects, builtInEffects, comboEffects, cssReach, effectDirections, effectNames, isBuiltInEffect, jsReach, negate, timingKeys, } from "./effects/definitions.js";
|
|
28
|
+
export { effectFrames, effectParamNames } from "./effects/frames.js";
|
|
29
|
+
export { parseSequence, splitTiming, } from "./effects/sequence.js";
|
|
30
|
+
export { cssPlan, effectClass, effectCss, effectPlan, endsHidden, entranceNames, exitNames, toCssSteps, } from "./effects/css.js";
|