| 1 | 'use strict';
|
|---|
| 2 | const { unit } = require('postcss-value-parser');
|
|---|
| 3 | const { getArguments } = require('cssnano-utils');
|
|---|
| 4 | const addSpace = require('../lib/addSpace');
|
|---|
| 5 | const getValue = require('../lib/getValue');
|
|---|
| 6 |
|
|---|
| 7 | // animation: [ none | <keyframes-name> ] || <time> || <single-timing-function> || <time> || <single-animation-iteration-count> || <single-animation-direction> || <single-animation-fill-mode> || <single-animation-play-state>
|
|---|
| 8 | const functions = new Set(['steps', 'cubic-bezier', 'frames']);
|
|---|
| 9 | const keywords = new Set([
|
|---|
| 10 | 'ease',
|
|---|
| 11 | 'ease-in',
|
|---|
| 12 | 'ease-in-out',
|
|---|
| 13 | 'ease-out',
|
|---|
| 14 | 'linear',
|
|---|
| 15 | 'step-end',
|
|---|
| 16 | 'step-start',
|
|---|
| 17 | ]);
|
|---|
| 18 |
|
|---|
| 19 | const directions = new Set([
|
|---|
| 20 | 'normal',
|
|---|
| 21 | 'reverse',
|
|---|
| 22 | 'alternate',
|
|---|
| 23 | 'alternate-reverse',
|
|---|
| 24 | ]);
|
|---|
| 25 | const fillModes = new Set(['none', 'forwards', 'backwards', 'both']);
|
|---|
| 26 | const playStates = new Set(['running', 'paused']);
|
|---|
| 27 | const timeUnits = new Set(['ms', 's']);
|
|---|
| 28 |
|
|---|
| 29 | /**
|
|---|
| 30 | * @param {string} value
|
|---|
| 31 | * @param {string} type
|
|---|
| 32 | * @return {boolean}
|
|---|
| 33 | */
|
|---|
| 34 | const isTimingFunction = (value, type) => {
|
|---|
| 35 | return (type === 'function' && functions.has(value)) || keywords.has(value);
|
|---|
| 36 | };
|
|---|
| 37 | /**
|
|---|
| 38 | * @param {string} value
|
|---|
| 39 | * @return {boolean}
|
|---|
| 40 | */
|
|---|
| 41 | const isDirection = (value) => {
|
|---|
| 42 | return directions.has(value);
|
|---|
| 43 | };
|
|---|
| 44 | /**
|
|---|
| 45 | * @param {string} value
|
|---|
| 46 | * @return {boolean}
|
|---|
| 47 | */
|
|---|
| 48 | const isFillMode = (value) => {
|
|---|
| 49 | return fillModes.has(value);
|
|---|
| 50 | };
|
|---|
| 51 | /**
|
|---|
| 52 | * @param {string} value
|
|---|
| 53 | * @return {boolean}
|
|---|
| 54 | */
|
|---|
| 55 | const isPlayState = (value) => {
|
|---|
| 56 | return playStates.has(value);
|
|---|
| 57 | };
|
|---|
| 58 | /**
|
|---|
| 59 | * @param {string} value
|
|---|
| 60 | * @return {boolean}
|
|---|
| 61 | */
|
|---|
| 62 | const isTime = (value) => {
|
|---|
| 63 | const quantity = unit(value);
|
|---|
| 64 |
|
|---|
| 65 | return quantity && timeUnits.has(quantity.unit);
|
|---|
| 66 | };
|
|---|
| 67 | /**
|
|---|
| 68 | * @param {string} value
|
|---|
| 69 | * @return {boolean}
|
|---|
| 70 | */
|
|---|
| 71 | const isIterationCount = (value) => {
|
|---|
| 72 | const quantity = unit(value);
|
|---|
| 73 |
|
|---|
| 74 | return value === 'infinite' || (quantity && !quantity.unit);
|
|---|
| 75 | };
|
|---|
| 76 |
|
|---|
| 77 | const stateConditions = [
|
|---|
| 78 | { property: 'duration', delegate: isTime },
|
|---|
| 79 | { property: 'timingFunction', delegate: isTimingFunction },
|
|---|
| 80 | { property: 'delay', delegate: isTime },
|
|---|
| 81 | { property: 'iterationCount', delegate: isIterationCount },
|
|---|
| 82 | { property: 'direction', delegate: isDirection },
|
|---|
| 83 | { property: 'fillMode', delegate: isFillMode },
|
|---|
| 84 | { property: 'playState', delegate: isPlayState },
|
|---|
| 85 | ];
|
|---|
| 86 | /**
|
|---|
| 87 | * @param {import('postcss-value-parser').Node[][]} args
|
|---|
| 88 | * @return {import('postcss-value-parser').Node[][]}
|
|---|
| 89 | */
|
|---|
| 90 | function normalize(args) {
|
|---|
| 91 | const list = [];
|
|---|
| 92 |
|
|---|
| 93 | for (const arg of args) {
|
|---|
| 94 | /** @type {Record<string, import('postcss-value-parser').Node[]>} */
|
|---|
| 95 | const state = {
|
|---|
| 96 | name: [],
|
|---|
| 97 | duration: [],
|
|---|
| 98 | timingFunction: [],
|
|---|
| 99 | delay: [],
|
|---|
| 100 | iterationCount: [],
|
|---|
| 101 | direction: [],
|
|---|
| 102 | fillMode: [],
|
|---|
| 103 | playState: [],
|
|---|
| 104 | };
|
|---|
| 105 |
|
|---|
| 106 | arg.forEach((node) => {
|
|---|
| 107 | let { type, value } = node;
|
|---|
| 108 |
|
|---|
| 109 | if (type === 'space') {
|
|---|
| 110 | return;
|
|---|
| 111 | }
|
|---|
| 112 |
|
|---|
| 113 | value = value.toLowerCase();
|
|---|
| 114 |
|
|---|
| 115 | const hasMatch = stateConditions.some(({ property, delegate }) => {
|
|---|
| 116 | if (delegate(value, type) && !state[property].length) {
|
|---|
| 117 | state[property] = [node, addSpace()];
|
|---|
| 118 | return true;
|
|---|
| 119 | }
|
|---|
| 120 | });
|
|---|
| 121 |
|
|---|
| 122 | if (!hasMatch) {
|
|---|
| 123 | state.name = [...state.name, node, addSpace()];
|
|---|
| 124 | }
|
|---|
| 125 | });
|
|---|
| 126 |
|
|---|
| 127 | list.push([
|
|---|
| 128 | ...state.name,
|
|---|
| 129 | ...state.duration,
|
|---|
| 130 | ...state.timingFunction,
|
|---|
| 131 | ...state.delay,
|
|---|
| 132 | ...state.iterationCount,
|
|---|
| 133 | ...state.direction,
|
|---|
| 134 | ...state.fillMode,
|
|---|
| 135 | ...state.playState,
|
|---|
| 136 | ]);
|
|---|
| 137 | }
|
|---|
| 138 | return list;
|
|---|
| 139 | }
|
|---|
| 140 | /**
|
|---|
| 141 | * @param {import('postcss-value-parser').ParsedValue} parsed
|
|---|
| 142 | * @return {string}
|
|---|
| 143 | */
|
|---|
| 144 | module.exports = function normalizeAnimation(parsed) {
|
|---|
| 145 | const values = normalize(getArguments(parsed));
|
|---|
| 146 |
|
|---|
| 147 | return getValue(values);
|
|---|
| 148 | };
|
|---|