]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-dom/style): support CSS variables and auto prefixing
authorEvan You <yyx990803@gmail.com>
Sat, 9 Nov 2019 03:38:04 +0000 (22:38 -0500)
committerEvan You <yyx990803@gmail.com>
Sat, 9 Nov 2019 03:38:04 +0000 (22:38 -0500)
packages/runtime-dom/__tests__/modules/style.spec.ts
packages/runtime-dom/src/modules/style.ts

index 1e3770ce237c5cfb1fc3b5b4037b6049bcdef7b3..28eac3b426ba978ae91d5d489c1c5d21c1360fc5 100644 (file)
@@ -45,4 +45,33 @@ describe(`module style`, () => {
     expect(el.style.getPropertyValue('color')).toBe('red')
     expect(el.style.getPropertyValue('margin-right')).toBe('10px')
   })
+
+  // JSDOM doesn't support custom properties on style object so we have to
+  // mock it here.
+  function mockElementWithStyle() {
+    const store: any = {}
+    return {
+      style: {
+        WebkitTransition: '',
+        setProperty(key: string, val: string) {
+          store[key] = val
+        },
+        getPropertyValue(key: string) {
+          return store[key]
+        }
+      }
+    }
+  }
+
+  it('CSS custom properties', () => {
+    const el = mockElementWithStyle()
+    patchStyle(el as any, {}, { '--theme': 'red' } as any)
+    expect(el.style.getPropertyValue('--theme')).toBe('red')
+  })
+
+  it('auto vendor prefixing', () => {
+    const el = mockElementWithStyle()
+    patchStyle(el as any, {}, { transition: 'all 1s' })
+    expect(el.style.WebkitTransition).toBe('all 1s')
+  })
 })
index 55ef3870997a19958c59c11296fa0ee3e47ba5fa..67c5593fe5e5f43c18c4e4aa61a0a644a31f4b99 100644 (file)
@@ -1,4 +1,5 @@
-import { isString, hyphenate } from '@vue/shared'
+import { isString, hyphenate, capitalize } from '@vue/shared'
+import { camelize } from '@vue/runtime-core'
 
 type Style = string | Partial<CSSStyleDeclaration> | null
 
@@ -25,16 +26,42 @@ export function patchStyle(el: Element, prev: Style, next: Style) {
 const importantRE = /\s*!important$/
 
 function setStyle(style: CSSStyleDeclaration, name: string, val: string) {
-  let rawName = hyphenate(name)
-  if (importantRE.test(val)) {
-    style.setProperty(rawName, val.replace(importantRE, ''), 'important')
+  if (name.startsWith('--')) {
+    // custom property definition
+    style.setProperty(name, val)
   } else {
-    /**
-     * TODO:
-     * 1. support values array created by autoprefixer.
-     * 2. support css variable, maybe should import 'csstype'.
-     *    similar to https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L1450
-     */
-    style.setProperty(rawName, val)
+    const prefixed = autoPrefix(style, name)
+    if (importantRE.test(val)) {
+      // !important
+      style.setProperty(
+        hyphenate(prefixed),
+        val.replace(importantRE, ''),
+        'important'
+      )
+    } else {
+      style[prefixed as any] = val
+    }
+  }
+}
+
+const prefixes = ['Webkit', 'Moz', 'ms']
+const prefixCache: Record<string, string> = {}
+
+function autoPrefix(style: CSSStyleDeclaration, rawName: string): string {
+  const cached = prefixCache[rawName]
+  if (cached) {
+    return cached
+  }
+  let name = camelize(rawName)
+  if (name !== 'filter' && name in style) {
+    return (prefixCache[rawName] = name)
+  }
+  name = capitalize(name)
+  for (let i = 0; i < prefixes.length; i++) {
+    const prefixed = prefixes[i] + name
+    if (prefixed in style) {
+      return (prefixCache[rawName] = prefixed)
+    }
   }
+  return rawName
 }