CoolFace
Apppublic

AK-21/Graphite-Industrial-Intelligence

sourceHugging Faceupdated 3mo agoView on Hugging Face
0likes
transition.js330 linesDownload Raw Back to lib
1let { list } = require('postcss')2let parser = require('postcss-value-parser')3 4let Browsers = require('./browsers')5let vendor = require('./vendor')6 7class Transition {8  constructor(prefixes) {9    this.props = ['transition', 'transition-property']10    this.prefixes = prefixes11  }12 13  /**14   * Process transition and add prefixes for all necessary properties15   */16  add(decl, result) {17    let prefix, prop18    let add = this.prefixes.add[decl.prop]19    let vendorPrefixes = this.ruleVendorPrefixes(decl)20    let declPrefixes = vendorPrefixes || (add && add.prefixes) || []21 22    let params = this.parse(decl.value)23    let names = params.map(i => this.findProp(i))24    let added = []25 26    if (names.some(i => i[0] === '-')) {27      return28    }29 30    for (let param of params) {31      prop = this.findProp(param)32      if (prop[0] === '-') continue33 34      let prefixer = this.prefixes.add[prop]35      if (!prefixer || !prefixer.prefixes) continue36 37      for (prefix of prefixer.prefixes) {38        if (vendorPrefixes && !vendorPrefixes.some(p => prefix.includes(p))) {39          continue40        }41 42        let prefixed = this.prefixes.prefixed(prop, prefix)43        if (prefixed !== '-ms-transform' && !names.includes(prefixed)) {44          if (!this.disabled(prop, prefix)) {45            added.push(this.clone(prop, prefixed, param))46          }47        }48      }49    }50 51    params = params.concat(added)52    let value = this.stringify(params)53 54    let webkitClean = this.stringify(55      this.cleanFromUnprefixed(params, '-webkit-')56    )57    if (declPrefixes.includes('-webkit-')) {58      this.cloneBefore(decl, `-webkit-${decl.prop}`, webkitClean)59    }60    this.cloneBefore(decl, decl.prop, webkitClean)61    if (declPrefixes.includes('-o-')) {62      let operaClean = this.stringify(this.cleanFromUnprefixed(params, '-o-'))63      this.cloneBefore(decl, `-o-${decl.prop}`, operaClean)64    }65 66    for (prefix of declPrefixes) {67      if (prefix !== '-webkit-' && prefix !== '-o-') {68        let prefixValue = this.stringify(69          this.cleanOtherPrefixes(params, prefix)70        )71        this.cloneBefore(decl, prefix + decl.prop, prefixValue)72      }73    }74 75    if (value !== decl.value && !this.already(decl, decl.prop, value)) {76      this.checkForWarning(result, decl)77      decl.cloneBefore()78      decl.value = value79    }80  }81 82  /**83   * Does we already have this declaration84   */85  already(decl, prop, value) {86    return decl.parent.some(i => i.prop === prop && i.value === value)87  }88 89  /**90   * Show transition-property warning91   */92  checkForWarning(result, decl) {93    if (decl.prop !== 'transition-property') {94      return95    }96 97    let isPrefixed = false98    let hasAssociatedProp = false99 100    decl.parent.each(i => {101      if (i.type !== 'decl') {102        return undefined103      }104      if (i.prop.indexOf('transition-') !== 0) {105        return undefined106      }107      let values = list.comma(i.value)108      // check if current Rule's transition-property comma separated value list needs prefixes109      if (i.prop === 'transition-property') {110        values.forEach(value => {111          let lookup = this.prefixes.add[value]112          if (lookup && lookup.prefixes && lookup.prefixes.length > 0) {113            isPrefixed = true114          }115        })116        return undefined117      }118      // check if another transition-* prop in current Rule has comma separated value list119      hasAssociatedProp = hasAssociatedProp || values.length > 1120      return false121    })122 123    if (isPrefixed && hasAssociatedProp) {124      decl.warn(125        result,126        'Replace transition-property to transition, ' +127          'because Autoprefixer could not support ' +128          'any cases of transition-property ' +129          'and other transition-*'130      )131    }132  }133 134  /**135   * Remove all non-webkit prefixes and unprefixed params if we have prefixed136   */137  cleanFromUnprefixed(params, prefix) {138    let remove = params139      .map(i => this.findProp(i))140      .filter(i => i.slice(0, prefix.length) === prefix)141      .map(i => this.prefixes.unprefixed(i))142 143    let result = []144    for (let param of params) {145      let prop = this.findProp(param)146      let p = vendor.prefix(prop)147      if (!remove.includes(prop) && (p === prefix || p === '')) {148        result.push(param)149      }150    }151    return result152  }153 154  cleanOtherPrefixes(params, prefix) {155    return params.filter(param => {156      let current = vendor.prefix(this.findProp(param))157      return current === '' || current === prefix158    })159  }160 161  /**162   * Return new param array with different name163   */164  clone(origin, name, param) {165    let result = []166    let changed = false167    for (let i of param) {168      if (!changed && i.type === 'word' && i.value === origin) {169        result.push({ type: 'word', value: name })170        changed = true171      } else {172        result.push(i)173      }174    }175    return result176  }177 178  /**179   * Add declaration if it is not exist180   */181  cloneBefore(decl, prop, value) {182    if (!this.already(decl, prop, value)) {183      decl.cloneBefore({ prop, value })184    }185  }186 187  /**188   * Check property for disabled by option189   */190  disabled(prop, prefix) {191    let other = ['order', 'justify-content', 'align-self', 'align-content']192    if (prop.includes('flex') || other.includes(prop)) {193      if (this.prefixes.options.flexbox === false) {194        return true195      }196 197      if (this.prefixes.options.flexbox === 'no-2009') {198        return prefix.includes('2009')199      }200    }201    return undefined202  }203 204  /**205   * Find or create separator206   */207  div(params) {208    for (let param of params) {209      for (let node of param) {210        if (node.type === 'div' && node.value === ',') {211          return node212        }213      }214    }215    return { after: ' ', type: 'div', value: ',' }216  }217 218  /**219   * Find property name220   */221  findProp(param) {222    let prop = param[0].value223    if (/^\d/.test(prop)) {224      for (let [i, token] of param.entries()) {225        if (i !== 0 && token.type === 'word') {226          return token.value227        }228      }229    }230    return prop231  }232 233  /**234   * Parse properties list to array235   */236  parse(value) {237    let ast = parser(value)238    let result = []239    let param = []240    for (let node of ast.nodes) {241      param.push(node)242      if (node.type === 'div' && node.value === ',') {243        result.push(param)244        param = []245      }246    }247    result.push(param)248    return result.filter(i => i.length > 0)249  }250 251  /**252   * Process transition and remove all unnecessary properties253   */254  remove(decl) {255    let params = this.parse(decl.value)256    params = params.filter(i => {257      let prop = this.prefixes.remove[this.findProp(i)]258      return !prop || !prop.remove259    })260    let value = this.stringify(params)261 262    if (decl.value === value) {263      return264    }265 266    if (params.length === 0) {267      decl.remove()268      return269    }270 271    let double = decl.parent.some(i => {272      return i.prop === decl.prop && i.value === value273    })274    let smaller = decl.parent.some(i => {275      return i !== decl && i.prop === decl.prop && i.value.length > value.length276    })277 278    if (double || smaller) {279      decl.remove()280      return281    }282 283    decl.value = value284  }285 286  /**287   * Check if transition prop is inside vendor specific rule288   */289  ruleVendorPrefixes(decl) {290    let { parent } = decl291 292    if (parent.type !== 'rule') {293      return false294    } else if (!parent.selector.includes(':-')) {295      return false296    }297 298    let selectors = Browsers.prefixes().filter(s =>299      parent.selector.includes(':' + s)300    )301 302    return selectors.length > 0 ? selectors : false303  }304 305  /**306   * Return properties string from array307   */308  stringify(params) {309    if (params.length === 0) {310      return ''311    }312    let nodes = []313    for (let param of params) {314      if (param[param.length - 1].type !== 'div') {315        param.push(this.div(params))316      }317      nodes.push(...param)318    }319    if (nodes[0].type === 'div') {320      nodes = nodes.slice(1)321    }322    if (nodes[nodes.length - 1].type === 'div') {323      nodes = nodes.slice(0, -1)324    }325    return parser.stringify({ nodes })326  }327}328 329module.exports = Transition330