@react-x11/components 0.9.0 → 0.11.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/README.md +10 -4
- package/dist/charts/node.d.ts +11 -1
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +44 -14
- package/dist/charts/node.js.map +1 -1
- package/dist/charts/render.d.ts +12 -3
- package/dist/charts/render.d.ts.map +1 -1
- package/dist/charts/render.js +94 -25
- package/dist/charts/render.js.map +1 -1
- package/dist/code/index.d.ts.map +1 -1
- package/dist/code/index.js +27 -24
- package/dist/code/index.js.map +1 -1
- package/dist/code-editor/completion.d.ts.map +1 -1
- package/dist/code-editor/completion.js +3 -2
- package/dist/code-editor/completion.js.map +1 -1
- package/dist/code-editor/index.d.ts.map +1 -1
- package/dist/code-editor/index.js +19 -0
- package/dist/code-editor/index.js.map +1 -1
- package/dist/code-editor/node.d.ts +177 -7
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +1329 -164
- package/dist/code-editor/node.js.map +1 -1
- package/dist/code-language/index.d.ts +1 -1
- package/dist/code-language/index.d.ts.map +1 -1
- package/dist/code-language/index.js +1 -1
- package/dist/code-language/index.js.map +1 -1
- package/dist/code-language/languages/glsl.d.ts.map +1 -1
- package/dist/code-language/languages/glsl.js +10 -2
- package/dist/code-language/languages/glsl.js.map +1 -1
- package/dist/code-language/languages/javascript.d.ts.map +1 -1
- package/dist/code-language/languages/javascript.js +18 -3
- package/dist/code-language/languages/javascript.js.map +1 -1
- package/dist/code-language/languages/json.d.ts.map +1 -1
- package/dist/code-language/languages/json.js +10 -2
- package/dist/code-language/languages/json.js.map +1 -1
- package/dist/code-language/languages/shell.d.ts.map +1 -1
- package/dist/code-language/languages/shell.js +10 -2
- package/dist/code-language/languages/shell.js.map +1 -1
- package/dist/code-language/languages/sql.d.ts.map +1 -1
- package/dist/code-language/languages/sql.js +20 -2
- package/dist/code-language/languages/sql.js.map +1 -1
- package/dist/code-language/runs.d.ts +31 -0
- package/dist/code-language/runs.d.ts.map +1 -1
- package/dist/code-language/runs.js +105 -0
- package/dist/code-language/runs.js.map +1 -1
- package/dist/code-language/stream.d.ts +22 -0
- package/dist/code-language/stream.d.ts.map +1 -1
- package/dist/code-language/stream.js +259 -9
- package/dist/code-language/stream.js.map +1 -1
- package/dist/code-language/types.d.ts +5 -4
- package/dist/code-language/types.d.ts.map +1 -1
- package/dist/codeblock/index.d.ts +26 -2
- package/dist/codeblock/index.d.ts.map +1 -1
- package/dist/codeblock/index.js +57 -4
- package/dist/codeblock/index.js.map +1 -1
- package/dist/flow/draw.d.ts +50 -0
- package/dist/flow/draw.d.ts.map +1 -1
- package/dist/flow/draw.js +132 -33
- package/dist/flow/draw.js.map +1 -1
- package/dist/flow/gl/color.d.ts +13 -0
- package/dist/flow/gl/color.d.ts.map +1 -0
- package/dist/flow/gl/color.js +82 -0
- package/dist/flow/gl/color.js.map +1 -0
- package/dist/flow/gl/index.d.ts +60 -0
- package/dist/flow/gl/index.d.ts.map +1 -0
- package/dist/flow/gl/index.js +283 -0
- package/dist/flow/gl/index.js.map +1 -0
- package/dist/flow/gl/pack.d.ts +133 -0
- package/dist/flow/gl/pack.d.ts.map +1 -0
- package/dist/flow/gl/pack.js +408 -0
- package/dist/flow/gl/pack.js.map +1 -0
- package/dist/flow/gl/renderer.d.ts +95 -0
- package/dist/flow/gl/renderer.d.ts.map +1 -0
- package/dist/flow/gl/renderer.js +459 -0
- package/dist/flow/gl/renderer.js.map +1 -0
- package/dist/flow/gl/shaders.d.ts +79 -0
- package/dist/flow/gl/shaders.d.ts.map +1 -0
- package/dist/flow/gl/shaders.js +346 -0
- package/dist/flow/gl/shaders.js.map +1 -0
- package/dist/flow/gl/text.d.ts +318 -0
- package/dist/flow/gl/text.d.ts.map +1 -0
- package/dist/flow/gl/text.js +1103 -0
- package/dist/flow/gl/text.js.map +1 -0
- package/dist/flow/index.d.ts +1 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +619 -64
- package/dist/flow/index.js.map +1 -1
- package/dist/flow/node.d.ts +621 -72
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +2253 -1018
- package/dist/flow/node.js.map +1 -1
- package/dist/flow/paint.d.ts +35 -0
- package/dist/flow/paint.d.ts.map +1 -0
- package/dist/flow/paint.js +393 -0
- package/dist/flow/paint.js.map +1 -0
- package/dist/flow/paths.d.ts +14 -0
- package/dist/flow/paths.d.ts.map +1 -1
- package/dist/flow/paths.js +45 -19
- package/dist/flow/paths.js.map +1 -1
- package/dist/flow/scene.d.ts +584 -0
- package/dist/flow/scene.d.ts.map +1 -0
- package/dist/flow/scene.js +1453 -0
- package/dist/flow/scene.js.map +1 -0
- package/dist/flow/types.d.ts +111 -1
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/html/controls.d.ts +28 -0
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +52 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/calc.d.ts +14 -0
- package/dist/html/css/calc.d.ts.map +1 -0
- package/dist/html/css/calc.js +344 -0
- package/dist/html/css/calc.js.map +1 -0
- package/dist/html/css/cascade.d.ts +262 -6
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +1373 -67
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +23 -0
- package/dist/html/css/color.d.ts.map +1 -0
- package/dist/html/css/color.js +740 -0
- package/dist/html/css/color.js.map +1 -0
- package/dist/html/css/content.d.ts +92 -0
- package/dist/html/css/content.d.ts.map +1 -0
- package/dist/html/css/content.js +388 -0
- package/dist/html/css/content.js.map +1 -0
- package/dist/html/css/counter-styles.d.ts +76 -0
- package/dist/html/css/counter-styles.d.ts.map +1 -0
- package/dist/html/css/counter-styles.js +893 -0
- package/dist/html/css/counter-styles.js.map +1 -0
- package/dist/html/css/decode.d.ts +12 -0
- package/dist/html/css/decode.d.ts.map +1 -0
- package/dist/html/css/decode.js +116 -0
- package/dist/html/css/decode.js.map +1 -0
- package/dist/html/css/parse.d.ts +110 -10
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +1473 -180
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +501 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +3729 -206
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +65 -16
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +37 -3
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +122 -48
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/css/vars.d.ts +52 -0
- package/dist/html/css/vars.d.ts.map +1 -0
- package/dist/html/css/vars.js +358 -0
- package/dist/html/css/vars.js.map +1 -0
- package/dist/html/dom.d.ts +30 -4
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +278 -17
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +106 -0
- package/dist/html/fonts.d.ts.map +1 -0
- package/dist/html/fonts.js +565 -0
- package/dist/html/fonts.js.map +1 -0
- package/dist/html/gif.d.ts +15 -0
- package/dist/html/gif.d.ts.map +1 -0
- package/dist/html/gif.js +229 -0
- package/dist/html/gif.js.map +1 -0
- package/dist/html/index.d.ts +50 -11
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +115 -9
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +226 -3
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +3077 -219
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +367 -17
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +1742 -182
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +58 -0
- package/dist/html/layout/cache.d.ts.map +1 -0
- package/dist/html/layout/cache.js +224 -0
- package/dist/html/layout/cache.js.map +1 -0
- package/dist/html/layout/collapse.d.ts +45 -0
- package/dist/html/layout/collapse.d.ts.map +1 -0
- package/dist/html/layout/collapse.js +246 -0
- package/dist/html/layout/collapse.js.map +1 -0
- package/dist/html/layout/css-grid.d.ts +7 -0
- package/dist/html/layout/css-grid.d.ts.map +1 -0
- package/dist/html/layout/css-grid.js +900 -0
- package/dist/html/layout/css-grid.js.map +1 -0
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +1025 -89
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/floats.d.ts +45 -6
- package/dist/html/layout/floats.d.ts.map +1 -1
- package/dist/html/layout/floats.js +113 -14
- package/dist/html/layout/floats.js.map +1 -1
- package/dist/html/layout/grid-lines.d.ts +39 -0
- package/dist/html/layout/grid-lines.d.ts.map +1 -0
- package/dist/html/layout/grid-lines.js +119 -0
- package/dist/html/layout/grid-lines.js.map +1 -0
- package/dist/html/layout/grid.d.ts +53 -0
- package/dist/html/layout/grid.d.ts.map +1 -0
- package/dist/html/layout/grid.js +153 -0
- package/dist/html/layout/grid.js.map +1 -0
- package/dist/html/layout/inline.d.ts +154 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +3598 -175
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/shaping.d.ts +6 -0
- package/dist/html/layout/shaping.d.ts.map +1 -0
- package/dist/html/layout/shaping.js +100 -0
- package/dist/html/layout/shaping.js.map +1 -0
- package/dist/html/layout/table.d.ts +3 -0
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +681 -175
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/layout/tracks.d.ts +44 -0
- package/dist/html/layout/tracks.d.ts.map +1 -0
- package/dist/html/layout/tracks.js +349 -0
- package/dist/html/layout/tracks.js.map +1 -0
- package/dist/html/node.d.ts +169 -4
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +1429 -137
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +92 -3
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +3635 -107
- package/dist/html/paint.js.map +1 -1
- package/dist/html/resources.d.ts +60 -14
- package/dist/html/resources.d.ts.map +1 -1
- package/dist/html/resources.js +122 -36
- package/dist/html/resources.js.map +1 -1
- package/dist/html/surfaces.d.ts +23 -0
- package/dist/html/surfaces.d.ts.map +1 -0
- package/dist/html/surfaces.js +68 -0
- package/dist/html/surfaces.js.map +1 -0
- package/dist/html/svg.d.ts +80 -0
- package/dist/html/svg.d.ts.map +1 -0
- package/dist/html/svg.js +442 -0
- package/dist/html/svg.js.map +1 -0
- package/dist/html/url.d.ts +23 -0
- package/dist/html/url.d.ts.map +1 -0
- package/dist/html/url.js +80 -0
- package/dist/html/url.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/codelines.d.ts +42 -0
- package/dist/internal/codelines.d.ts.map +1 -0
- package/dist/internal/codelines.js +170 -0
- package/dist/internal/codelines.js.map +1 -0
- package/dist/internal/collate.d.ts +3 -0
- package/dist/internal/collate.d.ts.map +1 -0
- package/dist/internal/collate.js +11 -0
- package/dist/internal/collate.js.map +1 -0
- package/dist/internal/markdown/ast.d.ts +11 -0
- package/dist/internal/markdown/ast.d.ts.map +1 -1
- package/dist/internal/markdown/parse.d.ts.map +1 -1
- package/dist/internal/markdown/parse.js +109 -8
- package/dist/internal/markdown/parse.js.map +1 -1
- package/dist/internal/prewarm.d.ts +18 -0
- package/dist/internal/prewarm.d.ts.map +1 -0
- package/dist/internal/prewarm.js +42 -0
- package/dist/internal/prewarm.js.map +1 -0
- package/dist/internal/sdf.d.ts +20 -0
- package/dist/internal/sdf.d.ts.map +1 -0
- package/dist/internal/sdf.js +216 -0
- package/dist/internal/sdf.js.map +1 -0
- package/dist/internal/splice.d.ts +12 -0
- package/dist/internal/splice.d.ts.map +1 -0
- package/dist/internal/splice.js +23 -0
- package/dist/internal/splice.js.map +1 -0
- package/dist/internal/window.d.ts +12 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +23 -6
- package/dist/internal/window.js.map +1 -1
- package/dist/maps/anchors.d.ts +6 -1
- package/dist/maps/anchors.d.ts.map +1 -1
- package/dist/maps/anchors.js +260 -1
- package/dist/maps/anchors.js.map +1 -1
- package/dist/maps/controller.d.ts +57 -0
- package/dist/maps/controller.d.ts.map +1 -1
- package/dist/maps/controller.js +97 -18
- package/dist/maps/controller.js.map +1 -1
- package/dist/maps/gl/buckets.d.ts +5 -1
- package/dist/maps/gl/buckets.d.ts.map +1 -1
- package/dist/maps/gl/buckets.js +5 -2
- package/dist/maps/gl/buckets.js.map +1 -1
- package/dist/maps/gl/build-worker.d.ts +2 -0
- package/dist/maps/gl/build-worker.d.ts.map +1 -1
- package/dist/maps/gl/build-worker.js +1 -1
- package/dist/maps/gl/build-worker.js.map +1 -1
- package/dist/maps/gl/cover.d.ts +23 -4
- package/dist/maps/gl/cover.d.ts.map +1 -1
- package/dist/maps/gl/cover.js +61 -48
- package/dist/maps/gl/cover.js.map +1 -1
- package/dist/maps/gl/placement.d.ts +21 -8
- package/dist/maps/gl/placement.d.ts.map +1 -1
- package/dist/maps/gl/placement.js +133 -58
- package/dist/maps/gl/placement.js.map +1 -1
- package/dist/maps/gl/renderer.d.ts +36 -4
- package/dist/maps/gl/renderer.d.ts.map +1 -1
- package/dist/maps/gl/renderer.js +81 -24
- package/dist/maps/gl/renderer.js.map +1 -1
- package/dist/maps/gl/shaders.d.ts +18 -17
- package/dist/maps/gl/shaders.d.ts.map +1 -1
- package/dist/maps/gl/shaders.js +34 -32
- package/dist/maps/gl/shaders.js.map +1 -1
- package/dist/maps/gl/store.d.ts +10 -0
- package/dist/maps/gl/store.d.ts.map +1 -1
- package/dist/maps/gl/store.js +47 -4
- package/dist/maps/gl/store.js.map +1 -1
- package/dist/maps/gl/text.d.ts +153 -24
- package/dist/maps/gl/text.d.ts.map +1 -1
- package/dist/maps/gl/text.js +379 -63
- package/dist/maps/gl/text.js.map +1 -1
- package/dist/maps/gl/view.d.ts +293 -2
- package/dist/maps/gl/view.d.ts.map +1 -1
- package/dist/maps/gl/view.js +317 -82
- package/dist/maps/gl/view.js.map +1 -1
- package/dist/maps/icons.d.ts +32 -0
- package/dist/maps/icons.d.ts.map +1 -0
- package/dist/maps/icons.js +151 -0
- package/dist/maps/icons.js.map +1 -0
- package/dist/maps/index.d.ts +2 -0
- package/dist/maps/index.d.ts.map +1 -1
- package/dist/maps/index.js +2 -0
- package/dist/maps/index.js.map +1 -1
- package/dist/maps/labels.d.ts +56 -4
- package/dist/maps/labels.d.ts.map +1 -1
- package/dist/maps/labels.js +213 -46
- package/dist/maps/labels.js.map +1 -1
- package/dist/maps/node.d.ts +17 -0
- package/dist/maps/node.d.ts.map +1 -1
- package/dist/maps/node.js +64 -30
- package/dist/maps/node.js.map +1 -1
- package/dist/maps/proj.d.ts +48 -0
- package/dist/maps/proj.d.ts.map +1 -1
- package/dist/maps/proj.js +73 -0
- package/dist/maps/proj.js.map +1 -1
- package/dist/maps/sources.d.ts +5 -3
- package/dist/maps/sources.d.ts.map +1 -1
- package/dist/maps/sources.js +5 -3
- package/dist/maps/sources.js.map +1 -1
- package/dist/maps/style.d.ts +35 -0
- package/dist/maps/style.d.ts.map +1 -1
- package/dist/maps/style.js.map +1 -1
- package/dist/maps/styles.d.ts +12 -0
- package/dist/maps/styles.d.ts.map +1 -1
- package/dist/maps/styles.js +120 -6
- package/dist/maps/styles.js.map +1 -1
- package/dist/maps/tiles.d.ts +31 -11
- package/dist/maps/tiles.d.ts.map +1 -1
- package/dist/maps/tiles.js +61 -27
- package/dist/maps/tiles.js.map +1 -1
- package/dist/maps/types.d.ts +28 -4
- package/dist/maps/types.d.ts.map +1 -1
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +209 -68
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/markdown/spans.js +6 -0
- package/dist/markdown/spans.js.map +1 -1
- package/dist/qml/containers.d.ts.map +1 -1
- package/dist/qml/containers.js +6 -1
- package/dist/qml/containers.js.map +1 -1
- package/dist/qml/interaction.d.ts.map +1 -1
- package/dist/qml/interaction.js +38 -5
- package/dist/qml/interaction.js.map +1 -1
- package/dist/qml/layouts.d.ts.map +1 -1
- package/dist/qml/layouts.js +21 -6
- package/dist/qml/layouts.js.map +1 -1
- package/dist/qml/qtquick.d.ts.map +1 -1
- package/dist/qml/qtquick.js +9 -2
- package/dist/qml/qtquick.js.map +1 -1
- package/dist/qml/react.d.ts.map +1 -1
- package/dist/qml/react.js +9 -7
- package/dist/qml/react.js.map +1 -1
- package/dist/reorder/clock.d.ts +7 -0
- package/dist/reorder/clock.d.ts.map +1 -0
- package/dist/reorder/clock.js +26 -0
- package/dist/reorder/clock.js.map +1 -0
- package/dist/reorder/index.d.ts +25 -3
- package/dist/reorder/index.d.ts.map +1 -1
- package/dist/reorder/index.js +64 -19
- package/dist/reorder/index.js.map +1 -1
- package/dist/rich-text-editor/index.d.ts +0 -11
- package/dist/rich-text-editor/index.d.ts.map +1 -1
- package/dist/rich-text-editor/index.js +38 -9
- package/dist/rich-text-editor/index.js.map +1 -1
- package/dist/rich-text-editor/keymap.d.ts.map +1 -1
- package/dist/rich-text-editor/keymap.js +2 -1
- package/dist/rich-text-editor/keymap.js.map +1 -1
- package/dist/rich-text-editor/keys.d.ts +17 -6
- package/dist/rich-text-editor/keys.d.ts.map +1 -1
- package/dist/rich-text-editor/keys.js +135 -26
- package/dist/rich-text-editor/keys.js.map +1 -1
- package/dist/rich-text-editor/markdown.d.ts.map +1 -1
- package/dist/rich-text-editor/markdown.js +30 -1
- package/dist/rich-text-editor/markdown.js.map +1 -1
- package/dist/rich-text-editor/marks.d.ts +9 -0
- package/dist/rich-text-editor/marks.d.ts.map +1 -0
- package/dist/rich-text-editor/marks.js +83 -0
- package/dist/rich-text-editor/marks.js.map +1 -0
- package/dist/rich-text-editor/render.d.ts.map +1 -1
- package/dist/rich-text-editor/render.js +28 -10
- package/dist/rich-text-editor/render.js.map +1 -1
- package/dist/rich-text-editor/replace.d.ts +13 -0
- package/dist/rich-text-editor/replace.d.ts.map +1 -0
- package/dist/rich-text-editor/replace.js +64 -0
- package/dist/rich-text-editor/replace.js.map +1 -0
- package/dist/rich-text-editor/toolbar.d.ts.map +1 -1
- package/dist/rich-text-editor/toolbar.js +1 -1
- package/dist/rich-text-editor/toolbar.js.map +1 -1
- package/dist/rich-text-editor/virtual.d.ts.map +1 -1
- package/dist/rich-text-editor/virtual.js +17 -0
- package/dist/rich-text-editor/virtual.js.map +1 -1
- package/dist/richtext/node.d.ts +46 -2
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +187 -4
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +13 -8
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +18 -8
- package/dist/richtext/runs.js.map +1 -1
- package/dist/table/index.d.ts.map +1 -1
- package/dist/table/index.js +23 -0
- package/dist/table/index.js.map +1 -1
- package/dist/table/rows.d.ts +3 -2
- package/dist/table/rows.d.ts.map +1 -1
- package/dist/table/rows.js +20 -6
- package/dist/table/rows.js.map +1 -1
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +4 -0
- package/dist/terminal/index.js.map +1 -1
- package/dist/terminal/vt/pty.d.ts +13 -2
- package/dist/terminal/vt/pty.d.ts.map +1 -1
- package/dist/terminal/vt/pty.js +18 -3
- package/dist/terminal/vt/pty.js.map +1 -1
- package/dist/terminal-output/index.d.ts.map +1 -1
- package/dist/terminal-output/index.js +103 -23
- package/dist/terminal-output/index.js.map +1 -1
- package/dist/terminal-output/runs.d.ts +4 -1
- package/dist/terminal-output/runs.d.ts.map +1 -1
- package/dist/terminal-output/runs.js +5 -1
- package/dist/terminal-output/runs.js.map +1 -1
- package/dist/three/canvas.d.ts.map +1 -1
- package/dist/three/canvas.js +85 -7
- package/dist/three/canvas.js.map +1 -1
- package/dist/three/events.d.ts +22 -4
- package/dist/three/events.d.ts.map +1 -1
- package/dist/three/events.js +34 -4
- package/dist/three/events.js.map +1 -1
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +17 -0
- package/dist/tree/index.js.map +1 -1
- package/package.json +42 -33
- package/src/charts/node.ts +62 -26
- package/src/charts/render.ts +120 -30
- package/src/code/index.ts +36 -32
- package/src/code-editor/completion.ts +3 -2
- package/src/code-editor/index.ts +19 -0
- package/src/code-editor/node.ts +1574 -184
- package/src/code-language/index.ts +1 -1
- package/src/code-language/languages/glsl.ts +12 -2
- package/src/code-language/languages/javascript.ts +21 -4
- package/src/code-language/languages/json.ts +12 -2
- package/src/code-language/languages/shell.ts +12 -2
- package/src/code-language/languages/sql.ts +22 -2
- package/src/code-language/runs.ts +111 -1
- package/src/code-language/stream.ts +257 -17
- package/src/code-language/types.ts +5 -4
- package/src/codeblock/index.ts +68 -4
- package/src/flow/draw.ts +167 -41
- package/src/flow/gl/color.ts +85 -0
- package/src/flow/gl/index.ts +342 -0
- package/src/flow/gl/pack.ts +585 -0
- package/src/flow/gl/renderer.ts +679 -0
- package/src/flow/gl/shaders.ts +371 -0
- package/src/flow/gl/text.ts +1295 -0
- package/src/flow/index.ts +794 -67
- package/src/flow/node.ts +2496 -1256
- package/src/flow/paint.ts +511 -0
- package/src/flow/paths.ts +80 -15
- package/src/flow/scene.ts +2199 -0
- package/src/flow/types.ts +113 -1
- package/src/html/controls.ts +73 -2
- package/src/html/css/calc.ts +385 -0
- package/src/html/css/cascade.ts +1481 -62
- package/src/html/css/color.ts +755 -0
- package/src/html/css/content.ts +417 -0
- package/src/html/css/counter-styles.ts +973 -0
- package/src/html/css/decode.ts +130 -0
- package/src/html/css/parse.ts +1514 -160
- package/src/html/css/style.ts +4131 -223
- package/src/html/css/ua.ts +66 -17
- package/src/html/css/values.ts +150 -50
- package/src/html/css/vars.ts +348 -0
- package/src/html/dom.ts +284 -17
- package/src/html/fonts.ts +641 -0
- package/src/html/gif.ts +233 -0
- package/src/html/index.ts +180 -20
- package/src/html/layout/block.ts +3560 -208
- package/src/html/layout/boxes.ts +2141 -190
- package/src/html/layout/cache.ts +289 -0
- package/src/html/layout/collapse.ts +317 -0
- package/src/html/layout/css-grid.ts +1063 -0
- package/src/html/layout/flex.ts +1115 -106
- package/src/html/layout/floats.ts +128 -14
- package/src/html/layout/grid-lines.ts +141 -0
- package/src/html/layout/grid.ts +181 -0
- package/src/html/layout/inline.ts +4187 -192
- package/src/html/layout/shaping.ts +140 -0
- package/src/html/layout/table.ts +696 -186
- package/src/html/layout/tracks.ts +437 -0
- package/src/html/node.ts +1585 -156
- package/src/html/paint.ts +4582 -267
- package/src/html/resources.ts +161 -50
- package/src/html/surfaces.ts +81 -0
- package/src/html/svg.ts +504 -0
- package/src/html/url.ts +86 -0
- package/src/index.ts +2 -0
- package/src/internal/codelines.ts +200 -0
- package/src/internal/collate.ts +12 -0
- package/src/internal/markdown/ast.ts +11 -0
- package/src/internal/markdown/parse.ts +154 -11
- package/src/internal/prewarm.ts +45 -0
- package/src/internal/sdf.ts +227 -0
- package/src/internal/splice.ts +25 -0
- package/src/internal/window.ts +36 -5
- package/src/maps/anchors.ts +307 -1
- package/src/maps/controller.ts +116 -31
- package/src/maps/gl/buckets.ts +5 -1
- package/src/maps/gl/build-worker.ts +13 -2
- package/src/maps/gl/cover.ts +79 -44
- package/src/maps/gl/placement.ts +221 -69
- package/src/maps/gl/renderer.ts +104 -20
- package/src/maps/gl/shaders.ts +35 -32
- package/src/maps/gl/store.ts +66 -17
- package/src/maps/gl/text.ts +444 -71
- package/src/maps/gl/view.ts +361 -96
- package/src/maps/icons.ts +183 -0
- package/src/maps/index.ts +4 -0
- package/src/maps/labels.ts +269 -49
- package/src/maps/node.ts +82 -49
- package/src/maps/proj.ts +87 -0
- package/src/maps/sources.ts +5 -3
- package/src/maps/style.ts +32 -0
- package/src/maps/styles.ts +215 -4
- package/src/maps/tiles.ts +64 -31
- package/src/maps/types.ts +28 -4
- package/src/markdown/index.ts +293 -92
- package/src/markdown/spans.ts +5 -0
- package/src/qml/containers.tsx +6 -1
- package/src/qml/interaction.tsx +42 -5
- package/src/qml/layouts.tsx +21 -6
- package/src/qml/qtquick.tsx +9 -2
- package/src/qml/react.tsx +7 -5
- package/src/reorder/clock.ts +27 -0
- package/src/reorder/index.ts +100 -27
- package/src/rich-text-editor/index.ts +37 -12
- package/src/rich-text-editor/keymap.ts +1 -1
- package/src/rich-text-editor/keys.ts +135 -26
- package/src/rich-text-editor/markdown.ts +36 -1
- package/src/rich-text-editor/marks.ts +101 -0
- package/src/rich-text-editor/render.ts +49 -12
- package/src/rich-text-editor/replace.ts +85 -0
- package/src/rich-text-editor/toolbar.ts +1 -1
- package/src/rich-text-editor/virtual.ts +25 -0
- package/src/richtext/node.ts +235 -14
- package/src/richtext/runs.ts +23 -12
- package/src/table/index.ts +29 -0
- package/src/table/rows.ts +18 -8
- package/src/terminal/index.ts +4 -0
- package/src/terminal/vt/pty.ts +16 -2
- package/src/terminal-output/index.ts +129 -31
- package/src/terminal-output/runs.ts +16 -2
- package/src/three/canvas.ts +102 -8
- package/src/three/events.ts +49 -9
- package/src/tree/index.ts +19 -0
package/dist/html/css/style.js
CHANGED
|
@@ -12,11 +12,59 @@
|
|
|
12
12
|
// - **Nothing here depends on the containing block.** Percentages and `auto`
|
|
13
13
|
// survive as `Len`s (see values.ts). A style computed at one width is
|
|
14
14
|
// correct at every width, which is the whole of the resize story.
|
|
15
|
-
import { AUTO, fourSides, keywordFontSize, parseAlpha, parseColor, parseLength, parseNumber,
|
|
15
|
+
import { AUTO, alphaOf, fourSides, inkColor, isTransparent, keywordFontSize, parseAlpha, parseColor, isPct, parseLength, parseNumber, parseWeight, resolve, splitCommas, splitValue, } from './values.js';
|
|
16
|
+
import { parseUrl, readIdent, startsIdent } from './parse.js';
|
|
17
|
+
import { DEFAULT_QUOTES, parseContent, parseCounterList, parseListStyleType, parseQuotes, } from './content.js';
|
|
18
|
+
/** Whether a length is one `padding` takes: not `auto`, not negative. A
|
|
19
|
+
* `calc()` with a percentage in it has no sign until layout, which clamps
|
|
20
|
+
* it at zero (CSS Values 4 10.12). */
|
|
21
|
+
function notNegative(len) {
|
|
22
|
+
if (len === AUTO)
|
|
23
|
+
return true;
|
|
24
|
+
if (typeof len === 'number')
|
|
25
|
+
return len >= 0;
|
|
26
|
+
return len.px !== undefined || len.pct >= 0;
|
|
27
|
+
}
|
|
28
|
+
function validPadding(len) {
|
|
29
|
+
if (len === null || len === AUTO)
|
|
30
|
+
return false;
|
|
31
|
+
return notNegative(len);
|
|
32
|
+
}
|
|
33
|
+
/** `auto`, or `rect()` of four lengths or `auto`s, commas between them or
|
|
34
|
+
* not; undefined for anything else, which leaves the value as it was. */
|
|
35
|
+
function parseClip(value, ctx) {
|
|
36
|
+
const v = value.trim();
|
|
37
|
+
if (/^auto$/i.test(v))
|
|
38
|
+
return null;
|
|
39
|
+
const m = /^rect\(([^)]*)\)$/i.exec(v);
|
|
40
|
+
if (!m)
|
|
41
|
+
return undefined;
|
|
42
|
+
const parts = m[1].includes(',')
|
|
43
|
+
? m[1].split(',').map((p) => p.trim())
|
|
44
|
+
: m[1].trim().split(/\s+/);
|
|
45
|
+
if (parts.length !== 4)
|
|
46
|
+
return undefined;
|
|
47
|
+
const edges = [];
|
|
48
|
+
for (const part of parts) {
|
|
49
|
+
if (/^auto$/i.test(part)) {
|
|
50
|
+
edges.push(null);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const len = parseLength(part, ctx);
|
|
54
|
+
if (typeof len !== 'number')
|
|
55
|
+
return undefined;
|
|
56
|
+
edges.push(len);
|
|
57
|
+
}
|
|
58
|
+
return { top: edges[0], right: edges[1], bottom: edges[2], left: edges[3] };
|
|
59
|
+
}
|
|
16
60
|
/** The properties that inherit. Named once, so `inherit()` and the `inherit`
|
|
17
61
|
* keyword cannot disagree about the list. */
|
|
18
|
-
|
|
62
|
+
/** The fields a style takes from its parent's (CSS 2.1's "Inherited:
|
|
63
|
+
* yes"), which `inherit` copies one by one; the test holds the two to the
|
|
64
|
+
* same list. */
|
|
65
|
+
export const INHERITED = [
|
|
19
66
|
'color',
|
|
67
|
+
'textFillColor',
|
|
20
68
|
'fontFamily',
|
|
21
69
|
'fontSize',
|
|
22
70
|
'fontWeight',
|
|
@@ -24,22 +72,49 @@ const INHERITED = [
|
|
|
24
72
|
'lineHeight',
|
|
25
73
|
'lineHeightIsLength',
|
|
26
74
|
'textAlign',
|
|
75
|
+
'textAlignLast',
|
|
76
|
+
'textJustify',
|
|
77
|
+
'blockEllipsis',
|
|
78
|
+
'alignBlocks',
|
|
27
79
|
'textIndent',
|
|
28
80
|
'textTransform',
|
|
81
|
+
'textUnderlineOffset',
|
|
29
82
|
'letterSpacing',
|
|
30
83
|
'wordSpacing',
|
|
84
|
+
'textShadow',
|
|
85
|
+
'fontVariantNumeric',
|
|
86
|
+
'fontVariantCaps',
|
|
87
|
+
'fontVariantLigatures',
|
|
88
|
+
'fontVariantPosition',
|
|
89
|
+
'fontKerning',
|
|
90
|
+
'fontFeatureSettings',
|
|
91
|
+
'tabSize',
|
|
92
|
+
'tabSizeIsLength',
|
|
31
93
|
'whiteSpace',
|
|
94
|
+
'overflowWrap',
|
|
95
|
+
'wordBreak',
|
|
96
|
+
'lineBreakAnywhere',
|
|
97
|
+
'textWrapStyle',
|
|
32
98
|
'direction',
|
|
33
99
|
'visibility',
|
|
34
100
|
'listStyleType',
|
|
35
101
|
'listStylePosition',
|
|
102
|
+
'listStyleImage',
|
|
36
103
|
'cursor',
|
|
37
104
|
'borderCollapse',
|
|
38
105
|
'borderSpacing',
|
|
106
|
+
'borderSpacingY',
|
|
107
|
+
'captionSide',
|
|
108
|
+
'emptyCells',
|
|
39
109
|
'tableTextAlignSet',
|
|
110
|
+
'quotes',
|
|
111
|
+
'custom',
|
|
40
112
|
];
|
|
41
|
-
export function initialStyle(look) {
|
|
42
|
-
|
|
113
|
+
export function initialStyle(look, scale = 1) {
|
|
114
|
+
// `medium`, in device pixels
|
|
115
|
+
const medium = BORDER_WIDTH_KEYWORDS.medium * scale;
|
|
116
|
+
// a copy of the literal, not the literal: see `copyStyle`
|
|
117
|
+
return copyStyle({
|
|
43
118
|
color: look.color,
|
|
44
119
|
fontFamily: look.fontFamily,
|
|
45
120
|
fontSize: look.fontSize,
|
|
@@ -48,35 +123,95 @@ export function initialStyle(look) {
|
|
|
48
123
|
lineHeight: 'normal',
|
|
49
124
|
lineHeightIsLength: false,
|
|
50
125
|
textAlign: 'start',
|
|
126
|
+
textAlignLast: 'auto',
|
|
127
|
+
textJustify: 'auto',
|
|
128
|
+
alignBlocks: null,
|
|
51
129
|
textIndent: 0,
|
|
52
130
|
textTransform: 'none',
|
|
53
131
|
letterSpacing: 0,
|
|
54
132
|
wordSpacing: 0,
|
|
133
|
+
textShadow: null,
|
|
134
|
+
fontVariantNumeric: '',
|
|
135
|
+
fontVariantCaps: '',
|
|
136
|
+
fontVariantLigatures: '',
|
|
137
|
+
fontVariantPosition: '',
|
|
138
|
+
fontKerning: '',
|
|
139
|
+
fontFeatureSettings: '',
|
|
140
|
+
tabSize: 8,
|
|
141
|
+
tabSizeIsLength: false,
|
|
55
142
|
whiteSpace: 'normal',
|
|
143
|
+
overflowWrap: 'normal',
|
|
144
|
+
wordBreak: 'normal',
|
|
145
|
+
lineBreakAnywhere: false,
|
|
146
|
+
textWrapStyle: 'auto',
|
|
56
147
|
direction: 'ltr',
|
|
148
|
+
unicodeBidi: 'normal',
|
|
57
149
|
visibility: 'visible',
|
|
58
150
|
listStyleType: 'disc',
|
|
59
151
|
listStylePosition: 'outside',
|
|
152
|
+
listStyleImage: null,
|
|
60
153
|
cursor: null,
|
|
61
154
|
borderCollapse: 'separate',
|
|
62
|
-
|
|
155
|
+
// CSS's initial value; a `<table>` gets its 2px from the UA sheet, and
|
|
156
|
+
// an anonymous table, which no sheet names, has none
|
|
157
|
+
borderSpacing: 0,
|
|
158
|
+
borderSpacingY: 0,
|
|
159
|
+
captionSide: 'top',
|
|
160
|
+
emptyCells: 'show',
|
|
63
161
|
tableTextAlignSet: false,
|
|
162
|
+
quotes: DEFAULT_QUOTES,
|
|
163
|
+
custom: null,
|
|
64
164
|
display: 'inline',
|
|
165
|
+
flowRoot: false,
|
|
166
|
+
contain: 0,
|
|
167
|
+
contentVisibility: 'visible',
|
|
168
|
+
overflowClipBox: 'padding-box',
|
|
169
|
+
overflowClipMargin: 0,
|
|
170
|
+
containIntrinsicWidth: null,
|
|
171
|
+
containIntrinsicHeight: null,
|
|
65
172
|
position: 'static',
|
|
66
173
|
float: 'none',
|
|
67
174
|
clear: 'none',
|
|
68
175
|
boxSizing: 'content-box',
|
|
176
|
+
lineClamp: null,
|
|
177
|
+
maxLines: null,
|
|
178
|
+
clampContinue: 'auto',
|
|
179
|
+
blockEllipsis: false,
|
|
180
|
+
webkitBox: null,
|
|
181
|
+
webkitBoxOrient: 'horizontal',
|
|
182
|
+
webkitBoxDirection: 'normal',
|
|
183
|
+
webkitBoxPack: null,
|
|
184
|
+
webkitBoxAlign: null,
|
|
185
|
+
webkitBoxFlex: 0,
|
|
186
|
+
columns: 0,
|
|
187
|
+
textOverflow: 'clip',
|
|
188
|
+
aspectRatio: null,
|
|
189
|
+
objectFit: 'fill',
|
|
190
|
+
appearance: 'auto',
|
|
191
|
+
objectPositionX: { pct: 50 },
|
|
192
|
+
objectPositionY: { pct: 50 },
|
|
69
193
|
overflowX: 'visible',
|
|
70
194
|
overflowY: 'visible',
|
|
195
|
+
clip: null,
|
|
71
196
|
opacity: 1,
|
|
197
|
+
translate: null,
|
|
198
|
+
transformTranslate: null,
|
|
72
199
|
zIndex: AUTO,
|
|
73
200
|
verticalAlign: 'baseline',
|
|
74
201
|
width: AUTO,
|
|
75
202
|
height: AUTO,
|
|
76
|
-
|
|
203
|
+
// `auto`, which is 0 but for a flex item (CSS Flexbox 4.5): what
|
|
204
|
+
// `min-w-0` undoes has to be told from what it is set to
|
|
205
|
+
minWidth: AUTO,
|
|
77
206
|
maxWidth: 'none',
|
|
78
|
-
minHeight:
|
|
207
|
+
minHeight: AUTO,
|
|
79
208
|
maxHeight: 'none',
|
|
209
|
+
widthKeyword: null,
|
|
210
|
+
minWidthKeyword: null,
|
|
211
|
+
minHeightKeyword: null,
|
|
212
|
+
maxWidthKeyword: null,
|
|
213
|
+
maxHeightKeyword: null,
|
|
214
|
+
heightKeyword: null,
|
|
80
215
|
marginTop: 0,
|
|
81
216
|
marginRight: 0,
|
|
82
217
|
marginBottom: 0,
|
|
@@ -85,10 +220,13 @@ export function initialStyle(look) {
|
|
|
85
220
|
paddingRight: 0,
|
|
86
221
|
paddingBottom: 0,
|
|
87
222
|
paddingLeft: 0,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
223
|
+
// `medium`, and drawn as nothing while the style is `none`: a width
|
|
224
|
+
// computes to zero there (CSS 2.1 8.5.1), which is the layout's to
|
|
225
|
+
// apply, so that `border-style: solid` alone brings a border back
|
|
226
|
+
borderTopWidth: medium,
|
|
227
|
+
borderRightWidth: medium,
|
|
228
|
+
borderBottomWidth: medium,
|
|
229
|
+
borderLeftWidth: medium,
|
|
92
230
|
borderTopStyle: 'none',
|
|
93
231
|
borderRightStyle: 'none',
|
|
94
232
|
borderBottomStyle: 'none',
|
|
@@ -100,22 +238,49 @@ export function initialStyle(look) {
|
|
|
100
238
|
borderBottomColor: 'currentColor',
|
|
101
239
|
borderLeftColor: 'currentColor',
|
|
102
240
|
borderRadius: [0, 0, 0, 0],
|
|
241
|
+
borderRadiusY: null,
|
|
242
|
+
boxShadow: null,
|
|
243
|
+
outlineStyle: 'none',
|
|
244
|
+
outlineWidth: medium,
|
|
245
|
+
outlineColor: 'currentColor',
|
|
246
|
+
outlineOffset: 0,
|
|
103
247
|
top: AUTO,
|
|
104
248
|
right: AUTO,
|
|
105
249
|
bottom: AUTO,
|
|
106
250
|
left: AUTO,
|
|
107
251
|
backgroundColor: null,
|
|
108
252
|
backgroundImage: null,
|
|
109
|
-
|
|
253
|
+
backgroundGradient: null,
|
|
254
|
+
backgroundRepeat: REPEAT,
|
|
110
255
|
backgroundSize: 'auto',
|
|
256
|
+
backgroundAttachment: 'scroll',
|
|
111
257
|
backgroundPositionX: 0,
|
|
112
258
|
backgroundPositionY: 0,
|
|
259
|
+
backgroundImages: null,
|
|
260
|
+
backgroundRepeats: null,
|
|
261
|
+
backgroundSizes: null,
|
|
262
|
+
backgroundAttachments: null,
|
|
263
|
+
backgroundPositions: null,
|
|
264
|
+
backgroundClipText: false,
|
|
265
|
+
backgroundClip: 'border-box',
|
|
266
|
+
backgroundOrigin: 'padding-box',
|
|
267
|
+
backgroundClips: null,
|
|
268
|
+
backgroundOrigins: null,
|
|
269
|
+
borderImage: NO_BORDER_IMAGE,
|
|
270
|
+
textFillColor: null,
|
|
113
271
|
textDecorationLine: 'none',
|
|
114
272
|
textDecorationColor: null,
|
|
115
273
|
textDecorationStyle: 'solid',
|
|
274
|
+
textDecorationThickness: null,
|
|
275
|
+
textUnderlineOffset: null,
|
|
276
|
+
underline: null,
|
|
277
|
+
underlineStyle: 'solid',
|
|
278
|
+
underlineThickness: null,
|
|
279
|
+
underlineOffset: null,
|
|
280
|
+
lineThrough: null,
|
|
116
281
|
flexDirection: 'row',
|
|
117
282
|
flexWrap: 'nowrap',
|
|
118
|
-
justifyContent: '
|
|
283
|
+
justifyContent: 'normal',
|
|
119
284
|
alignItems: 'stretch',
|
|
120
285
|
alignSelf: 'auto',
|
|
121
286
|
alignContent: 'stretch',
|
|
@@ -125,8 +290,54 @@ export function initialStyle(look) {
|
|
|
125
290
|
order: 0,
|
|
126
291
|
rowGap: 0,
|
|
127
292
|
columnGap: 0,
|
|
293
|
+
grid: false,
|
|
294
|
+
gridColumns: null,
|
|
295
|
+
gridRows: null,
|
|
296
|
+
gridAutoRows: AUTO_TRACK,
|
|
297
|
+
gridAutoColumns: AUTO_TRACK,
|
|
298
|
+
gridAreas: null,
|
|
299
|
+
gridAutoFlow: 'row',
|
|
300
|
+
gridColumnStart: null,
|
|
301
|
+
gridColumnEnd: null,
|
|
302
|
+
gridRowStart: null,
|
|
303
|
+
gridRowEnd: null,
|
|
304
|
+
justifyItems: 'normal',
|
|
305
|
+
justifySelf: 'auto',
|
|
128
306
|
tableLayout: 'auto',
|
|
129
|
-
|
|
307
|
+
content: 'normal',
|
|
308
|
+
counterReset: null,
|
|
309
|
+
counterIncrement: null,
|
|
310
|
+
counterSet: null,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
let StyleCopy = null;
|
|
314
|
+
/**
|
|
315
|
+
* A copy of a style, made by a constructor that assigns every field.
|
|
316
|
+
*
|
|
317
|
+
* Never `{ ...style }`. V8 gives an object literal of 128 properties or
|
|
318
|
+
* more a dictionary map — a hash table where a shape would be — and a
|
|
319
|
+
* spread of one adds the copy's properties one at a time: 20 µs a copy at
|
|
320
|
+
* 149 fields, where a literal of 127 spreads in 0.2. A style passed 128 and
|
|
321
|
+
* every `inherit` paid it, and a spread of a style that is not a literal
|
|
322
|
+
* costs 4 µs. A constructor's fields are laid out in the object whatever
|
|
323
|
+
* their number: 0.13 µs a copy, and read as fast as a small literal's.
|
|
324
|
+
*
|
|
325
|
+
* The constructor is written from the fields of the first style copied,
|
|
326
|
+
* which is `initialStyle`'s literal and so every field there is: one added
|
|
327
|
+
* to the style cannot be left out of the copy.
|
|
328
|
+
*/
|
|
329
|
+
export function copyStyle(from) {
|
|
330
|
+
StyleCopy ??= copyConstructor(Object.keys(from));
|
|
331
|
+
return new StyleCopy(from);
|
|
332
|
+
}
|
|
333
|
+
function copyConstructor(fields) {
|
|
334
|
+
const body = fields.map((field) => {
|
|
335
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(field)) {
|
|
336
|
+
throw new Error(`a style field that is not a name: ${field}`);
|
|
337
|
+
}
|
|
338
|
+
return `this.${field} = from.${field};`;
|
|
339
|
+
});
|
|
340
|
+
return new Function('from', body.join('\n'));
|
|
130
341
|
}
|
|
131
342
|
/**
|
|
132
343
|
* A child's starting style: the inherited half of `parent`, everything else
|
|
@@ -134,17 +345,112 @@ export function initialStyle(look) {
|
|
|
134
345
|
* because it is the same object for every element in a pass.
|
|
135
346
|
*/
|
|
136
347
|
export function inherit(parent, initial) {
|
|
137
|
-
const out =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
348
|
+
const out = copyStyle(initial);
|
|
349
|
+
// `INHERITED`, a field at a time: a loop over the names was three
|
|
350
|
+
// quarters of the cost of every element's style, a store through a
|
|
351
|
+
// computed name costing thirty times one through a written one
|
|
352
|
+
out.color = parent.color;
|
|
353
|
+
out.textFillColor = parent.textFillColor;
|
|
354
|
+
out.fontFamily = parent.fontFamily;
|
|
355
|
+
out.fontSize = parent.fontSize;
|
|
356
|
+
out.fontWeight = parent.fontWeight;
|
|
357
|
+
out.fontStyle = parent.fontStyle;
|
|
358
|
+
out.lineHeight = parent.lineHeight;
|
|
359
|
+
out.lineHeightIsLength = parent.lineHeightIsLength;
|
|
360
|
+
out.textAlign = parent.textAlign;
|
|
361
|
+
out.textAlignLast = parent.textAlignLast;
|
|
362
|
+
out.textJustify = parent.textJustify;
|
|
363
|
+
out.blockEllipsis = parent.blockEllipsis;
|
|
364
|
+
out.alignBlocks = parent.alignBlocks;
|
|
365
|
+
out.textIndent = parent.textIndent;
|
|
366
|
+
out.textTransform = parent.textTransform;
|
|
367
|
+
out.letterSpacing = parent.letterSpacing;
|
|
368
|
+
out.wordSpacing = parent.wordSpacing;
|
|
369
|
+
out.textShadow = parent.textShadow;
|
|
370
|
+
out.fontVariantNumeric = parent.fontVariantNumeric;
|
|
371
|
+
out.fontVariantCaps = parent.fontVariantCaps;
|
|
372
|
+
out.fontVariantLigatures = parent.fontVariantLigatures;
|
|
373
|
+
out.fontVariantPosition = parent.fontVariantPosition;
|
|
374
|
+
out.fontKerning = parent.fontKerning;
|
|
375
|
+
out.fontFeatureSettings = parent.fontFeatureSettings;
|
|
376
|
+
out.tabSize = parent.tabSize;
|
|
377
|
+
out.tabSizeIsLength = parent.tabSizeIsLength;
|
|
378
|
+
out.whiteSpace = parent.whiteSpace;
|
|
379
|
+
out.overflowWrap = parent.overflowWrap;
|
|
380
|
+
out.wordBreak = parent.wordBreak;
|
|
381
|
+
out.lineBreakAnywhere = parent.lineBreakAnywhere;
|
|
382
|
+
out.textWrapStyle = parent.textWrapStyle;
|
|
383
|
+
out.direction = parent.direction;
|
|
384
|
+
out.visibility = parent.visibility;
|
|
385
|
+
out.listStyleType = parent.listStyleType;
|
|
386
|
+
out.listStylePosition = parent.listStylePosition;
|
|
387
|
+
out.listStyleImage = parent.listStyleImage;
|
|
388
|
+
out.cursor = parent.cursor;
|
|
389
|
+
out.borderCollapse = parent.borderCollapse;
|
|
390
|
+
out.borderSpacing = parent.borderSpacing;
|
|
391
|
+
out.borderSpacingY = parent.borderSpacingY;
|
|
392
|
+
out.captionSide = parent.captionSide;
|
|
393
|
+
out.emptyCells = parent.emptyCells;
|
|
394
|
+
out.tableTextAlignSet = parent.tableTextAlignSet;
|
|
395
|
+
out.quotes = parent.quotes;
|
|
396
|
+
out.custom = parent.custom;
|
|
144
397
|
out.borderTopColor = 'currentColor';
|
|
145
398
|
out.borderRightColor = 'currentColor';
|
|
146
399
|
out.borderBottomColor = 'currentColor';
|
|
147
400
|
out.borderLeftColor = 'currentColor';
|
|
401
|
+
out.textUnderlineOffset = parent.textUnderlineOffset;
|
|
402
|
+
// propagated, not inherited: `decorate` drops them where CSS stops them
|
|
403
|
+
out.underline = parent.underline;
|
|
404
|
+
out.underlineStyle = parent.underlineStyle;
|
|
405
|
+
out.underlineThickness = parent.underlineThickness;
|
|
406
|
+
out.underlineOffset = parent.underlineOffset;
|
|
407
|
+
out.lineThrough = parent.lineThrough;
|
|
408
|
+
return out;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* The properties that apply to `::first-line` and inherit (CSS Pseudo 4,
|
|
412
|
+
* 2.1.1): its fonts, colour, spacing, transform and decorations. Only
|
|
413
|
+
* these are inherited through it by what is on the line; the rest are the
|
|
414
|
+
* parent's own (2.1.2).
|
|
415
|
+
*/
|
|
416
|
+
export const FIRST_LINE_INHERITED = [
|
|
417
|
+
'color',
|
|
418
|
+
'textFillColor',
|
|
419
|
+
'fontFamily',
|
|
420
|
+
'fontSize',
|
|
421
|
+
'fontWeight',
|
|
422
|
+
'fontStyle',
|
|
423
|
+
'lineHeight',
|
|
424
|
+
'lineHeightIsLength',
|
|
425
|
+
'textTransform',
|
|
426
|
+
'textUnderlineOffset',
|
|
427
|
+
'letterSpacing',
|
|
428
|
+
'wordSpacing',
|
|
429
|
+
'textShadow',
|
|
430
|
+
'fontVariantNumeric',
|
|
431
|
+
'fontVariantCaps',
|
|
432
|
+
'fontVariantLigatures',
|
|
433
|
+
'fontVariantPosition',
|
|
434
|
+
'fontKerning',
|
|
435
|
+
'fontFeatureSettings',
|
|
436
|
+
'underline',
|
|
437
|
+
'underlineStyle',
|
|
438
|
+
'underlineThickness',
|
|
439
|
+
'underlineOffset',
|
|
440
|
+
'lineThrough',
|
|
441
|
+
];
|
|
442
|
+
/**
|
|
443
|
+
* What a box on its block's first line inherits from: its parent's style,
|
|
444
|
+
* with the properties `::first-line` takes from the parent's style on that
|
|
445
|
+
* line (CSS Pseudo 4, 2.1.2) — the pseudo-element's own, for the block's
|
|
446
|
+
* children.
|
|
447
|
+
*/
|
|
448
|
+
export function firstLineParent(parent, onLine) {
|
|
449
|
+
const out = copyStyle(parent);
|
|
450
|
+
const to = out;
|
|
451
|
+
const from = onLine;
|
|
452
|
+
for (const name of FIRST_LINE_INHERITED)
|
|
453
|
+
to[name] = from[name];
|
|
148
454
|
return out;
|
|
149
455
|
}
|
|
150
456
|
// --- applying a declaration -------------------------------------------------
|
|
@@ -157,11 +463,19 @@ const COLOR_PROPS = {
|
|
|
157
463
|
'border-bottom-color': 'borderBottomColor',
|
|
158
464
|
'border-left-color': 'borderLeftColor',
|
|
159
465
|
'text-decoration-color': 'textDecorationColor',
|
|
466
|
+
'-webkit-text-fill-color': 'textFillColor',
|
|
160
467
|
};
|
|
161
468
|
const SIDE_PROPS = {
|
|
162
469
|
margin: ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'],
|
|
163
470
|
padding: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
|
|
471
|
+
inset: ['top', 'right', 'bottom', 'left'],
|
|
164
472
|
};
|
|
473
|
+
/** An `outline-style`: a border style but `hidden`, or `auto`. */
|
|
474
|
+
function outlineStyleOf(v) {
|
|
475
|
+
if (v === 'auto')
|
|
476
|
+
return 'auto';
|
|
477
|
+
return BORDER_STYLES.has(v) && v !== 'hidden' ? v : null;
|
|
478
|
+
}
|
|
165
479
|
const BORDER_STYLES = new Set([
|
|
166
480
|
'none',
|
|
167
481
|
'hidden',
|
|
@@ -174,6 +488,8 @@ const BORDER_STYLES = new Set([
|
|
|
174
488
|
'inset',
|
|
175
489
|
'outset',
|
|
176
490
|
]);
|
|
491
|
+
/** The largest font size, in CSS pixels (`font-size`). */
|
|
492
|
+
const MAX_FONT_SIZE = 10000;
|
|
177
493
|
const BORDER_WIDTH_KEYWORDS = {
|
|
178
494
|
thin: 1,
|
|
179
495
|
medium: 3,
|
|
@@ -192,6 +508,13 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
192
508
|
let value = rawValue.trim();
|
|
193
509
|
if (!value)
|
|
194
510
|
return;
|
|
511
|
+
// a logical property is the physical one it stands for, the CSS-wide
|
|
512
|
+
// keywords and a stray `!important` included, so it is read first
|
|
513
|
+
const logical = LOGICAL[name];
|
|
514
|
+
if (logical) {
|
|
515
|
+
applyLogical(style, parent, logical, value.replace(/\s*!\s*important$/i, ''), ctx);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
195
518
|
// CSS-wide keywords, before anything else parses the value.
|
|
196
519
|
const lower = value.toLowerCase();
|
|
197
520
|
if (lower === 'inherit') {
|
|
@@ -199,11 +522,18 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
199
522
|
return;
|
|
200
523
|
}
|
|
201
524
|
if (lower === 'initial' || lower === 'unset' || lower === 'revert') {
|
|
202
|
-
// `unset` is `inherit` for an inherited property and `initial` otherwise
|
|
203
|
-
// `
|
|
204
|
-
//
|
|
525
|
+
// `unset` is `inherit` for an inherited property and `initial` otherwise.
|
|
526
|
+
// `initial` is the value the property starts at, which an inherited one
|
|
527
|
+
// does not keep: left as it was, `line-height: initial` in a box inside
|
|
528
|
+
// a 200px line height was that line height still, and one after an
|
|
529
|
+
// earlier declaration of the property was that declaration. `revert`
|
|
530
|
+
// would be the UA sheet's value, and leaving the one the cascade has
|
|
531
|
+
// reached is the closest this gets without a third cascade level.
|
|
205
532
|
if (lower === 'unset' && isInherited(name))
|
|
206
533
|
inheritOne(style, parent, name);
|
|
534
|
+
else if (lower !== 'revert' && ctx.initial) {
|
|
535
|
+
inheritOne(style, ctx.initial, name);
|
|
536
|
+
}
|
|
207
537
|
return;
|
|
208
538
|
}
|
|
209
539
|
// `!important` is stripped by the parser; a stray one here is an author
|
|
@@ -216,10 +546,12 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
216
546
|
const parsed = parseColor(value);
|
|
217
547
|
if (parsed === null)
|
|
218
548
|
return;
|
|
219
|
-
// `
|
|
220
|
-
// means
|
|
221
|
-
if (name === 'color'
|
|
549
|
+
// `currentColor` in `color` is the one place the token refers to itself:
|
|
550
|
+
// it means the inherited colour, and so does one in a `color-mix()`
|
|
551
|
+
if (name === 'color') {
|
|
552
|
+
style.color = inkColor(parsed, parent.color);
|
|
222
553
|
return;
|
|
554
|
+
}
|
|
223
555
|
style[color] = parsed;
|
|
224
556
|
return;
|
|
225
557
|
}
|
|
@@ -229,12 +561,89 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
229
561
|
const v = value.toLowerCase();
|
|
230
562
|
if (v === 'inline-block' || v === 'inline-flex')
|
|
231
563
|
style.display = v;
|
|
564
|
+
// a grid container is a flex container to the box tree — its
|
|
565
|
+
// children are blockified items, and it holds its floats — and its
|
|
566
|
+
// own layout (`css-grid.ts`) to the engine
|
|
232
567
|
else if (v === 'grid')
|
|
233
|
-
style.display = '
|
|
568
|
+
style.display = 'flex';
|
|
234
569
|
else if (v === 'inline-grid')
|
|
235
|
-
style.display = 'inline-
|
|
570
|
+
style.display = 'inline-flex';
|
|
571
|
+
else if (v === 'flow-root')
|
|
572
|
+
style.display = 'block';
|
|
573
|
+
// the legacy flexible box, a flex box until `settleClamp` has read
|
|
574
|
+
// its orient, and the prefixed spelling of the modern one
|
|
575
|
+
else if (v === '-webkit-box' || v === '-webkit-flex') {
|
|
576
|
+
style.display = 'flex';
|
|
577
|
+
}
|
|
578
|
+
else if (v === '-webkit-inline-box' || v === '-webkit-inline-flex') {
|
|
579
|
+
style.display = 'inline-flex';
|
|
580
|
+
}
|
|
236
581
|
else if (DISPLAYS.has(v))
|
|
237
582
|
style.display = v;
|
|
583
|
+
else
|
|
584
|
+
return;
|
|
585
|
+
style.flowRoot = v === 'flow-root';
|
|
586
|
+
style.grid = v === 'grid' || v === 'inline-grid';
|
|
587
|
+
style.webkitBox =
|
|
588
|
+
v === '-webkit-box'
|
|
589
|
+
? 'block'
|
|
590
|
+
: v === '-webkit-inline-box'
|
|
591
|
+
? 'inline'
|
|
592
|
+
: null;
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
case '-webkit-box-orient': {
|
|
596
|
+
const v = value.toLowerCase();
|
|
597
|
+
if (v === 'horizontal' || v === 'inline-axis') {
|
|
598
|
+
style.webkitBoxOrient = 'horizontal';
|
|
599
|
+
}
|
|
600
|
+
else if (v === 'vertical' || v === 'block-axis') {
|
|
601
|
+
style.webkitBoxOrient = 'vertical';
|
|
602
|
+
}
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
case '-webkit-box-direction': {
|
|
606
|
+
const v = value.toLowerCase();
|
|
607
|
+
if (v === 'normal' || v === 'reverse')
|
|
608
|
+
style.webkitBoxDirection = v;
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
case '-webkit-box-pack': {
|
|
612
|
+
const v = BOX_PACK.get(value.trim().toLowerCase());
|
|
613
|
+
if (v)
|
|
614
|
+
style.webkitBoxPack = v;
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
case '-webkit-box-align': {
|
|
618
|
+
const v = BOX_ALIGN.get(value.trim().toLowerCase());
|
|
619
|
+
if (v)
|
|
620
|
+
style.webkitBoxAlign = v;
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
case 'column-count':
|
|
624
|
+
case 'column-width':
|
|
625
|
+
case 'columns': {
|
|
626
|
+
// `auto`, or a count, a width or both of them
|
|
627
|
+
const bit = name === 'column-count' ? 1 : name === 'column-width' ? 2 : 3;
|
|
628
|
+
let set = 0;
|
|
629
|
+
for (const part of splitValue(value)) {
|
|
630
|
+
const v = part.toLowerCase();
|
|
631
|
+
if (v === 'auto')
|
|
632
|
+
continue;
|
|
633
|
+
if (/^\d+$/.test(v) && Number(v) >= 1)
|
|
634
|
+
set |= 1;
|
|
635
|
+
else if (parseLength(part, ctx) !== null)
|
|
636
|
+
set |= 2;
|
|
637
|
+
else
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
style.columns = (style.columns & ~bit) | (set & bit);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
case '-webkit-box-flex': {
|
|
644
|
+
const n = parseNumber(value);
|
|
645
|
+
if (n !== null && n >= 0)
|
|
646
|
+
style.webkitBoxFlex = n;
|
|
238
647
|
return;
|
|
239
648
|
}
|
|
240
649
|
case 'position': {
|
|
@@ -264,6 +673,100 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
264
673
|
const v = value.toLowerCase();
|
|
265
674
|
if (v === 'left' || v === 'right' || v === 'both' || v === 'none')
|
|
266
675
|
style.clear = v;
|
|
676
|
+
else if (v === 'inline-start')
|
|
677
|
+
style.clear = style.direction === 'rtl' ? 'right' : 'left';
|
|
678
|
+
else if (v === 'inline-end')
|
|
679
|
+
style.clear = style.direction === 'rtl' ? 'left' : 'right';
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
case 'line-clamp': {
|
|
683
|
+
// `none`, or a count of lines, an ellipsis or both, and
|
|
684
|
+
// `-webkit-legacy` (CSS Overflow 4, 5.1): the ellipsis a string
|
|
685
|
+
// names is drawn as the one `auto` draws
|
|
686
|
+
const parts = splitValue(value);
|
|
687
|
+
if (parts.length === 1 && parts[0].toLowerCase() === 'none') {
|
|
688
|
+
style.maxLines = null;
|
|
689
|
+
style.clampContinue = 'auto';
|
|
690
|
+
style.blockEllipsis = false;
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
let lines = null;
|
|
694
|
+
let ellipsis = null;
|
|
695
|
+
let legacy = false;
|
|
696
|
+
for (const part of parts) {
|
|
697
|
+
const v = part.toLowerCase();
|
|
698
|
+
if (/^\d+$/.test(v) && lines === null)
|
|
699
|
+
lines = Number(v);
|
|
700
|
+
else if (v === '-webkit-legacy' && !legacy)
|
|
701
|
+
legacy = true;
|
|
702
|
+
else if (ellipsis === null && (v === 'auto' || v === 'no-ellipsis')) {
|
|
703
|
+
ellipsis = v === 'auto';
|
|
704
|
+
}
|
|
705
|
+
else if (ellipsis === null && /^(["']).*\1$/s.test(part)) {
|
|
706
|
+
ellipsis = part.length > 2;
|
|
707
|
+
}
|
|
708
|
+
else
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
if (lines === 0 || (lines === null && ellipsis === null))
|
|
712
|
+
return;
|
|
713
|
+
style.maxLines = lines;
|
|
714
|
+
style.clampContinue = legacy ? 'legacy' : 'collapse';
|
|
715
|
+
style.blockEllipsis = ellipsis ?? true;
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
case '-webkit-line-clamp': {
|
|
719
|
+
// `line-clamp` as it was, which clamps only a vertical `-webkit-box`
|
|
720
|
+
// (5.1.1) and always with an ellipsis
|
|
721
|
+
const v = value.trim().toLowerCase();
|
|
722
|
+
if (v === 'none') {
|
|
723
|
+
style.maxLines = null;
|
|
724
|
+
style.clampContinue = 'auto';
|
|
725
|
+
style.blockEllipsis = true;
|
|
726
|
+
}
|
|
727
|
+
else if (/^\d+$/.test(v) && Number(v) >= 1) {
|
|
728
|
+
style.maxLines = Number(v);
|
|
729
|
+
style.clampContinue = 'legacy';
|
|
730
|
+
style.blockEllipsis = true;
|
|
731
|
+
}
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
case 'max-lines': {
|
|
735
|
+
const v = value.trim().toLowerCase();
|
|
736
|
+
if (v === 'none')
|
|
737
|
+
style.maxLines = null;
|
|
738
|
+
else if (/^\d+$/.test(v) && Number(v) >= 1)
|
|
739
|
+
style.maxLines = Number(v);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
case 'continue': {
|
|
743
|
+
const v = value.trim().toLowerCase();
|
|
744
|
+
if (v === 'auto')
|
|
745
|
+
style.clampContinue = 'auto';
|
|
746
|
+
else if (v === 'collapse' || v === 'discard') {
|
|
747
|
+
style.clampContinue = 'collapse';
|
|
748
|
+
}
|
|
749
|
+
else if (v === '-webkit-legacy')
|
|
750
|
+
style.clampContinue = 'legacy';
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
case 'block-ellipsis': {
|
|
754
|
+
const v = value.trim();
|
|
755
|
+
const lower = v.toLowerCase();
|
|
756
|
+
if (lower === 'auto' || lower === 'no-ellipsis') {
|
|
757
|
+
style.blockEllipsis = lower === 'auto';
|
|
758
|
+
}
|
|
759
|
+
else if (/^(["']).*\1$/s.test(v))
|
|
760
|
+
style.blockEllipsis = v.length > 2;
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
case 'text-overflow': {
|
|
764
|
+
// one value for the end a line is cut at, or two, start and end
|
|
765
|
+
const parts = splitValue(value).map((p) => p.toLowerCase());
|
|
766
|
+
const end = parts[parts.length - 1];
|
|
767
|
+
if (parts.length <= 2 && (end === 'clip' || end === 'ellipsis')) {
|
|
768
|
+
style.textOverflow = end;
|
|
769
|
+
}
|
|
267
770
|
return;
|
|
268
771
|
}
|
|
269
772
|
case 'box-sizing': {
|
|
@@ -272,6 +775,111 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
272
775
|
style.boxSizing = v;
|
|
273
776
|
return;
|
|
274
777
|
}
|
|
778
|
+
case 'aspect-ratio': {
|
|
779
|
+
const parsed = parseAspectRatio(value);
|
|
780
|
+
if (parsed !== undefined)
|
|
781
|
+
style.aspectRatio = parsed;
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
case 'object-fit': {
|
|
785
|
+
const v = value.trim().toLowerCase();
|
|
786
|
+
if (OBJECT_FITS.has(v))
|
|
787
|
+
style.objectFit = v;
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
case 'appearance':
|
|
791
|
+
case '-webkit-appearance':
|
|
792
|
+
case '-moz-appearance': {
|
|
793
|
+
// every keyword but `none` is some control's own look, which is the
|
|
794
|
+
// only other thing this engine has
|
|
795
|
+
const v = value.trim().toLowerCase();
|
|
796
|
+
if (/^[a-z-]+$/.test(v))
|
|
797
|
+
style.appearance = v === 'none' ? 'none' : 'auto';
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
case 'contain': {
|
|
801
|
+
const bits = containOf(value);
|
|
802
|
+
if (bits !== null)
|
|
803
|
+
style.contain = bits;
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
case 'overflow-clip-margin': {
|
|
807
|
+
let box = null;
|
|
808
|
+
let margin = null;
|
|
809
|
+
// a length that shrinks the box where it is negative (CSS Overflow 4)
|
|
810
|
+
for (const part of splitValue(value)) {
|
|
811
|
+
const word = part.toLowerCase();
|
|
812
|
+
if (box === null && isVisualBox(word)) {
|
|
813
|
+
box = word;
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
const len = parseLength(part, ctx);
|
|
817
|
+
if (margin !== null || typeof len !== 'number')
|
|
818
|
+
return;
|
|
819
|
+
margin = len;
|
|
820
|
+
}
|
|
821
|
+
if (box === null && margin === null)
|
|
822
|
+
return;
|
|
823
|
+
style.overflowClipBox = box ?? 'padding-box';
|
|
824
|
+
style.overflowClipMargin = margin ?? 0;
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
case 'content-visibility': {
|
|
828
|
+
const v = value.trim().toLowerCase();
|
|
829
|
+
if (v === 'visible' || v === 'auto' || v === 'hidden') {
|
|
830
|
+
style.contentVisibility = v;
|
|
831
|
+
}
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
case 'contain-intrinsic-size':
|
|
835
|
+
case 'contain-intrinsic-width':
|
|
836
|
+
case 'contain-intrinsic-height':
|
|
837
|
+
case 'contain-intrinsic-inline-size':
|
|
838
|
+
case 'contain-intrinsic-block-size': {
|
|
839
|
+
const parts = splitValue(value);
|
|
840
|
+
const sizes = [];
|
|
841
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
842
|
+
const word = parts[i].toLowerCase();
|
|
843
|
+
// `auto` beside a length remembers a size last laid out, which a
|
|
844
|
+
// document that does not change has not got: the length
|
|
845
|
+
if (word === 'auto' && i + 1 < parts.length)
|
|
846
|
+
continue;
|
|
847
|
+
if (word === 'none') {
|
|
848
|
+
sizes.push(null);
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
const len = parseLength(parts[i], ctx);
|
|
852
|
+
if (typeof len !== 'number' || len < 0)
|
|
853
|
+
return;
|
|
854
|
+
sizes.push(len);
|
|
855
|
+
}
|
|
856
|
+
if (!sizes.length || sizes.length > 2)
|
|
857
|
+
return;
|
|
858
|
+
const across = name === 'contain-intrinsic-size' ||
|
|
859
|
+
name === 'contain-intrinsic-width' ||
|
|
860
|
+
name === 'contain-intrinsic-inline-size';
|
|
861
|
+
const down = name !== 'contain-intrinsic-width' &&
|
|
862
|
+
name !== 'contain-intrinsic-inline-size';
|
|
863
|
+
if (name === 'contain-intrinsic-size') {
|
|
864
|
+
style.containIntrinsicWidth = sizes[0];
|
|
865
|
+
style.containIntrinsicHeight = sizes.length > 1 ? sizes[1] : sizes[0];
|
|
866
|
+
}
|
|
867
|
+
else if (sizes.length === 1) {
|
|
868
|
+
if (across)
|
|
869
|
+
style.containIntrinsicWidth = sizes[0];
|
|
870
|
+
else if (down)
|
|
871
|
+
style.containIntrinsicHeight = sizes[0];
|
|
872
|
+
}
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
case 'object-position': {
|
|
876
|
+
const pair = positionPair(splitValue(value), ctx);
|
|
877
|
+
if (!pair)
|
|
878
|
+
return;
|
|
879
|
+
style.objectPositionX = pair[0];
|
|
880
|
+
style.objectPositionY = pair[1];
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
275
883
|
case 'overflow':
|
|
276
884
|
case 'overflow-x':
|
|
277
885
|
case 'overflow-y': {
|
|
@@ -284,27 +892,60 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
284
892
|
style.overflowY = y;
|
|
285
893
|
return;
|
|
286
894
|
}
|
|
895
|
+
case 'clip': {
|
|
896
|
+
const clip = parseClip(value, ctx);
|
|
897
|
+
if (clip !== undefined)
|
|
898
|
+
style.clip = clip;
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
287
901
|
case 'opacity': {
|
|
288
902
|
const a = parseAlpha(value);
|
|
289
903
|
if (a !== null)
|
|
290
904
|
style.opacity = a;
|
|
291
905
|
return;
|
|
292
906
|
}
|
|
907
|
+
case 'translate': {
|
|
908
|
+
// `none`, or across, down, and a depth nothing here has
|
|
909
|
+
if (value.trim().toLowerCase() === 'none') {
|
|
910
|
+
style.translate = null;
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
const parts = splitValue(value);
|
|
914
|
+
if (parts.length > 3)
|
|
915
|
+
return;
|
|
916
|
+
const [x, y, z] = parts.map((p) => parseLength(p, ctx));
|
|
917
|
+
if (x == null || x === AUTO || y === null || y === AUTO)
|
|
918
|
+
return;
|
|
919
|
+
if (z !== undefined && typeof z !== 'number')
|
|
920
|
+
return;
|
|
921
|
+
style.translate = [x, y ?? 0];
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
case 'transform': {
|
|
925
|
+
const moved = transformTranslation(value, ctx);
|
|
926
|
+
if (moved !== undefined)
|
|
927
|
+
style.transformTranslate = moved;
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
293
930
|
case 'visibility': {
|
|
294
931
|
const v = value.toLowerCase();
|
|
295
|
-
if (v === 'hidden' || v === 'collapse')
|
|
296
|
-
style.visibility =
|
|
297
|
-
|
|
298
|
-
style.visibility = 'visible';
|
|
932
|
+
if (v === 'hidden' || v === 'collapse' || v === 'visible') {
|
|
933
|
+
style.visibility = v;
|
|
934
|
+
}
|
|
299
935
|
return;
|
|
300
936
|
}
|
|
301
937
|
case 'z-index': {
|
|
302
|
-
|
|
938
|
+
// an integer: `1.5` is none, and a `calc()` is rounded to one, half
|
|
939
|
+
// up (CSS Values 4 10.9)
|
|
940
|
+
const v = value.trim().toLowerCase();
|
|
941
|
+
if (v === 'auto')
|
|
303
942
|
style.zIndex = AUTO;
|
|
304
|
-
else
|
|
305
|
-
|
|
943
|
+
else if (/^[+-]?\d+$/.test(v))
|
|
944
|
+
style.zIndex = Number(v);
|
|
945
|
+
else if (/^[a-z-]+\(/.test(v)) {
|
|
946
|
+
const n = parseNumber(v);
|
|
306
947
|
if (n !== null)
|
|
307
|
-
style.zIndex = Math.
|
|
948
|
+
style.zIndex = Math.floor(n + 0.5);
|
|
308
949
|
}
|
|
309
950
|
return;
|
|
310
951
|
}
|
|
@@ -315,12 +956,16 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
315
956
|
v === 'middle' ||
|
|
316
957
|
v === 'bottom' ||
|
|
317
958
|
v === 'sub' ||
|
|
318
|
-
v === 'super'
|
|
959
|
+
v === 'super' ||
|
|
960
|
+
v === 'text-top' ||
|
|
961
|
+
v === 'text-bottom') {
|
|
319
962
|
style.verticalAlign = v;
|
|
320
963
|
}
|
|
321
964
|
else {
|
|
965
|
+
// a percentage is of the element's own line height, which layout
|
|
966
|
+
// knows
|
|
322
967
|
const len = parseLength(value, ctx);
|
|
323
|
-
if (
|
|
968
|
+
if (len !== null && len !== AUTO)
|
|
324
969
|
style.verticalAlign = len;
|
|
325
970
|
}
|
|
326
971
|
return;
|
|
@@ -330,20 +975,55 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
330
975
|
case 'height':
|
|
331
976
|
case 'min-width':
|
|
332
977
|
case 'min-height': {
|
|
978
|
+
const keyword = contentSizeOf(value, ctx, name.endsWith('width'));
|
|
979
|
+
if (keyword) {
|
|
980
|
+
// `auto` beside it, which is a height's content height already
|
|
981
|
+
style[camel(name)] = AUTO;
|
|
982
|
+
if (name === 'width')
|
|
983
|
+
style.widthKeyword = keyword;
|
|
984
|
+
else if (name === 'min-width')
|
|
985
|
+
style.minWidthKeyword = keyword;
|
|
986
|
+
else if (name === 'min-height')
|
|
987
|
+
style.minHeightKeyword = keyword;
|
|
988
|
+
else
|
|
989
|
+
style.heightKeyword = keyword;
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
// a negative size is no value, and the declaration goes (CSS 2.1
|
|
993
|
+
// 10.2, 10.4, 10.5, 10.7)
|
|
333
994
|
const len = parseLength(value, ctx);
|
|
334
|
-
if (len !== null)
|
|
995
|
+
if (len !== null && notNegative(len)) {
|
|
335
996
|
style[camel(name)] = len;
|
|
997
|
+
if (name === 'width')
|
|
998
|
+
style.widthKeyword = null;
|
|
999
|
+
else if (name === 'min-width')
|
|
1000
|
+
style.minWidthKeyword = null;
|
|
1001
|
+
else if (name === 'min-height')
|
|
1002
|
+
style.minHeightKeyword = null;
|
|
1003
|
+
else
|
|
1004
|
+
style.heightKeyword = null;
|
|
1005
|
+
}
|
|
336
1006
|
return;
|
|
337
1007
|
}
|
|
338
1008
|
case 'max-width':
|
|
339
1009
|
case 'max-height': {
|
|
340
|
-
|
|
1010
|
+
const keyword = contentSizeOf(value, ctx, name === 'max-width');
|
|
1011
|
+
if (keyword || value.toLowerCase() === 'none') {
|
|
341
1012
|
style[camel(name)] = 'none';
|
|
1013
|
+
if (name === 'max-width')
|
|
1014
|
+
style.maxWidthKeyword = keyword;
|
|
1015
|
+
else
|
|
1016
|
+
style.maxHeightKeyword = keyword;
|
|
342
1017
|
return;
|
|
343
1018
|
}
|
|
344
1019
|
const len = parseLength(value, ctx);
|
|
345
|
-
if (len !== null)
|
|
1020
|
+
if (len !== null && notNegative(len)) {
|
|
346
1021
|
style[camel(name)] = len;
|
|
1022
|
+
if (name === 'max-width')
|
|
1023
|
+
style.maxWidthKeyword = null;
|
|
1024
|
+
else
|
|
1025
|
+
style.maxHeightKeyword = null;
|
|
1026
|
+
}
|
|
347
1027
|
return;
|
|
348
1028
|
}
|
|
349
1029
|
case 'top':
|
|
@@ -355,18 +1035,20 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
355
1035
|
style[name] = len;
|
|
356
1036
|
return;
|
|
357
1037
|
}
|
|
1038
|
+
case 'inset':
|
|
358
1039
|
case 'margin':
|
|
359
1040
|
case 'padding': {
|
|
360
1041
|
const keys = SIDE_PROPS[name];
|
|
361
1042
|
const parts = splitValue(value).map((p) => parseLength(p, ctx));
|
|
362
1043
|
if (parts.some((p) => p === null))
|
|
363
1044
|
return;
|
|
1045
|
+
// padding is never `auto` and never negative: a value that is either
|
|
1046
|
+
// is no value, and the declaration goes (CSS 2.1 8.4)
|
|
1047
|
+
if (name === 'padding' && !parts.every(validPadding))
|
|
1048
|
+
return;
|
|
364
1049
|
const sides = fourSides(parts);
|
|
365
|
-
// `padding: auto` is not a thing; a stray one computes to zero rather
|
|
366
|
-
// than making layout branch on an impossible value.
|
|
367
1050
|
for (let i = 0; i < 4; i += 1) {
|
|
368
|
-
|
|
369
|
-
style[keys[i]] = v;
|
|
1051
|
+
style[keys[i]] = sides[i];
|
|
370
1052
|
}
|
|
371
1053
|
return;
|
|
372
1054
|
}
|
|
@@ -381,9 +1063,9 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
381
1063
|
const len = parseLength(value, ctx);
|
|
382
1064
|
if (len === null)
|
|
383
1065
|
return;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
1066
|
+
if (name.startsWith('padding') && !validPadding(len))
|
|
1067
|
+
return;
|
|
1068
|
+
style[camel(name)] = len;
|
|
387
1069
|
return;
|
|
388
1070
|
}
|
|
389
1071
|
// --- borders ------------------------------------------------------------
|
|
@@ -446,21 +1128,104 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
446
1128
|
style[camel(name)] = v;
|
|
447
1129
|
return;
|
|
448
1130
|
}
|
|
1131
|
+
case 'border-top-left-radius':
|
|
1132
|
+
case 'border-top-right-radius':
|
|
1133
|
+
case 'border-bottom-right-radius':
|
|
1134
|
+
case 'border-bottom-left-radius': {
|
|
1135
|
+
// a radius, or a horizontal one and a vertical one
|
|
1136
|
+
const parts = splitValue(value).map((p) => radiusOf(p, ctx));
|
|
1137
|
+
if (!parts.length || parts.length > 2 || parts.includes(null))
|
|
1138
|
+
return;
|
|
1139
|
+
const [x, y = x] = parts;
|
|
1140
|
+
const corner = CORNERS.indexOf(name);
|
|
1141
|
+
const radii = style.borderRadius.slice();
|
|
1142
|
+
const vertical = (style.borderRadiusY ?? style.borderRadius).slice();
|
|
1143
|
+
radii[corner] = x;
|
|
1144
|
+
vertical[corner] = y;
|
|
1145
|
+
style.borderRadius = radii;
|
|
1146
|
+
style.borderRadiusY = sameRadii(radii, vertical) ? null : vertical;
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
case 'outline': {
|
|
1150
|
+
// width, style and colour in any order, each reset where it is left
|
|
1151
|
+
// out, as `border`'s are
|
|
1152
|
+
let width = BORDER_WIDTH_KEYWORDS.medium * ctx.scale;
|
|
1153
|
+
let outline = 'none';
|
|
1154
|
+
let color = 'currentColor';
|
|
1155
|
+
for (const part of splitValue(value)) {
|
|
1156
|
+
const v = part.toLowerCase();
|
|
1157
|
+
const kind = outlineStyleOf(v);
|
|
1158
|
+
if (kind) {
|
|
1159
|
+
outline = kind;
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
const w = borderWidth(part, ctx);
|
|
1163
|
+
if (w !== null) {
|
|
1164
|
+
width = w;
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
const c = v === 'invert' ? 'currentColor' : parseColor(part);
|
|
1168
|
+
if (c === null)
|
|
1169
|
+
return;
|
|
1170
|
+
color = c;
|
|
1171
|
+
}
|
|
1172
|
+
style.outlineStyle = outline;
|
|
1173
|
+
style.outlineWidth = width;
|
|
1174
|
+
style.outlineColor = color;
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
case 'outline-style': {
|
|
1178
|
+
const kind = outlineStyleOf(value.trim().toLowerCase());
|
|
1179
|
+
if (kind)
|
|
1180
|
+
style.outlineStyle = kind;
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
case 'outline-width': {
|
|
1184
|
+
const w = borderWidth(value, ctx);
|
|
1185
|
+
if (w !== null)
|
|
1186
|
+
style.outlineWidth = w;
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
case 'outline-color': {
|
|
1190
|
+
// `invert` is the text's colour where a browser cannot invert
|
|
1191
|
+
const v = value.trim().toLowerCase();
|
|
1192
|
+
const c = v === 'invert' ? 'currentColor' : parseColor(value);
|
|
1193
|
+
if (c !== null)
|
|
1194
|
+
style.outlineColor = c;
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
case 'outline-offset': {
|
|
1198
|
+
const len = parseLength(value, ctx);
|
|
1199
|
+
if (typeof len === 'number')
|
|
1200
|
+
style.outlineOffset = len;
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
case 'box-shadow': {
|
|
1204
|
+
const shadows = parseBoxShadow(value, ctx);
|
|
1205
|
+
if (shadows !== undefined)
|
|
1206
|
+
style.boxShadow = shadows;
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
case 'text-shadow': {
|
|
1210
|
+
const shadows = parseBoxShadow(value, ctx, true);
|
|
1211
|
+
if (shadows !== undefined)
|
|
1212
|
+
style.textShadow = shadows;
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
449
1215
|
case 'border-radius': {
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
const len = parseLength(p, ctx);
|
|
455
|
-
return typeof len === 'number'
|
|
456
|
-
? len
|
|
457
|
-
: len && typeof len === 'object'
|
|
458
|
-
? 0
|
|
459
|
-
: null;
|
|
460
|
-
});
|
|
461
|
-
if (parts.some((p) => p === null))
|
|
1216
|
+
// one to four radii, and after a `/` one to four more for the
|
|
1217
|
+
// vertical radii where they differ: elliptical corners
|
|
1218
|
+
const halves = value.split('/');
|
|
1219
|
+
if (halves.length > 2)
|
|
462
1220
|
return;
|
|
463
|
-
|
|
1221
|
+
const [horizontal, vertical] = halves.map((half) => splitValue(half).map((p) => radiusOf(p, ctx)));
|
|
1222
|
+
for (const parts of vertical ? [horizontal, vertical] : [horizontal]) {
|
|
1223
|
+
if (!parts.length || parts.length > 4 || parts.includes(null))
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
style.borderRadius = fourSides(horizontal);
|
|
1227
|
+
const y = vertical ? fourSides(vertical) : null;
|
|
1228
|
+
style.borderRadiusY = y && !sameRadii(style.borderRadius, y) ? y : null;
|
|
464
1229
|
return;
|
|
465
1230
|
}
|
|
466
1231
|
// --- background ---------------------------------------------------------
|
|
@@ -469,33 +1234,166 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
469
1234
|
return;
|
|
470
1235
|
}
|
|
471
1236
|
case 'background-image': {
|
|
472
|
-
|
|
1237
|
+
// one a layer; a bad url makes the declaration invalid, and it is
|
|
1238
|
+
// dropped
|
|
1239
|
+
const images = [];
|
|
1240
|
+
for (const part of splitCommas(value)) {
|
|
1241
|
+
const image = backgroundImageOf(part, ctx);
|
|
1242
|
+
if (image === undefined)
|
|
1243
|
+
return;
|
|
1244
|
+
images.push(image);
|
|
1245
|
+
}
|
|
1246
|
+
if (!images.length)
|
|
1247
|
+
return;
|
|
1248
|
+
const [first] = images;
|
|
1249
|
+
style.backgroundImage = typeof first === 'string' ? first : null;
|
|
1250
|
+
style.backgroundGradient = typeof first === 'string' ? null : first;
|
|
1251
|
+
style.backgroundImages = images.length > 1 ? images : null;
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
case 'background-clip':
|
|
1255
|
+
case '-webkit-background-clip': {
|
|
1256
|
+
// `text` the first layer's alone, which paints through the text —
|
|
1257
|
+
// and with `border-area` through its border as well, in either order
|
|
1258
|
+
// (CSS Backgrounds 4, 2.1)
|
|
1259
|
+
const clips = layerValues(value, (part) => {
|
|
1260
|
+
const v = part.toLowerCase();
|
|
1261
|
+
if (v === 'text' || isBackgroundClip(v))
|
|
1262
|
+
return v;
|
|
1263
|
+
const [a, b, more] = splitValue(v);
|
|
1264
|
+
return !more &&
|
|
1265
|
+
((a === 'border-area' && b === 'text') ||
|
|
1266
|
+
(a === 'text' && b === 'border-area'))
|
|
1267
|
+
? 'border-area text'
|
|
1268
|
+
: null;
|
|
1269
|
+
});
|
|
1270
|
+
if (!clips)
|
|
1271
|
+
return;
|
|
1272
|
+
style.backgroundClipText =
|
|
1273
|
+
clips[0] === 'text' || clips[0] === 'border-area text';
|
|
1274
|
+
const boxes = clips.map((c) => c === 'text'
|
|
1275
|
+
? 'border-box'
|
|
1276
|
+
: c === 'border-area text'
|
|
1277
|
+
? 'border-area'
|
|
1278
|
+
: c);
|
|
1279
|
+
style.backgroundClip = boxes[0];
|
|
1280
|
+
style.backgroundClips = boxes.length > 1 ? boxes : null;
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
case 'border-image': {
|
|
1284
|
+
const image = readBorderImage(value, ctx);
|
|
1285
|
+
if (image)
|
|
1286
|
+
style.borderImage = image;
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
case 'border-image-source': {
|
|
1290
|
+
const source = backgroundImageOf(value, ctx);
|
|
1291
|
+
if (source === undefined)
|
|
1292
|
+
return;
|
|
1293
|
+
style.borderImage = { ...style.borderImage, source };
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
case 'border-image-slice': {
|
|
1297
|
+
const slice = borderImageSlice(splitValue(value));
|
|
1298
|
+
if (slice)
|
|
1299
|
+
style.borderImage = { ...style.borderImage, ...slice };
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
case 'border-image-width': {
|
|
1303
|
+
const width = sidesOf(splitValue(value), (p) => imageWidthOf(p, ctx));
|
|
1304
|
+
if (width)
|
|
1305
|
+
style.borderImage = { ...style.borderImage, width };
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
case 'border-image-outset': {
|
|
1309
|
+
const outset = sidesOf(splitValue(value), (p) => imageOutsetOf(p, ctx));
|
|
1310
|
+
if (outset)
|
|
1311
|
+
style.borderImage = { ...style.borderImage, outset };
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
case 'border-image-repeat': {
|
|
1315
|
+
const repeat = imageRepeatOf(splitValue(value.toLowerCase()));
|
|
1316
|
+
if (repeat)
|
|
1317
|
+
style.borderImage = { ...style.borderImage, repeat };
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
case 'background-origin': {
|
|
1321
|
+
const origins = layerValues(value, (part) => {
|
|
1322
|
+
const v = part.toLowerCase();
|
|
1323
|
+
return isVisualBox(v) ? v : null;
|
|
1324
|
+
});
|
|
1325
|
+
if (!origins)
|
|
1326
|
+
return;
|
|
1327
|
+
style.backgroundOrigin = origins[0];
|
|
1328
|
+
style.backgroundOrigins = origins.length > 1 ? origins : null;
|
|
473
1329
|
return;
|
|
474
1330
|
}
|
|
475
1331
|
case 'background-repeat': {
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
1332
|
+
const repeats = layerValues(value, (part) => {
|
|
1333
|
+
const words = splitValue(part.toLowerCase());
|
|
1334
|
+
return words.length <= 2 ? readRepeat(words) : null;
|
|
1335
|
+
});
|
|
1336
|
+
if (!repeats)
|
|
1337
|
+
return;
|
|
1338
|
+
style.backgroundRepeat = repeats[0];
|
|
1339
|
+
style.backgroundRepeats = repeats.length > 1 ? repeats : null;
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
case 'background-attachment': {
|
|
1343
|
+
const attachments = layerValues(value, (part) => {
|
|
1344
|
+
const v = part.toLowerCase();
|
|
1345
|
+
return v === 'scroll' || v === 'fixed' || v === 'local' ? v : null;
|
|
1346
|
+
});
|
|
1347
|
+
if (!attachments)
|
|
1348
|
+
return;
|
|
1349
|
+
style.backgroundAttachment = attachments[0];
|
|
1350
|
+
style.backgroundAttachments = attachments.length > 1 ? attachments : null;
|
|
483
1351
|
return;
|
|
484
1352
|
}
|
|
485
1353
|
case 'background-size': {
|
|
486
|
-
const
|
|
487
|
-
if (
|
|
488
|
-
|
|
1354
|
+
const sizes = layerValues(value, (part) => backgroundSizeOf(splitValue(part), ctx));
|
|
1355
|
+
if (!sizes)
|
|
1356
|
+
return;
|
|
1357
|
+
style.backgroundSize = sizes[0];
|
|
1358
|
+
style.backgroundSizes = sizes.length > 1 ? sizes : null;
|
|
489
1359
|
return;
|
|
490
1360
|
}
|
|
491
1361
|
case 'background-position': {
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
1362
|
+
const pairs = layerValues(value, (part) => positionPair(splitValue(part), ctx));
|
|
1363
|
+
if (!pairs)
|
|
1364
|
+
return;
|
|
1365
|
+
[style.backgroundPositionX, style.backgroundPositionY] = pairs[0];
|
|
1366
|
+
style.backgroundPositions = pairs.length > 1 ? pairs : null;
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
// --- generated content --------------------------------------------------
|
|
1370
|
+
case 'content': {
|
|
1371
|
+
const parsed = parseContent(value);
|
|
1372
|
+
if (parsed !== null)
|
|
1373
|
+
style.content = parsed;
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
case 'counter-reset':
|
|
1377
|
+
case 'counter-increment':
|
|
1378
|
+
case 'counter-set': {
|
|
1379
|
+
const reset = name === 'counter-reset';
|
|
1380
|
+
const increment = name === 'counter-increment';
|
|
1381
|
+
const parsed = parseCounterList(value, increment ? 1 : 0, reset);
|
|
1382
|
+
if (parsed === null)
|
|
1383
|
+
return;
|
|
1384
|
+
const list = parsed === 'none' ? null : parsed;
|
|
1385
|
+
if (reset)
|
|
1386
|
+
style.counterReset = list;
|
|
1387
|
+
else if (increment)
|
|
1388
|
+
style.counterIncrement = list;
|
|
1389
|
+
else
|
|
1390
|
+
style.counterSet = list;
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
case 'quotes': {
|
|
1394
|
+
const parsed = parseQuotes(value);
|
|
1395
|
+
if (parsed !== null)
|
|
1396
|
+
style.quotes = parsed;
|
|
499
1397
|
return;
|
|
500
1398
|
}
|
|
501
1399
|
// --- text ---------------------------------------------------------------
|
|
@@ -505,26 +1403,48 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
505
1403
|
}
|
|
506
1404
|
case 'font-family': {
|
|
507
1405
|
// ntk's font matcher takes the CSS list as written and walks it, so the
|
|
508
|
-
// value passes through whole rather than being resolved here
|
|
509
|
-
|
|
510
|
-
|
|
1406
|
+
// value passes through whole rather than being resolved here — once
|
|
1407
|
+
// every name in it is one: a string, or identifiers (CSS 2.1 15.3).
|
|
1408
|
+
// `test!foo, Ahem` is no list, and set Ahem.
|
|
1409
|
+
const names = splitCommas(value);
|
|
1410
|
+
if (!names.every(isFamilyName))
|
|
1411
|
+
return;
|
|
1412
|
+
// and an unquoted name is its identifiers joined by one space each,
|
|
1413
|
+
// however they were spaced: `Courier New` over two lines is
|
|
1414
|
+
// `Courier New`, and was a name no font has
|
|
1415
|
+
const list = names
|
|
1416
|
+
.map((f) => /^['"]/.test(f)
|
|
1417
|
+
? f.replace(/^['"]|['"]$/g, '')
|
|
1418
|
+
: f.trim().replace(/[ \t\n\r\f]+/g, ' '))
|
|
511
1419
|
.filter(Boolean)
|
|
512
1420
|
.join(', ');
|
|
1421
|
+
style.fontFamily = ctx.families ? ctx.families(list) : list;
|
|
513
1422
|
return;
|
|
514
1423
|
}
|
|
515
1424
|
case 'font-size': {
|
|
1425
|
+
// No larger than 10,000 pixels, as Chrome keeps it: a text engine
|
|
1426
|
+
// handed a face at a size of millions shapes and caches glyphs that
|
|
1427
|
+
// size, and a nest of `larger`s gets there on its own
|
|
1428
|
+
const most = MAX_FONT_SIZE * ctx.scale;
|
|
516
1429
|
const kw = keywordFontSize(value, parent.fontSize, ctx.rem);
|
|
517
1430
|
if (kw !== null) {
|
|
518
|
-
style.fontSize = kw;
|
|
1431
|
+
style.fontSize = Math.min(kw, most);
|
|
519
1432
|
return;
|
|
520
1433
|
}
|
|
521
1434
|
// `em` in a `font-size` is relative to the *parent's* size, not this
|
|
522
1435
|
// element's — the one place the unit context has to be overridden.
|
|
1436
|
+
// 0 is a size — the text takes no room, which is what a container of
|
|
1437
|
+
// inline-blocks sets to lose the spaces between them — and a negative
|
|
1438
|
+
// one is no size at all, so the declaration goes
|
|
523
1439
|
const len = parseLength(value, { ...ctx, em: parent.fontSize });
|
|
524
|
-
if (typeof len === 'number')
|
|
525
|
-
|
|
1440
|
+
if (typeof len === 'number') {
|
|
1441
|
+
if (len >= 0)
|
|
1442
|
+
style.fontSize = Math.min(len, most);
|
|
1443
|
+
}
|
|
526
1444
|
else if (len && typeof len === 'object') {
|
|
527
|
-
|
|
1445
|
+
const size = resolve(len, parent.fontSize);
|
|
1446
|
+
if (size >= 0)
|
|
1447
|
+
style.fontSize = Math.min(size, most);
|
|
528
1448
|
}
|
|
529
1449
|
return;
|
|
530
1450
|
}
|
|
@@ -545,19 +1465,28 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
545
1465
|
style.lineHeightIsLength = false;
|
|
546
1466
|
return;
|
|
547
1467
|
}
|
|
1468
|
+
// none of it below nought, which drops the declaration (CSS 2.1
|
|
1469
|
+
// 10.8.1)
|
|
548
1470
|
const n = parseNumber(value);
|
|
549
1471
|
if (n !== null) {
|
|
1472
|
+
if (n < 0)
|
|
1473
|
+
return;
|
|
550
1474
|
style.lineHeight = n;
|
|
551
1475
|
style.lineHeightIsLength = false;
|
|
552
1476
|
return;
|
|
553
1477
|
}
|
|
554
1478
|
const len = parseLength(value, ctx);
|
|
555
1479
|
if (typeof len === 'number') {
|
|
1480
|
+
if (len < 0)
|
|
1481
|
+
return;
|
|
556
1482
|
style.lineHeight = len;
|
|
557
1483
|
style.lineHeightIsLength = true;
|
|
558
1484
|
}
|
|
559
1485
|
else if (len && typeof len === 'object') {
|
|
560
|
-
|
|
1486
|
+
const px = resolve(len, style.fontSize);
|
|
1487
|
+
if (px < 0)
|
|
1488
|
+
return;
|
|
1489
|
+
style.lineHeight = px;
|
|
561
1490
|
style.lineHeightIsLength = true;
|
|
562
1491
|
}
|
|
563
1492
|
return;
|
|
@@ -571,10 +1500,58 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
571
1500
|
v === 'start' ||
|
|
572
1501
|
v === 'end') {
|
|
573
1502
|
style.textAlign = v;
|
|
1503
|
+
style.alignBlocks = null;
|
|
1504
|
+
style.tableTextAlignSet = true;
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
// every line justified, the last as well (CSS Text 3, 7.1)
|
|
1508
|
+
if (v === 'justify-all') {
|
|
1509
|
+
style.textAlign = 'justify';
|
|
1510
|
+
style.textAlignLast = 'justify';
|
|
1511
|
+
style.alignBlocks = null;
|
|
1512
|
+
style.tableTextAlignSet = true;
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
// the value HTML's alignment is given as, the blocks inside aligned
|
|
1516
|
+
// with the text — and which mail writes for itself
|
|
1517
|
+
const aligned = /^-(?:webkit|moz|khtml)-(left|right|center)$/.exec(v);
|
|
1518
|
+
if (aligned) {
|
|
1519
|
+
const side = aligned[1];
|
|
1520
|
+
style.textAlign = side;
|
|
1521
|
+
style.alignBlocks = side;
|
|
574
1522
|
style.tableTextAlignSet = true;
|
|
575
1523
|
}
|
|
576
1524
|
return;
|
|
577
1525
|
}
|
|
1526
|
+
case 'text-align-last': {
|
|
1527
|
+
const v = value.toLowerCase();
|
|
1528
|
+
if (v === 'auto' ||
|
|
1529
|
+
v === 'left' ||
|
|
1530
|
+
v === 'right' ||
|
|
1531
|
+
v === 'center' ||
|
|
1532
|
+
v === 'justify' ||
|
|
1533
|
+
v === 'start' ||
|
|
1534
|
+
v === 'end') {
|
|
1535
|
+
style.textAlignLast = v;
|
|
1536
|
+
}
|
|
1537
|
+
else if (v === 'match-parent') {
|
|
1538
|
+
style.textAlignLast = parent.textAlignLast;
|
|
1539
|
+
}
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
case 'text-justify': {
|
|
1543
|
+
const v = value.toLowerCase();
|
|
1544
|
+
if (v === 'auto' ||
|
|
1545
|
+
v === 'none' ||
|
|
1546
|
+
v === 'inter-word' ||
|
|
1547
|
+
v === 'inter-character') {
|
|
1548
|
+
style.textJustify = v;
|
|
1549
|
+
}
|
|
1550
|
+
else if (v === 'distribute') {
|
|
1551
|
+
style.textJustify = 'inter-character';
|
|
1552
|
+
}
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
578
1555
|
case 'text-indent': {
|
|
579
1556
|
const len = parseLength(value, ctx);
|
|
580
1557
|
if (len !== null && len !== AUTO)
|
|
@@ -607,17 +1584,159 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
607
1584
|
else
|
|
608
1585
|
style.wordSpacing = len;
|
|
609
1586
|
}
|
|
1587
|
+
else if (name === 'letter-spacing' && len && isPct(len)) {
|
|
1588
|
+
// a percentage of the font size, as `em` is (CSS Text 4): `200%`
|
|
1589
|
+
// was dropped, where it is twice the letter's size apart
|
|
1590
|
+
style.letterSpacing = resolve(len, ctx.em);
|
|
1591
|
+
}
|
|
610
1592
|
return;
|
|
611
1593
|
}
|
|
612
|
-
case '
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1594
|
+
case 'font-variant-numeric':
|
|
1595
|
+
case 'font-variant-caps':
|
|
1596
|
+
case 'font-variant-ligatures':
|
|
1597
|
+
case 'font-variant-position': {
|
|
1598
|
+
const kind = name.slice('font-variant-'.length);
|
|
1599
|
+
const variant = parseFontVariant(value, kind);
|
|
1600
|
+
if (variant)
|
|
1601
|
+
Object.assign(style, variant);
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
case 'font-variant': {
|
|
1605
|
+
const variant = parseFontVariant(value, null);
|
|
1606
|
+
if (variant)
|
|
1607
|
+
Object.assign(style, variant);
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
case 'font-kerning': {
|
|
1611
|
+
const v = value.trim().toLowerCase();
|
|
1612
|
+
if (v === 'auto')
|
|
1613
|
+
style.fontKerning = '';
|
|
1614
|
+
else if (v === 'normal')
|
|
1615
|
+
style.fontKerning = 'kern=1';
|
|
1616
|
+
else if (v === 'none')
|
|
1617
|
+
style.fontKerning = 'kern=0';
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
case 'font-feature-settings': {
|
|
1621
|
+
const settings = parseFeatureSettings(value);
|
|
1622
|
+
if (settings !== null)
|
|
1623
|
+
style.fontFeatureSettings = settings;
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
case 'tab-size':
|
|
1627
|
+
case '-moz-tab-size': {
|
|
1628
|
+
// a number of spaces, or a length; neither below nought
|
|
1629
|
+
const v = value.trim();
|
|
1630
|
+
if (/^[+]?(\d+\.?\d*|\.\d+)$/.test(v)) {
|
|
1631
|
+
style.tabSize = Number(v);
|
|
1632
|
+
style.tabSizeIsLength = false;
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
const len = parseLength(v, ctx);
|
|
1636
|
+
if (typeof len === 'number' && len >= 0) {
|
|
1637
|
+
style.tabSize = len;
|
|
1638
|
+
style.tabSizeIsLength = true;
|
|
1639
|
+
}
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
case 'white-space': {
|
|
1643
|
+
const v = value.trim().toLowerCase();
|
|
1644
|
+
if (v === 'normal' ||
|
|
1645
|
+
v === 'nowrap' ||
|
|
1646
|
+
v === 'pre' ||
|
|
1647
|
+
v === 'pre-wrap' ||
|
|
618
1648
|
v === 'pre-line') {
|
|
619
1649
|
style.whiteSpace = v;
|
|
620
1650
|
}
|
|
1651
|
+
else if (v === 'break-spaces') {
|
|
1652
|
+
// `pre-wrap` whose spaces take room at a line's end rather than
|
|
1653
|
+
// hanging past it, which is as near as the engines come
|
|
1654
|
+
style.whiteSpace = 'pre-wrap';
|
|
1655
|
+
}
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
// a word is cut inside itself only where the style says it may be:
|
|
1659
|
+
// `anywhere` as `break-word` is
|
|
1660
|
+
case 'overflow-wrap':
|
|
1661
|
+
case 'word-wrap': {
|
|
1662
|
+
const v = value.trim().toLowerCase();
|
|
1663
|
+
if (v === 'normal')
|
|
1664
|
+
style.overflowWrap = 'normal';
|
|
1665
|
+
else if (v === 'break-word' || v === 'anywhere') {
|
|
1666
|
+
style.overflowWrap = 'break-word';
|
|
1667
|
+
}
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
case 'line-break': {
|
|
1671
|
+
const v = value.trim().toLowerCase();
|
|
1672
|
+
if (v === 'auto' ||
|
|
1673
|
+
v === 'loose' ||
|
|
1674
|
+
v === 'normal' ||
|
|
1675
|
+
v === 'strict' ||
|
|
1676
|
+
v === 'anywhere') {
|
|
1677
|
+
style.lineBreakAnywhere = v === 'anywhere';
|
|
1678
|
+
}
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
case 'word-break': {
|
|
1682
|
+
const v = value.trim().toLowerCase();
|
|
1683
|
+
if (v === 'normal' ||
|
|
1684
|
+
v === 'break-all' ||
|
|
1685
|
+
v === 'keep-all' ||
|
|
1686
|
+
v === 'break-word') {
|
|
1687
|
+
style.wordBreak = v;
|
|
1688
|
+
}
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
// CSS Text 4 splits `white-space` into what it keeps of the white space
|
|
1692
|
+
// and whether its lines wrap; each longhand changes its half of it
|
|
1693
|
+
case 'white-space-collapse': {
|
|
1694
|
+
const v = value.trim().toLowerCase();
|
|
1695
|
+
const collapse = v === 'collapse'
|
|
1696
|
+
? 'collapse'
|
|
1697
|
+
: v === 'preserve' || v === 'break-spaces' || v === 'preserve-spaces'
|
|
1698
|
+
? 'preserve'
|
|
1699
|
+
: v === 'preserve-breaks'
|
|
1700
|
+
? 'preserve-breaks'
|
|
1701
|
+
: null;
|
|
1702
|
+
if (collapse) {
|
|
1703
|
+
style.whiteSpace = whiteSpaceOf(collapse, wrapsIn(style.whiteSpace));
|
|
1704
|
+
}
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
case 'text-wrap':
|
|
1708
|
+
case 'text-wrap-mode':
|
|
1709
|
+
case 'text-wrap-style': {
|
|
1710
|
+
// `text-wrap` is both, and resets the one it leaves out: Tailwind 4
|
|
1711
|
+
// writes `text-nowrap` and `text-balance` in it
|
|
1712
|
+
let wrap = null;
|
|
1713
|
+
let balance = null;
|
|
1714
|
+
for (const word of splitValue(value.toLowerCase())) {
|
|
1715
|
+
const mode = word === 'wrap' || word === 'nowrap';
|
|
1716
|
+
const kind = word === 'auto' ||
|
|
1717
|
+
word === 'balance' ||
|
|
1718
|
+
word === 'pretty' ||
|
|
1719
|
+
word === 'stable';
|
|
1720
|
+
if (mode && wrap === null && name !== 'text-wrap-style') {
|
|
1721
|
+
wrap = word === 'wrap';
|
|
1722
|
+
}
|
|
1723
|
+
else if (kind && balance === null && name !== 'text-wrap-mode') {
|
|
1724
|
+
balance = word === 'balance';
|
|
1725
|
+
}
|
|
1726
|
+
else
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
if (wrap === null && balance === null)
|
|
1730
|
+
return;
|
|
1731
|
+
if (name === 'text-wrap') {
|
|
1732
|
+
wrap ??= true;
|
|
1733
|
+
balance ??= false;
|
|
1734
|
+
}
|
|
1735
|
+
if (wrap !== null) {
|
|
1736
|
+
style.whiteSpace = whiteSpaceOf(COLLAPSE_OF[style.whiteSpace], wrap);
|
|
1737
|
+
}
|
|
1738
|
+
if (balance !== null)
|
|
1739
|
+
style.textWrapStyle = balance ? 'balance' : 'auto';
|
|
621
1740
|
return;
|
|
622
1741
|
}
|
|
623
1742
|
case 'direction': {
|
|
@@ -626,28 +1745,86 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
626
1745
|
style.direction = v;
|
|
627
1746
|
return;
|
|
628
1747
|
}
|
|
1748
|
+
case 'unicode-bidi': {
|
|
1749
|
+
const v = value.trim().toLowerCase();
|
|
1750
|
+
if (v === 'normal' ||
|
|
1751
|
+
v === 'embed' ||
|
|
1752
|
+
v === 'isolate' ||
|
|
1753
|
+
v === 'bidi-override' ||
|
|
1754
|
+
v === 'isolate-override' ||
|
|
1755
|
+
v === 'plaintext') {
|
|
1756
|
+
style.unicodeBidi = v;
|
|
1757
|
+
}
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
629
1760
|
case 'text-decoration':
|
|
630
1761
|
case 'text-decoration-line': {
|
|
1762
|
+
// Read whole before any of it is taken: a word the property does not
|
|
1763
|
+
// know, one given twice, or `none` beside a line makes the declaration
|
|
1764
|
+
// invalid, and it is ignored (CSS 2.1 4.2) — `underline overline
|
|
1765
|
+
// line-through diagonal` draws nothing, where it drew a line-through.
|
|
1766
|
+
const lines = [];
|
|
1767
|
+
let none = false;
|
|
1768
|
+
let color = null;
|
|
1769
|
+
let decorationStyle = null;
|
|
1770
|
+
let thickness;
|
|
631
1771
|
for (const part of splitValue(value)) {
|
|
632
1772
|
const v = part.toLowerCase();
|
|
633
|
-
if (v
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
v
|
|
637
|
-
|
|
1773
|
+
if (LINE_KEYWORDS.has(v)) {
|
|
1774
|
+
if (none || lines.includes(v))
|
|
1775
|
+
return;
|
|
1776
|
+
lines.push(v);
|
|
1777
|
+
continue;
|
|
638
1778
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
1779
|
+
if (v === 'none') {
|
|
1780
|
+
if (none || lines.length)
|
|
1781
|
+
return;
|
|
1782
|
+
none = true;
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
if (name !== 'text-decoration')
|
|
1786
|
+
return;
|
|
1787
|
+
const c = parseColor(part);
|
|
1788
|
+
if (c) {
|
|
1789
|
+
if (color !== null)
|
|
1790
|
+
return;
|
|
1791
|
+
color = c;
|
|
647
1792
|
}
|
|
1793
|
+
else if (DECORATION_STYLES.has(v)) {
|
|
1794
|
+
if (decorationStyle !== null)
|
|
1795
|
+
return;
|
|
1796
|
+
decorationStyle = v;
|
|
1797
|
+
}
|
|
1798
|
+
else {
|
|
1799
|
+
const t = decorationLength(part, ctx);
|
|
1800
|
+
if (t === undefined || thickness !== undefined)
|
|
1801
|
+
return;
|
|
1802
|
+
thickness = t;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
if (!none && !lines.length && name === 'text-decoration-line')
|
|
1806
|
+
return;
|
|
1807
|
+
style.textDecorationLine = lines.length ? lines.join(' ') : 'none';
|
|
1808
|
+
if (name === 'text-decoration') {
|
|
1809
|
+
// a shorthand: what it does not name goes back to its initial value
|
|
1810
|
+
style.textDecorationColor = color;
|
|
1811
|
+
style.textDecorationStyle = decorationStyle ?? 'solid';
|
|
1812
|
+
style.textDecorationThickness = thickness ?? null;
|
|
648
1813
|
}
|
|
649
1814
|
return;
|
|
650
1815
|
}
|
|
1816
|
+
case 'text-decoration-thickness': {
|
|
1817
|
+
const thickness = decorationLength(value, ctx, 'from-font');
|
|
1818
|
+
if (thickness !== undefined)
|
|
1819
|
+
style.textDecorationThickness = thickness;
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
case 'text-underline-offset': {
|
|
1823
|
+
const offset = decorationLength(value, ctx);
|
|
1824
|
+
if (offset !== undefined)
|
|
1825
|
+
style.textUnderlineOffset = offset;
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
651
1828
|
case 'text-decoration-style': {
|
|
652
1829
|
const v = value.toLowerCase();
|
|
653
1830
|
if (DECORATION_STYLES.has(v)) {
|
|
@@ -661,17 +1838,77 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
661
1838
|
}
|
|
662
1839
|
// --- lists --------------------------------------------------------------
|
|
663
1840
|
case 'list-style': {
|
|
1841
|
+
// a type, a position and an image, each at most once and in any
|
|
1842
|
+
// order, and each left out reset (CSS 2.1 12.5.1); a `none` is
|
|
1843
|
+
// whichever of the type and the image is not otherwise given, both
|
|
1844
|
+
// where neither is, and a third thing where both are, which is
|
|
1845
|
+
// nothing and makes the declaration invalid
|
|
1846
|
+
let type = null;
|
|
1847
|
+
let position = null;
|
|
1848
|
+
let image;
|
|
1849
|
+
let nones = 0;
|
|
664
1850
|
for (const part of splitValue(value)) {
|
|
665
1851
|
const v = part.toLowerCase();
|
|
666
|
-
if (v === 'inside' || v === 'outside')
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
1852
|
+
if (v === 'inside' || v === 'outside') {
|
|
1853
|
+
if (position)
|
|
1854
|
+
return;
|
|
1855
|
+
position = v;
|
|
1856
|
+
}
|
|
1857
|
+
else if (v === 'none')
|
|
1858
|
+
nones += 1;
|
|
1859
|
+
else if (v.startsWith('url(') || IMAGE_FUNCTION.test(v)) {
|
|
1860
|
+
if (image !== undefined)
|
|
1861
|
+
return;
|
|
1862
|
+
image = v.startsWith('url(') ? (parseUrl(part) ?? null) : null;
|
|
1863
|
+
}
|
|
1864
|
+
else {
|
|
1865
|
+
if (type !== null)
|
|
1866
|
+
return;
|
|
1867
|
+
type = parseListStyleType(part);
|
|
1868
|
+
if (type === null)
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
if (nones > 2)
|
|
1873
|
+
return;
|
|
1874
|
+
if (nones === 2 && (type !== null || image !== undefined))
|
|
1875
|
+
return;
|
|
1876
|
+
if (nones === 1) {
|
|
1877
|
+
if (type !== null && image !== undefined)
|
|
1878
|
+
return;
|
|
1879
|
+
if (type === null)
|
|
1880
|
+
type = 'none';
|
|
1881
|
+
else
|
|
1882
|
+
image = null;
|
|
1883
|
+
}
|
|
1884
|
+
if (nones === 2) {
|
|
1885
|
+
type = 'none';
|
|
1886
|
+
image = null;
|
|
1887
|
+
}
|
|
1888
|
+
style.listStyleType = type ?? 'disc';
|
|
1889
|
+
style.listStylePosition = position ?? 'outside';
|
|
1890
|
+
style.listStyleImage = image ?? null;
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
case 'list-style-image': {
|
|
1894
|
+
const v = value.trim();
|
|
1895
|
+
if (v.toLowerCase() === 'none')
|
|
1896
|
+
style.listStyleImage = null;
|
|
1897
|
+
else if (IMAGE_FUNCTION.test(v.toLowerCase())) {
|
|
1898
|
+
// a gradient is an image this draws no marker as
|
|
1899
|
+
style.listStyleImage = null;
|
|
1900
|
+
}
|
|
1901
|
+
else if (/^url\(/i.test(v)) {
|
|
1902
|
+
const url = parseUrl(v);
|
|
1903
|
+
if (url !== undefined)
|
|
1904
|
+
style.listStyleImage = url;
|
|
670
1905
|
}
|
|
671
1906
|
return;
|
|
672
1907
|
}
|
|
673
1908
|
case 'list-style-type': {
|
|
674
|
-
|
|
1909
|
+
const type = parseListStyleType(value);
|
|
1910
|
+
if (type !== null)
|
|
1911
|
+
style.listStyleType = type;
|
|
675
1912
|
return;
|
|
676
1913
|
}
|
|
677
1914
|
case 'list-style-position': {
|
|
@@ -705,7 +1942,7 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
705
1942
|
return;
|
|
706
1943
|
}
|
|
707
1944
|
case 'justify-content': {
|
|
708
|
-
const v =
|
|
1945
|
+
const v = justifyKeyword(value);
|
|
709
1946
|
if (v)
|
|
710
1947
|
style.justifyContent = v;
|
|
711
1948
|
return;
|
|
@@ -759,23 +1996,239 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
759
1996
|
return;
|
|
760
1997
|
}
|
|
761
1998
|
case 'order': {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1999
|
+
// an integer, and nothing else (CSS Flexbox 5.4)
|
|
2000
|
+
const v = value.trim();
|
|
2001
|
+
if (/^[+-]?\d+$/.test(v))
|
|
2002
|
+
style.order = Number(v);
|
|
765
2003
|
return;
|
|
766
2004
|
}
|
|
767
2005
|
case 'gap':
|
|
768
2006
|
case 'row-gap':
|
|
769
|
-
case 'column-gap':
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
2007
|
+
case 'column-gap':
|
|
2008
|
+
// the names CSS Grid 1 first gave them, which browsers still read
|
|
2009
|
+
case 'grid-gap':
|
|
2010
|
+
case 'grid-row-gap':
|
|
2011
|
+
case 'grid-column-gap': {
|
|
2012
|
+
const which = name.replace(/^grid-/, '');
|
|
2013
|
+
// a length or a percentage, not below nought, or `normal`, which is
|
|
2014
|
+
// nought outside columns of text
|
|
2015
|
+
const gap = (part) => {
|
|
2016
|
+
if (part === undefined)
|
|
2017
|
+
return null;
|
|
2018
|
+
if (part.toLowerCase() === 'normal')
|
|
2019
|
+
return 0;
|
|
2020
|
+
const len = parseLength(part, ctx);
|
|
2021
|
+
if (typeof len === 'number')
|
|
2022
|
+
return len >= 0 ? len : null;
|
|
2023
|
+
return len !== null && isPct(len) && !(len.pct < 0) ? len : null;
|
|
2024
|
+
};
|
|
2025
|
+
const parts = splitValue(value);
|
|
2026
|
+
if (!parts.length || parts.length > (which === 'gap' ? 2 : 1))
|
|
774
2027
|
return;
|
|
775
|
-
|
|
2028
|
+
const row = gap(parts[0]);
|
|
2029
|
+
const col = parts.length > 1 ? gap(parts[1]) : row;
|
|
2030
|
+
if (row === null || col === null)
|
|
2031
|
+
return;
|
|
2032
|
+
if (which !== 'column-gap')
|
|
776
2033
|
style.rowGap = row;
|
|
777
|
-
if (
|
|
778
|
-
style.columnGap = col
|
|
2034
|
+
if (which !== 'row-gap')
|
|
2035
|
+
style.columnGap = col;
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
case 'grid-template-columns':
|
|
2039
|
+
case 'grid-template-rows': {
|
|
2040
|
+
const template = value.toLowerCase() === 'none' ? null : parseGridTemplate(value, ctx);
|
|
2041
|
+
if (template === undefined)
|
|
2042
|
+
return;
|
|
2043
|
+
if (name === 'grid-template-columns')
|
|
2044
|
+
style.gridColumns = template;
|
|
2045
|
+
else
|
|
2046
|
+
style.gridRows = template;
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
case 'grid-template-areas': {
|
|
2050
|
+
if (value.trim().toLowerCase() === 'none') {
|
|
2051
|
+
style.gridAreas = null;
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
const parts = trackParts(value);
|
|
2055
|
+
if (!parts?.length || !parts.every((p) => /^["']/.test(p)))
|
|
2056
|
+
return;
|
|
2057
|
+
const areas = parseAreas(parts);
|
|
2058
|
+
if (areas)
|
|
2059
|
+
style.gridAreas = areas;
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
case 'grid-auto-flow': {
|
|
2063
|
+
const words = value.trim().toLowerCase().split(/\s+/);
|
|
2064
|
+
const dense = words.includes('dense');
|
|
2065
|
+
const axis = words.filter((w) => w !== 'dense');
|
|
2066
|
+
if (axis.length > 1 ||
|
|
2067
|
+
words.length > 2 ||
|
|
2068
|
+
(axis.length && axis[0] !== 'row' && axis[0] !== 'column') ||
|
|
2069
|
+
(!dense && !axis.length)) {
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
const flow = axis[0] ?? 'row';
|
|
2073
|
+
style.gridAutoFlow = dense
|
|
2074
|
+
? flow === 'row'
|
|
2075
|
+
? 'row dense'
|
|
2076
|
+
: 'column dense'
|
|
2077
|
+
: flow;
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
case 'grid-auto-rows':
|
|
2081
|
+
case 'grid-auto-columns': {
|
|
2082
|
+
const track = parseGridTrack(splitValue(value)[0] ?? '', ctx);
|
|
2083
|
+
if (!track)
|
|
2084
|
+
return;
|
|
2085
|
+
if (name === 'grid-auto-rows')
|
|
2086
|
+
style.gridAutoRows = track;
|
|
2087
|
+
else
|
|
2088
|
+
style.gridAutoColumns = track;
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
2091
|
+
case 'grid-template': {
|
|
2092
|
+
// rows, a slash and columns, or `none` (CSS Grid 1, 7.4)
|
|
2093
|
+
if (value.trim().toLowerCase() === 'none') {
|
|
2094
|
+
style.gridRows = null;
|
|
2095
|
+
style.gridColumns = null;
|
|
2096
|
+
style.gridAreas = null;
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
const template = parseTemplateShorthand(value, ctx);
|
|
2100
|
+
if (!template)
|
|
2101
|
+
return;
|
|
2102
|
+
style.gridRows = template.rows;
|
|
2103
|
+
style.gridColumns = template.columns;
|
|
2104
|
+
style.gridAreas = template.areas;
|
|
2105
|
+
return;
|
|
2106
|
+
}
|
|
2107
|
+
case 'grid': {
|
|
2108
|
+
// a template, or one axis's template and the other's `auto-flow`
|
|
2109
|
+
// tracks (7.8); every longhand it does not name back to its initial
|
|
2110
|
+
const parts = splitTopLevelSlash(value);
|
|
2111
|
+
const flows = (part) => part !== undefined && /(^|\s)auto-flow(\s|$)/i.test(part);
|
|
2112
|
+
const autoTrack = (part) => {
|
|
2113
|
+
const words = splitValue(part).filter((w) => !/^(auto-flow|dense)$/i.test(w));
|
|
2114
|
+
return words.length ? parseGridTrack(words[0], ctx) : AUTO_TRACK;
|
|
2115
|
+
};
|
|
2116
|
+
const dense = (part) => /(^|\s)dense(\s|$)/i.test(part);
|
|
2117
|
+
const axis = (part) => part.trim().toLowerCase() === 'none'
|
|
2118
|
+
? null
|
|
2119
|
+
: parseGridTemplate(part, ctx);
|
|
2120
|
+
if (parts.length === 2 && flows(parts[0])) {
|
|
2121
|
+
const rows = autoTrack(parts[0]);
|
|
2122
|
+
const columns = axis(parts[1]);
|
|
2123
|
+
if (!rows || columns === undefined)
|
|
2124
|
+
return;
|
|
2125
|
+
style.gridRows = null;
|
|
2126
|
+
style.gridColumns = columns;
|
|
2127
|
+
style.gridAreas = null;
|
|
2128
|
+
style.gridAutoRows = rows;
|
|
2129
|
+
style.gridAutoColumns = AUTO_TRACK;
|
|
2130
|
+
style.gridAutoFlow = dense(parts[0]) ? 'row dense' : 'row';
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
if (parts.length === 2 && flows(parts[1])) {
|
|
2134
|
+
const rows = axis(parts[0]);
|
|
2135
|
+
const columns = autoTrack(parts[1]);
|
|
2136
|
+
if (rows === undefined || !columns)
|
|
2137
|
+
return;
|
|
2138
|
+
style.gridRows = rows;
|
|
2139
|
+
style.gridColumns = null;
|
|
2140
|
+
style.gridAreas = null;
|
|
2141
|
+
style.gridAutoRows = AUTO_TRACK;
|
|
2142
|
+
style.gridAutoColumns = columns;
|
|
2143
|
+
style.gridAutoFlow = dense(parts[1]) ? 'column dense' : 'column';
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
const none = value.trim().toLowerCase() === 'none';
|
|
2147
|
+
const template = none ? null : parseTemplateShorthand(value, ctx);
|
|
2148
|
+
if (!none && !template)
|
|
2149
|
+
return;
|
|
2150
|
+
style.gridRows = template?.rows ?? null;
|
|
2151
|
+
style.gridColumns = template?.columns ?? null;
|
|
2152
|
+
style.gridAreas = template?.areas ?? null;
|
|
2153
|
+
style.gridAutoRows = AUTO_TRACK;
|
|
2154
|
+
style.gridAutoColumns = AUTO_TRACK;
|
|
2155
|
+
style.gridAutoFlow = 'row';
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
case 'grid-column':
|
|
2159
|
+
case 'grid-row': {
|
|
2160
|
+
// an end left out is the start where the start is a name, and auto
|
|
2161
|
+
// where it is not (8.4)
|
|
2162
|
+
const parts = splitTopLevelSlash(value);
|
|
2163
|
+
const start = parseGridLine(parts[0]);
|
|
2164
|
+
const end = parts.length > 1 ? parseGridLine(parts[1]) : named(start);
|
|
2165
|
+
if (start === undefined || end === undefined || parts.length > 2)
|
|
2166
|
+
return;
|
|
2167
|
+
if (name === 'grid-column') {
|
|
2168
|
+
style.gridColumnStart = start;
|
|
2169
|
+
style.gridColumnEnd = end;
|
|
2170
|
+
}
|
|
2171
|
+
else {
|
|
2172
|
+
style.gridRowStart = start;
|
|
2173
|
+
style.gridRowEnd = end;
|
|
2174
|
+
}
|
|
2175
|
+
return;
|
|
2176
|
+
}
|
|
2177
|
+
case 'grid-area': {
|
|
2178
|
+
// the lines left out copy a name: the column start the row start's,
|
|
2179
|
+
// and each end its start's (8.4)
|
|
2180
|
+
const parts = splitTopLevelSlash(value).map(parseGridLine);
|
|
2181
|
+
if (parts.length > 4 || parts.some((p) => p === undefined))
|
|
2182
|
+
return;
|
|
2183
|
+
const rowStart = parts[0] ?? null;
|
|
2184
|
+
const columnStart = parts.length > 1 ? parts[1] : named(rowStart);
|
|
2185
|
+
style.gridRowStart = rowStart;
|
|
2186
|
+
style.gridColumnStart = columnStart;
|
|
2187
|
+
style.gridRowEnd = parts.length > 2 ? parts[2] : named(rowStart);
|
|
2188
|
+
style.gridColumnEnd = parts.length > 3 ? parts[3] : named(columnStart);
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
case 'grid-column-start':
|
|
2192
|
+
case 'grid-column-end':
|
|
2193
|
+
case 'grid-row-start':
|
|
2194
|
+
case 'grid-row-end': {
|
|
2195
|
+
const line = parseGridLine(value);
|
|
2196
|
+
if (line === undefined)
|
|
2197
|
+
return;
|
|
2198
|
+
style[camel(name)] = line;
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
case 'justify-items':
|
|
2202
|
+
case 'justify-self': {
|
|
2203
|
+
const v = value.trim().toLowerCase();
|
|
2204
|
+
const keyword = v === 'auto' && name === 'justify-self'
|
|
2205
|
+
? 'auto'
|
|
2206
|
+
: v === 'normal' || v === 'stretch'
|
|
2207
|
+
? v
|
|
2208
|
+
: alignKeyword(v);
|
|
2209
|
+
if (keyword === 'auto' ||
|
|
2210
|
+
keyword === 'normal' ||
|
|
2211
|
+
keyword === 'stretch' ||
|
|
2212
|
+
keyword === 'flex-start' ||
|
|
2213
|
+
keyword === 'flex-end' ||
|
|
2214
|
+
keyword === 'center') {
|
|
2215
|
+
if (name === 'justify-items' && keyword !== 'auto') {
|
|
2216
|
+
style.justifyItems = keyword;
|
|
2217
|
+
}
|
|
2218
|
+
else if (name === 'justify-self')
|
|
2219
|
+
style.justifySelf = keyword;
|
|
2220
|
+
}
|
|
2221
|
+
return;
|
|
2222
|
+
}
|
|
2223
|
+
case 'place-items':
|
|
2224
|
+
case 'place-self': {
|
|
2225
|
+
const parts = splitValue(value);
|
|
2226
|
+
if (!parts.length || parts.length > 2)
|
|
2227
|
+
return;
|
|
2228
|
+
const align = name === 'place-items' ? 'align-items' : 'align-self';
|
|
2229
|
+
const justify = name === 'place-items' ? 'justify-items' : 'justify-self';
|
|
2230
|
+
applyDeclaration(style, parent, align, parts[0], ctx);
|
|
2231
|
+
applyDeclaration(style, parent, justify, parts[1] ?? parts[0], ctx);
|
|
779
2232
|
return;
|
|
780
2233
|
}
|
|
781
2234
|
// --- tables -------------------------------------------------------------
|
|
@@ -785,10 +2238,30 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
785
2238
|
style.borderCollapse = v;
|
|
786
2239
|
return;
|
|
787
2240
|
}
|
|
2241
|
+
case 'caption-side': {
|
|
2242
|
+
const v = value.toLowerCase();
|
|
2243
|
+
if (v === 'top' || v === 'bottom')
|
|
2244
|
+
style.captionSide = v;
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
case 'empty-cells': {
|
|
2248
|
+
const v = value.trim().toLowerCase();
|
|
2249
|
+
if (v === 'show' || v === 'hide')
|
|
2250
|
+
style.emptyCells = v;
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
788
2253
|
case 'border-spacing': {
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
2254
|
+
// one length for both, or the columns' and then the rows' (CSS 2.1
|
|
2255
|
+
// 17.6.1), never negative
|
|
2256
|
+
const parts = splitValue(value);
|
|
2257
|
+
if (parts.length < 1 || parts.length > 2)
|
|
2258
|
+
return;
|
|
2259
|
+
const x = parseLength(parts[0], ctx);
|
|
2260
|
+
const y = parts.length > 1 ? parseLength(parts[1], ctx) : x;
|
|
2261
|
+
if (typeof x !== 'number' || typeof y !== 'number' || x < 0 || y < 0)
|
|
2262
|
+
return;
|
|
2263
|
+
style.borderSpacing = x;
|
|
2264
|
+
style.borderSpacingY = y;
|
|
792
2265
|
return;
|
|
793
2266
|
}
|
|
794
2267
|
case 'table-layout': {
|
|
@@ -801,6 +2274,21 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
801
2274
|
return;
|
|
802
2275
|
}
|
|
803
2276
|
}
|
|
2277
|
+
/** `-webkit-box-pack` and `-webkit-box-align`, as `justify-content` and
|
|
2278
|
+
* `align-items` say them. */
|
|
2279
|
+
const BOX_PACK = new Map([
|
|
2280
|
+
['start', 'flex-start'],
|
|
2281
|
+
['end', 'flex-end'],
|
|
2282
|
+
['center', 'center'],
|
|
2283
|
+
['justify', 'space-between'],
|
|
2284
|
+
]);
|
|
2285
|
+
const BOX_ALIGN = new Map([
|
|
2286
|
+
['start', 'flex-start'],
|
|
2287
|
+
['end', 'flex-end'],
|
|
2288
|
+
['center', 'center'],
|
|
2289
|
+
['baseline', 'baseline'],
|
|
2290
|
+
['stretch', 'stretch'],
|
|
2291
|
+
]);
|
|
804
2292
|
const DISPLAYS = new Set([
|
|
805
2293
|
'none',
|
|
806
2294
|
'block',
|
|
@@ -819,6 +2307,16 @@ const DISPLAYS = new Set([
|
|
|
819
2307
|
'table-caption',
|
|
820
2308
|
'table-column',
|
|
821
2309
|
'table-column-group',
|
|
2310
|
+
'contents',
|
|
2311
|
+
]);
|
|
2312
|
+
/** `text-decoration-line`'s lines. `blink` is one a user agent may leave
|
|
2313
|
+
* undrawn (CSS 2.1 16.3.1), and this one does; `overline` is not drawn
|
|
2314
|
+
* yet either. */
|
|
2315
|
+
const LINE_KEYWORDS = new Set([
|
|
2316
|
+
'underline',
|
|
2317
|
+
'overline',
|
|
2318
|
+
'line-through',
|
|
2319
|
+
'blink',
|
|
822
2320
|
]);
|
|
823
2321
|
const DECORATION_STYLES = new Set([
|
|
824
2322
|
'solid',
|
|
@@ -829,23 +2327,157 @@ const DECORATION_STYLES = new Set([
|
|
|
829
2327
|
]);
|
|
830
2328
|
function overflowKeyword(v) {
|
|
831
2329
|
const s = (v ?? '').toLowerCase();
|
|
832
|
-
if (s === 'visible' ||
|
|
2330
|
+
if (s === 'visible' ||
|
|
2331
|
+
s === 'hidden' ||
|
|
2332
|
+
s === 'scroll' ||
|
|
2333
|
+
s === 'auto' ||
|
|
2334
|
+
s === 'clip') {
|
|
833
2335
|
return s;
|
|
834
|
-
|
|
835
|
-
return 'hidden';
|
|
2336
|
+
}
|
|
836
2337
|
return null;
|
|
837
2338
|
}
|
|
2339
|
+
/** `contain`'s containments (CSS Containment 2, 3). */
|
|
2340
|
+
export const CONTAIN_SIZE = 1;
|
|
2341
|
+
export const CONTAIN_INLINE_SIZE = 2;
|
|
2342
|
+
export const CONTAIN_LAYOUT = 4;
|
|
2343
|
+
export const CONTAIN_STYLE = 8;
|
|
2344
|
+
export const CONTAIN_PAINT = 16;
|
|
2345
|
+
/** A `contain` value as its bits, or null where it is none: `strict` and
|
|
2346
|
+
* `content` are shorthands, and the rest a set of the five, `size` and
|
|
2347
|
+
* `inline-size` not both. */
|
|
2348
|
+
function containOf(value) {
|
|
2349
|
+
const words = value.trim().toLowerCase().split(/\s+/);
|
|
2350
|
+
if (words.length === 1) {
|
|
2351
|
+
if (words[0] === 'none')
|
|
2352
|
+
return 0;
|
|
2353
|
+
if (words[0] === 'strict') {
|
|
2354
|
+
return CONTAIN_SIZE | CONTAIN_LAYOUT | CONTAIN_STYLE | CONTAIN_PAINT;
|
|
2355
|
+
}
|
|
2356
|
+
if (words[0] === 'content') {
|
|
2357
|
+
return CONTAIN_LAYOUT | CONTAIN_STYLE | CONTAIN_PAINT;
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
let bits = 0;
|
|
2361
|
+
for (const word of words) {
|
|
2362
|
+
const bit = word === 'size'
|
|
2363
|
+
? CONTAIN_SIZE
|
|
2364
|
+
: word === 'inline-size'
|
|
2365
|
+
? CONTAIN_INLINE_SIZE
|
|
2366
|
+
: word === 'layout'
|
|
2367
|
+
? CONTAIN_LAYOUT
|
|
2368
|
+
: word === 'style'
|
|
2369
|
+
? CONTAIN_STYLE
|
|
2370
|
+
: word === 'paint'
|
|
2371
|
+
? CONTAIN_PAINT
|
|
2372
|
+
: 0;
|
|
2373
|
+
if (!bit || bits & bit)
|
|
2374
|
+
return null;
|
|
2375
|
+
bits |= bit;
|
|
2376
|
+
}
|
|
2377
|
+
if (bits & CONTAIN_SIZE && bits & CONTAIN_INLINE_SIZE)
|
|
2378
|
+
return null;
|
|
2379
|
+
return bits;
|
|
2380
|
+
}
|
|
2381
|
+
/**
|
|
2382
|
+
* The two axes' `overflow` as they compute together (CSS Overflow 3, 3.1):
|
|
2383
|
+
* `visible` and `clip` hold beside each other, and beside a value that
|
|
2384
|
+
* makes the box a scroll container, `visible` is `auto` and `clip` is
|
|
2385
|
+
* `hidden`.
|
|
2386
|
+
*/
|
|
2387
|
+
/**
|
|
2388
|
+
* The containment `content-visibility` brings with it (CSS Containment 2,
|
|
2389
|
+
* 4): `auto` layout, style and paint containment — and size containment
|
|
2390
|
+
* where the box is not on screen, which a document laid out whole never
|
|
2391
|
+
* tells — and `hidden` all four.
|
|
2392
|
+
*/
|
|
2393
|
+
export function settleContentVisibility(style) {
|
|
2394
|
+
if (style.contentVisibility === 'auto') {
|
|
2395
|
+
style.contain |= CONTAIN_LAYOUT | CONTAIN_STYLE | CONTAIN_PAINT;
|
|
2396
|
+
}
|
|
2397
|
+
else if (style.contentVisibility === 'hidden') {
|
|
2398
|
+
style.contain |=
|
|
2399
|
+
CONTAIN_SIZE | CONTAIN_LAYOUT | CONTAIN_STYLE | CONTAIN_PAINT;
|
|
2400
|
+
style.contain &= ~CONTAIN_INLINE_SIZE;
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
export function settleOverflow(style) {
|
|
2404
|
+
const x = style.overflowX;
|
|
2405
|
+
const y = style.overflowY;
|
|
2406
|
+
const scrollsX = x !== 'visible' && x !== 'clip';
|
|
2407
|
+
const scrollsY = y !== 'visible' && y !== 'clip';
|
|
2408
|
+
if (scrollsX === scrollsY)
|
|
2409
|
+
return;
|
|
2410
|
+
if (scrollsX)
|
|
2411
|
+
style.overflowY = y === 'visible' ? 'auto' : 'hidden';
|
|
2412
|
+
else
|
|
2413
|
+
style.overflowX = x === 'visible' ? 'auto' : 'hidden';
|
|
2414
|
+
}
|
|
2415
|
+
function isVisualBox(v) {
|
|
2416
|
+
return v === 'border-box' || v === 'padding-box' || v === 'content-box';
|
|
2417
|
+
}
|
|
2418
|
+
function isBackgroundClip(v) {
|
|
2419
|
+
return v === 'border-area' || isVisualBox(v);
|
|
2420
|
+
}
|
|
2421
|
+
/**
|
|
2422
|
+
* Whether a box is a scroll container: its `overflow` is neither `visible`
|
|
2423
|
+
* nor `clip`. `clip` cuts what overflows as `hidden` does, but makes no
|
|
2424
|
+
* formatting context and keeps a flex or grid item's automatic minimum
|
|
2425
|
+
* (CSS Overflow 3, 3.1) — so only this asks it apart from `hidden`.
|
|
2426
|
+
*/
|
|
2427
|
+
export function scrolls(style) {
|
|
2428
|
+
const x = style.overflowX;
|
|
2429
|
+
const y = style.overflowY;
|
|
2430
|
+
return (x !== 'visible' && x !== 'clip') || (y !== 'visible' && y !== 'clip');
|
|
2431
|
+
}
|
|
2432
|
+
/** A `justify-content`: the writing mode's `start` and `end`, and `left`
|
|
2433
|
+
* and `right`, kept for the flex box to resolve against its direction;
|
|
2434
|
+
* `normal` and `stretch` pack as `flex-start` does (`overflowAligned`). */
|
|
2435
|
+
function justifyKeyword(value) {
|
|
2436
|
+
const v = overflowAligned(value);
|
|
2437
|
+
switch (v) {
|
|
2438
|
+
case 'start':
|
|
2439
|
+
case 'end':
|
|
2440
|
+
case 'left':
|
|
2441
|
+
case 'right':
|
|
2442
|
+
case 'flex-start':
|
|
2443
|
+
case 'flex-end':
|
|
2444
|
+
case 'center':
|
|
2445
|
+
case 'space-between':
|
|
2446
|
+
case 'space-around':
|
|
2447
|
+
case 'space-evenly':
|
|
2448
|
+
return v;
|
|
2449
|
+
case 'normal':
|
|
2450
|
+
case 'stretch':
|
|
2451
|
+
return v;
|
|
2452
|
+
default:
|
|
2453
|
+
return null;
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
/** An alignment's keyword, with `unsafe` before it read past, which is
|
|
2457
|
+
* what an alignment does anyway. A `safe` one sets the subject at the
|
|
2458
|
+
* start where it overflows its room, which is not done here: it is no
|
|
2459
|
+
* value, and the declaration is dropped — which is the start's for the
|
|
2460
|
+
* flex box that overflows, as it was before `safe` was read at all. */
|
|
2461
|
+
function overflowAligned(value) {
|
|
2462
|
+
const v = value.trim().toLowerCase().replace(/\s+/g, ' ');
|
|
2463
|
+
const m = /^unsafe (.+)$/.exec(v);
|
|
2464
|
+
return m ? m[1] : v;
|
|
2465
|
+
}
|
|
838
2466
|
function alignKeyword(value) {
|
|
839
|
-
const v = value
|
|
2467
|
+
const v = overflowAligned(value);
|
|
840
2468
|
switch (v) {
|
|
841
2469
|
case 'start':
|
|
842
2470
|
case 'flex-start':
|
|
2471
|
+
case 'self-start':
|
|
843
2472
|
case 'left':
|
|
844
2473
|
return 'flex-start';
|
|
845
2474
|
case 'end':
|
|
846
2475
|
case 'flex-end':
|
|
2476
|
+
case 'self-end':
|
|
847
2477
|
case 'right':
|
|
848
2478
|
return 'flex-end';
|
|
2479
|
+
case 'first baseline':
|
|
2480
|
+
return 'baseline';
|
|
849
2481
|
case 'center':
|
|
850
2482
|
return 'center';
|
|
851
2483
|
case 'stretch':
|
|
@@ -865,23 +2497,316 @@ function borderWidth(value, ctx) {
|
|
|
865
2497
|
// The keywords are CSS pixels that never pass through `parseLength`, so
|
|
866
2498
|
// they take the display scale here.
|
|
867
2499
|
if (kw !== undefined)
|
|
868
|
-
return kw * ctx.scale;
|
|
2500
|
+
return snapBorderWidth(kw * ctx.scale);
|
|
869
2501
|
const len = parseLength(value, ctx);
|
|
870
|
-
|
|
2502
|
+
// a negative width is not a width: the declaration is dropped, and the
|
|
2503
|
+
// one before it stands (CSS 2.1 8.5.1)
|
|
2504
|
+
return typeof len === 'number' && len >= 0 ? snapBorderWidth(len) : null;
|
|
871
2505
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
2506
|
+
/**
|
|
2507
|
+
* A border's or an outline's width in whole device pixels, as it computes
|
|
2508
|
+
* (CSS Values 4, "snap as a border width"): down to the pixel, and a
|
|
2509
|
+
* hairline narrower than one pixel up to it — so two borders of 49.75px
|
|
2510
|
+
* leave the box between them its two pixels, and a 0.5px rule is drawn.
|
|
2511
|
+
* The fraction a length in `em` comes to beside a whole number is not
|
|
2512
|
+
* taken for less than it.
|
|
2513
|
+
*/
|
|
2514
|
+
function snapBorderWidth(width) {
|
|
2515
|
+
if (width > 0 && width < 1)
|
|
2516
|
+
return 1;
|
|
2517
|
+
return Math.floor(width + 1e-6);
|
|
2518
|
+
}
|
|
2519
|
+
const HORIZONTAL = { left: 0, center: 50, right: 100 };
|
|
2520
|
+
const VERTICAL = { top: 0, center: 50, bottom: 100 };
|
|
2521
|
+
/**
|
|
2522
|
+
* A `background-position` as its horizontal and vertical parts. A keyword
|
|
2523
|
+
* says which axis it is on, so one value alone is centred on the other —
|
|
2524
|
+
* `bottom` is the bottom, midway across — and two keywords come in either
|
|
2525
|
+
* order; with a length or a percentage among two, the first is across and
|
|
2526
|
+
* the second down (CSS 2.1 14.2.1). Three or four are CSS3's edge form:
|
|
2527
|
+
* `right 10px center` is ten pixels in from the right, midway down.
|
|
2528
|
+
*/
|
|
2529
|
+
function positionPair(parts, ctx) {
|
|
2530
|
+
if (parts.length === 0 || parts.length > 4)
|
|
875
2531
|
return null;
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
2532
|
+
const words = parts.map((p) => p.toLowerCase());
|
|
2533
|
+
if (parts.length > 2)
|
|
2534
|
+
return edgePosition(words, ctx);
|
|
2535
|
+
const keyword = (w) => w in HORIZONTAL || w in VERTICAL;
|
|
2536
|
+
const pct = (n) => (n === 0 ? 0 : { pct: n });
|
|
2537
|
+
if (parts.length === 1) {
|
|
2538
|
+
const [w] = words;
|
|
2539
|
+
if (w in VERTICAL && !(w in HORIZONTAL))
|
|
2540
|
+
return [pct(50), pct(VERTICAL[w])];
|
|
2541
|
+
if (keyword(w))
|
|
2542
|
+
return [pct(HORIZONTAL[w]), pct(50)];
|
|
2543
|
+
const x = positionLength(w, ctx);
|
|
2544
|
+
return x === null ? null : [x, pct(50)];
|
|
2545
|
+
}
|
|
2546
|
+
const [a, b] = words;
|
|
2547
|
+
if (keyword(a) && keyword(b)) {
|
|
2548
|
+
const swap = (a in VERTICAL && !(a in HORIZONTAL)) ||
|
|
2549
|
+
(b in HORIZONTAL && !(b in VERTICAL));
|
|
2550
|
+
const [h, v] = swap ? [b, a] : [a, b];
|
|
2551
|
+
if (!(h in HORIZONTAL) || !(v in VERTICAL))
|
|
2552
|
+
return null;
|
|
2553
|
+
return [pct(HORIZONTAL[h]), pct(VERTICAL[v])];
|
|
2554
|
+
}
|
|
2555
|
+
if (a in VERTICAL && !(a in HORIZONTAL))
|
|
2556
|
+
return null;
|
|
2557
|
+
if (b in HORIZONTAL && !(b in VERTICAL))
|
|
2558
|
+
return null;
|
|
2559
|
+
const x = keyword(a) ? pct(HORIZONTAL[a]) : positionLength(a, ctx);
|
|
2560
|
+
const y = keyword(b) ? pct(VERTICAL[b]) : positionLength(b, ctx);
|
|
2561
|
+
return x === null || y === null ? null : [x, y];
|
|
884
2562
|
}
|
|
2563
|
+
/** A position's length or percentage — `auto` is none. */
|
|
2564
|
+
function positionLength(word, ctx) {
|
|
2565
|
+
const len = parseLength(word, ctx);
|
|
2566
|
+
return len === AUTO ? null : len;
|
|
2567
|
+
}
|
|
2568
|
+
/** The edge form: a keyword for each axis, each but `center` with an
|
|
2569
|
+
* offset in from its edge or not. */
|
|
2570
|
+
function edgePosition(words, ctx) {
|
|
2571
|
+
const keyword = (w) => w !== undefined && (w in HORIZONTAL || w in VERTICAL);
|
|
2572
|
+
const groups = [];
|
|
2573
|
+
for (let i = 0; i < words.length; i += 1) {
|
|
2574
|
+
const word = words[i];
|
|
2575
|
+
if (!keyword(word))
|
|
2576
|
+
return null;
|
|
2577
|
+
let offset = null;
|
|
2578
|
+
if (i + 1 < words.length && !keyword(words[i + 1])) {
|
|
2579
|
+
if (word === 'center')
|
|
2580
|
+
return null;
|
|
2581
|
+
offset = positionLength(words[i + 1], ctx);
|
|
2582
|
+
if (offset === null)
|
|
2583
|
+
return null;
|
|
2584
|
+
i += 1;
|
|
2585
|
+
}
|
|
2586
|
+
groups.push({ word, offset });
|
|
2587
|
+
}
|
|
2588
|
+
if (groups.length !== 2)
|
|
2589
|
+
return null;
|
|
2590
|
+
let [h, v] = groups;
|
|
2591
|
+
if ((h.word in VERTICAL && !(h.word in HORIZONTAL)) ||
|
|
2592
|
+
(v.word in HORIZONTAL && !(v.word in VERTICAL))) {
|
|
2593
|
+
[h, v] = [v, h];
|
|
2594
|
+
}
|
|
2595
|
+
if (!(h.word in HORIZONTAL) || !(v.word in VERTICAL))
|
|
2596
|
+
return null;
|
|
2597
|
+
const x = fromEdge(HORIZONTAL[h.word], h.offset);
|
|
2598
|
+
const y = fromEdge(VERTICAL[v.word], v.offset);
|
|
2599
|
+
return x === null || y === null ? null : [x, y];
|
|
2600
|
+
}
|
|
2601
|
+
function fromEdge(at, offset) {
|
|
2602
|
+
if (offset === null)
|
|
2603
|
+
return at === 0 ? 0 : { pct: at };
|
|
2604
|
+
if (at === 0)
|
|
2605
|
+
return offset;
|
|
2606
|
+
// in from the right or the bottom: `100% - offset`, which a comparison
|
|
2607
|
+
// (`right min(10%, 20px)`) is not the sum for
|
|
2608
|
+
if (typeof offset === 'number')
|
|
2609
|
+
return { pct: 100, px: -offset };
|
|
2610
|
+
if (offset === AUTO || offset.of)
|
|
2611
|
+
return null;
|
|
2612
|
+
return { pct: 100 - offset.pct, px: -(offset.px ?? 0) };
|
|
2613
|
+
}
|
|
2614
|
+
const ONCE = { times: 1 };
|
|
2615
|
+
const ALL = { pct: 100 };
|
|
2616
|
+
export const NO_BORDER_IMAGE = {
|
|
2617
|
+
source: null,
|
|
2618
|
+
slice: [ALL, ALL, ALL, ALL],
|
|
2619
|
+
fill: false,
|
|
2620
|
+
width: [ONCE, ONCE, ONCE, ONCE],
|
|
2621
|
+
outset: [0, 0, 0, 0],
|
|
2622
|
+
repeat: ['stretch', 'stretch'],
|
|
2623
|
+
};
|
|
2624
|
+
/** One to four values, one a side as `margin` takes them; null where one
|
|
2625
|
+
* is not a value. */
|
|
2626
|
+
function sidesOf(parts, read) {
|
|
2627
|
+
if (!parts.length || parts.length > 4)
|
|
2628
|
+
return null;
|
|
2629
|
+
const values = [];
|
|
2630
|
+
for (const part of parts) {
|
|
2631
|
+
const v = read(part);
|
|
2632
|
+
if (v === null)
|
|
2633
|
+
return null;
|
|
2634
|
+
values.push(v);
|
|
2635
|
+
}
|
|
2636
|
+
return fourSides(values);
|
|
2637
|
+
}
|
|
2638
|
+
/** A slice's depth: a number, image pixels, or a percentage; neither may
|
|
2639
|
+
* be negative. */
|
|
2640
|
+
function sliceOf(part) {
|
|
2641
|
+
if (part.endsWith('%')) {
|
|
2642
|
+
const n = parseNumber(part.slice(0, -1));
|
|
2643
|
+
return n !== null && n >= 0 ? { pct: n } : null;
|
|
2644
|
+
}
|
|
2645
|
+
const n = parseNumber(part);
|
|
2646
|
+
return n !== null && n >= 0 ? n : null;
|
|
2647
|
+
}
|
|
2648
|
+
/** `border-image-slice`: one to four depths and `fill`, before them or
|
|
2649
|
+
* after them. */
|
|
2650
|
+
function borderImageSlice(parts) {
|
|
2651
|
+
const words = parts.map((p) => p.toLowerCase());
|
|
2652
|
+
const at = words.indexOf('fill');
|
|
2653
|
+
let fill = false;
|
|
2654
|
+
if (at >= 0) {
|
|
2655
|
+
if (at !== 0 && at !== words.length - 1)
|
|
2656
|
+
return null;
|
|
2657
|
+
fill = true;
|
|
2658
|
+
words.splice(at, 1);
|
|
2659
|
+
}
|
|
2660
|
+
const slice = sidesOf(words, sliceOf);
|
|
2661
|
+
return slice ? { slice, fill } : null;
|
|
2662
|
+
}
|
|
2663
|
+
/** A border image's width over a side: a multiple, a length or a
|
|
2664
|
+
* percentage of the image area, or `auto`; none negative. */
|
|
2665
|
+
function imageWidthOf(part, ctx) {
|
|
2666
|
+
const n = parseNumber(part);
|
|
2667
|
+
if (n !== null)
|
|
2668
|
+
return n >= 0 ? { times: n } : null;
|
|
2669
|
+
const len = parseLength(part, ctx);
|
|
2670
|
+
if (len === null)
|
|
2671
|
+
return null;
|
|
2672
|
+
if (typeof len === 'number')
|
|
2673
|
+
return len >= 0 ? len : null;
|
|
2674
|
+
return len === 'auto' || (len.pct >= 0 && !len.px) ? len : null;
|
|
2675
|
+
}
|
|
2676
|
+
/** How far a border image reaches out on a side: a multiple, or a
|
|
2677
|
+
* length; neither negative. */
|
|
2678
|
+
function imageOutsetOf(part, ctx) {
|
|
2679
|
+
const n = parseNumber(part);
|
|
2680
|
+
if (n !== null)
|
|
2681
|
+
return n >= 0 ? { times: n } : null;
|
|
2682
|
+
const len = parseLength(part, ctx);
|
|
2683
|
+
return typeof len === 'number' && len >= 0 ? len : null;
|
|
2684
|
+
}
|
|
2685
|
+
const IMAGE_REPEATS = new Set(['stretch', 'repeat', 'round', 'space']);
|
|
2686
|
+
/** `border-image-repeat`: one keyword for both ways, or two. */
|
|
2687
|
+
function imageRepeatOf(words) {
|
|
2688
|
+
if (!words.length || words.length > 2)
|
|
2689
|
+
return null;
|
|
2690
|
+
if (!words.every((w) => IMAGE_REPEATS.has(w)))
|
|
2691
|
+
return null;
|
|
2692
|
+
const [x, y = x] = words;
|
|
2693
|
+
return [x, y];
|
|
2694
|
+
}
|
|
2695
|
+
/**
|
|
2696
|
+
* The `border-image` shorthand: the image, the slices — with a width after
|
|
2697
|
+
* a slash and an outset after a second — and the repeat, in any order; each
|
|
2698
|
+
* at most once, and what it does not name reset (CSS Backgrounds 3, 6.8).
|
|
2699
|
+
*/
|
|
2700
|
+
function readBorderImage(value, ctx) {
|
|
2701
|
+
const parts = splitValue(value).flatMap(splitSlash);
|
|
2702
|
+
const image = { ...NO_BORDER_IMAGE };
|
|
2703
|
+
let seen = 0;
|
|
2704
|
+
const once = (bit) => {
|
|
2705
|
+
if (seen & bit)
|
|
2706
|
+
return false;
|
|
2707
|
+
seen |= bit;
|
|
2708
|
+
return true;
|
|
2709
|
+
};
|
|
2710
|
+
const isSlice = (p) => p.toLowerCase() === 'fill' || sliceOf(p) !== null;
|
|
2711
|
+
for (let i = 0; i < parts.length;) {
|
|
2712
|
+
const part = parts[i];
|
|
2713
|
+
const v = part.toLowerCase();
|
|
2714
|
+
if (v === 'none' || v.startsWith('url(') || IMAGE_FUNCTION.test(v)) {
|
|
2715
|
+
const source = backgroundImageOf(part, ctx);
|
|
2716
|
+
if (source === undefined || !once(1))
|
|
2717
|
+
return null;
|
|
2718
|
+
image.source = source;
|
|
2719
|
+
i += 1;
|
|
2720
|
+
}
|
|
2721
|
+
else if (IMAGE_REPEATS.has(v)) {
|
|
2722
|
+
let end = i + 1;
|
|
2723
|
+
if (end < parts.length && IMAGE_REPEATS.has(parts[end].toLowerCase())) {
|
|
2724
|
+
end += 1;
|
|
2725
|
+
}
|
|
2726
|
+
const repeat = imageRepeatOf(parts.slice(i, end).map((p) => p.toLowerCase()));
|
|
2727
|
+
if (!repeat || !once(2))
|
|
2728
|
+
return null;
|
|
2729
|
+
image.repeat = repeat;
|
|
2730
|
+
i = end;
|
|
2731
|
+
}
|
|
2732
|
+
else if (isSlice(part)) {
|
|
2733
|
+
let end = i;
|
|
2734
|
+
while (end < parts.length && isSlice(parts[end]))
|
|
2735
|
+
end += 1;
|
|
2736
|
+
const slice = borderImageSlice(parts.slice(i, end));
|
|
2737
|
+
if (!slice || !once(4))
|
|
2738
|
+
return null;
|
|
2739
|
+
image.slice = slice.slice;
|
|
2740
|
+
image.fill = slice.fill;
|
|
2741
|
+
i = end;
|
|
2742
|
+
if (parts[i] !== '/')
|
|
2743
|
+
continue;
|
|
2744
|
+
// `/ width`, `/ width / outset` or `/ / outset`
|
|
2745
|
+
i += 1;
|
|
2746
|
+
end = i;
|
|
2747
|
+
while (end < parts.length && imageWidthOf(parts[end], ctx) !== null) {
|
|
2748
|
+
end += 1;
|
|
2749
|
+
}
|
|
2750
|
+
if (end > i) {
|
|
2751
|
+
const width = sidesOf(parts.slice(i, end), (p) => imageWidthOf(p, ctx));
|
|
2752
|
+
if (!width)
|
|
2753
|
+
return null;
|
|
2754
|
+
image.width = width;
|
|
2755
|
+
}
|
|
2756
|
+
const widened = end > i;
|
|
2757
|
+
i = end;
|
|
2758
|
+
if (parts[i] !== '/') {
|
|
2759
|
+
if (!widened)
|
|
2760
|
+
return null;
|
|
2761
|
+
continue;
|
|
2762
|
+
}
|
|
2763
|
+
i += 1;
|
|
2764
|
+
end = i;
|
|
2765
|
+
while (end < parts.length && imageOutsetOf(parts[end], ctx) !== null) {
|
|
2766
|
+
end += 1;
|
|
2767
|
+
}
|
|
2768
|
+
const outset = sidesOf(parts.slice(i, end), (p) => imageOutsetOf(p, ctx));
|
|
2769
|
+
if (!outset)
|
|
2770
|
+
return null;
|
|
2771
|
+
image.outset = outset;
|
|
2772
|
+
i = end;
|
|
2773
|
+
}
|
|
2774
|
+
else {
|
|
2775
|
+
return null;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
return image;
|
|
2779
|
+
}
|
|
2780
|
+
function readRepeat(words) {
|
|
2781
|
+
if (words.length === 1) {
|
|
2782
|
+
const [w] = words;
|
|
2783
|
+
if (w === 'repeat-x')
|
|
2784
|
+
return repeatPair('repeat', 'no-repeat');
|
|
2785
|
+
if (w === 'repeat-y')
|
|
2786
|
+
return repeatPair('no-repeat', 'repeat');
|
|
2787
|
+
return isRepeatMode(w) ? repeatPair(w, w) : null;
|
|
2788
|
+
}
|
|
2789
|
+
if (words.length !== 2)
|
|
2790
|
+
return null;
|
|
2791
|
+
const [x, y] = words;
|
|
2792
|
+
return isRepeatMode(x) && isRepeatMode(y) ? repeatPair(x, y) : null;
|
|
2793
|
+
}
|
|
2794
|
+
const REPEATS = new Set(['repeat', 'space', 'round', 'no-repeat']);
|
|
2795
|
+
function isRepeatMode(word) {
|
|
2796
|
+
return REPEATS.has(word);
|
|
2797
|
+
}
|
|
2798
|
+
const REPEAT_PAIRS = new Map();
|
|
2799
|
+
/** The one pair for each way of repeating. */
|
|
2800
|
+
function repeatPair(x, y) {
|
|
2801
|
+
const key = `${x} ${y}`;
|
|
2802
|
+
let pair = REPEAT_PAIRS.get(key);
|
|
2803
|
+
if (!pair) {
|
|
2804
|
+
pair = Object.freeze([x, y]);
|
|
2805
|
+
REPEAT_PAIRS.set(key, pair);
|
|
2806
|
+
}
|
|
2807
|
+
return pair;
|
|
2808
|
+
}
|
|
2809
|
+
const REPEAT = repeatPair('repeat', 'repeat');
|
|
885
2810
|
function applyBorderShorthand(style, name, value, ctx) {
|
|
886
2811
|
const sides = name === 'border'
|
|
887
2812
|
? ['Top', 'Right', 'Bottom', 'Left']
|
|
@@ -891,30 +2816,66 @@ function applyBorderShorthand(style, name, value, ctx) {
|
|
|
891
2816
|
// `border: none` and `border: 0` both mean "no border", and neither names
|
|
892
2817
|
// all three components — so the shorthand resets all three first, which is
|
|
893
2818
|
// what the spec says and what an author relies on to undo a UA border.
|
|
894
|
-
let width =
|
|
2819
|
+
let width = BORDER_WIDTH_KEYWORDS.medium * ctx.scale;
|
|
895
2820
|
let borderStyle = 'none';
|
|
896
2821
|
let color = 'currentColor';
|
|
2822
|
+
// each of the three at most once (`<line-width> || <line-style> ||
|
|
2823
|
+
// <color>`): `red solid 16px red` is no border, where the second colour
|
|
2824
|
+
// was taken for the first
|
|
2825
|
+
let seen = 0;
|
|
897
2826
|
for (const part of splitValue(value)) {
|
|
898
2827
|
const v = part.toLowerCase();
|
|
899
2828
|
if (BORDER_STYLES.has(v)) {
|
|
2829
|
+
if (seen & 1)
|
|
2830
|
+
return;
|
|
2831
|
+
seen |= 1;
|
|
900
2832
|
borderStyle = v;
|
|
901
2833
|
continue;
|
|
902
2834
|
}
|
|
903
2835
|
const w = borderWidth(part, ctx);
|
|
904
2836
|
if (w !== null) {
|
|
2837
|
+
if (seen & 2)
|
|
2838
|
+
return;
|
|
2839
|
+
seen |= 2;
|
|
905
2840
|
width = w;
|
|
906
2841
|
continue;
|
|
907
2842
|
}
|
|
908
2843
|
const c = parseColor(part);
|
|
909
|
-
if (c !== null)
|
|
2844
|
+
if (c !== null) {
|
|
2845
|
+
if (seen & 4)
|
|
2846
|
+
return;
|
|
2847
|
+
seen |= 4;
|
|
910
2848
|
color = c;
|
|
2849
|
+
continue;
|
|
2850
|
+
}
|
|
2851
|
+
// a negative width makes the whole shorthand invalid
|
|
2852
|
+
const len = parseLength(part, ctx);
|
|
2853
|
+
if (typeof len === 'number' && len < 0)
|
|
2854
|
+
return;
|
|
2855
|
+
}
|
|
2856
|
+
// A width with no style is kept rather than zeroed — the layout draws
|
|
2857
|
+
// nothing for `none` — so a later `border-style` alone finds it.
|
|
2858
|
+
if (name === 'border') {
|
|
2859
|
+
// named, not built: preflight's `border: 0 solid` is on every element,
|
|
2860
|
+
// and twelve stores through a computed name were most of its cost
|
|
2861
|
+
style.borderTopWidth = width;
|
|
2862
|
+
style.borderRightWidth = width;
|
|
2863
|
+
style.borderBottomWidth = width;
|
|
2864
|
+
style.borderLeftWidth = width;
|
|
2865
|
+
style.borderTopStyle = borderStyle;
|
|
2866
|
+
style.borderRightStyle = borderStyle;
|
|
2867
|
+
style.borderBottomStyle = borderStyle;
|
|
2868
|
+
style.borderLeftStyle = borderStyle;
|
|
2869
|
+
if (color !== null) {
|
|
2870
|
+
style.borderTopColor = color;
|
|
2871
|
+
style.borderRightColor = color;
|
|
2872
|
+
style.borderBottomColor = color;
|
|
2873
|
+
style.borderLeftColor = color;
|
|
2874
|
+
}
|
|
2875
|
+
return;
|
|
911
2876
|
}
|
|
912
|
-
// A shorthand with a style but no width takes the initial `medium`; one
|
|
913
|
-
// with neither paints nothing, so the width is irrelevant.
|
|
914
|
-
const effective = borderStyle === 'none' || borderStyle === 'hidden' ? 0 : (width ?? 3);
|
|
915
2877
|
for (const side of sides) {
|
|
916
|
-
style[`border${side}Width`] =
|
|
917
|
-
effective;
|
|
2878
|
+
style[`border${side}Width`] = width;
|
|
918
2879
|
style[`border${side}Style`] =
|
|
919
2880
|
borderStyle;
|
|
920
2881
|
if (color !== null)
|
|
@@ -922,53 +2883,541 @@ function applyBorderShorthand(style, name, value, ctx) {
|
|
|
922
2883
|
color;
|
|
923
2884
|
}
|
|
924
2885
|
}
|
|
2886
|
+
/** A font family name: quoted, or a sequence of identifiers. */
|
|
2887
|
+
function isFamilyName(name) {
|
|
2888
|
+
const n = name.trim();
|
|
2889
|
+
if (n[0] === '"' || n[0] === "'")
|
|
2890
|
+
return true;
|
|
2891
|
+
if (!n)
|
|
2892
|
+
return false;
|
|
2893
|
+
for (const word of n.split(/\s+/)) {
|
|
2894
|
+
if (!startsIdent(word, 0) || readIdent(word, 0).end !== word.length) {
|
|
2895
|
+
return false;
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
return true;
|
|
2899
|
+
}
|
|
925
2900
|
function applyBackgroundShorthand(style, value, ctx) {
|
|
926
|
-
//
|
|
927
|
-
//
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
2901
|
+
// A layer a comma group, top first: each has to be a layer, and only the
|
|
2902
|
+
// last, which is painted against the box, may have a colour, or the
|
|
2903
|
+
// declaration is none
|
|
2904
|
+
const texts = splitCommas(value);
|
|
2905
|
+
const layers = [];
|
|
2906
|
+
for (let k = 0; k < texts.length; k += 1) {
|
|
2907
|
+
const layer = readBackgroundLayer(texts[k], ctx, k === texts.length - 1);
|
|
2908
|
+
if (!layer)
|
|
2909
|
+
return;
|
|
2910
|
+
layers.push(layer);
|
|
2911
|
+
}
|
|
2912
|
+
if (!layers.length)
|
|
2913
|
+
return;
|
|
2914
|
+
const [top] = layers;
|
|
2915
|
+
style.backgroundClipText = false;
|
|
2916
|
+
style.backgroundColor = layers[layers.length - 1].color;
|
|
2917
|
+
style.backgroundImage = top.image;
|
|
2918
|
+
style.backgroundGradient = top.gradient;
|
|
2919
|
+
style.backgroundRepeat = top.repeat;
|
|
2920
|
+
style.backgroundSize = top.size;
|
|
2921
|
+
style.backgroundAttachment = top.attachment;
|
|
2922
|
+
style.backgroundOrigin = top.origin;
|
|
2923
|
+
style.backgroundClip = top.clip;
|
|
2924
|
+
[style.backgroundPositionX, style.backgroundPositionY] = top.position ?? [
|
|
2925
|
+
0, 0,
|
|
2926
|
+
];
|
|
2927
|
+
const many = layers.length > 1;
|
|
2928
|
+
style.backgroundImages = many
|
|
2929
|
+
? layers.map((l) => l.image ?? l.gradient)
|
|
2930
|
+
: null;
|
|
2931
|
+
style.backgroundRepeats = many ? layers.map((l) => l.repeat) : null;
|
|
2932
|
+
style.backgroundSizes = many ? layers.map((l) => l.size) : null;
|
|
2933
|
+
style.backgroundAttachments = many ? layers.map((l) => l.attachment) : null;
|
|
2934
|
+
style.backgroundOrigins = many ? layers.map((l) => l.origin) : null;
|
|
2935
|
+
style.backgroundClips = many ? layers.map((l) => l.clip) : null;
|
|
2936
|
+
style.backgroundPositions = many
|
|
2937
|
+
? layers.map((l) => l.position ?? [0, 0])
|
|
2938
|
+
: null;
|
|
2939
|
+
}
|
|
2940
|
+
/** One layer's `background-image`: a url, a gradient — none where it is
|
|
2941
|
+
* one this does not draw, as though there were none — or none; undefined
|
|
2942
|
+
* where it is not an image at all. */
|
|
2943
|
+
function backgroundImageOf(text, ctx) {
|
|
2944
|
+
const v = text.trim();
|
|
2945
|
+
if (IMAGE_FUNCTION.test(v.toLowerCase()))
|
|
2946
|
+
return parseLinearGradient(v, ctx);
|
|
2947
|
+
return parseUrl(v);
|
|
2948
|
+
}
|
|
2949
|
+
/** A background property's values, one a comma-separated layer, each read
|
|
2950
|
+
* by `read`; null where one is not a value, which drops the declaration. */
|
|
2951
|
+
function layerValues(value, read) {
|
|
2952
|
+
const out = [];
|
|
2953
|
+
for (const part of splitCommas(value)) {
|
|
2954
|
+
const v = read(part.trim());
|
|
2955
|
+
if (v === null)
|
|
2956
|
+
return null;
|
|
2957
|
+
out.push(v);
|
|
2958
|
+
}
|
|
2959
|
+
return out.length ? out : null;
|
|
2960
|
+
}
|
|
2961
|
+
/**
|
|
2962
|
+
* One layer of the `background` shorthand, read whole before anything is
|
|
2963
|
+
* set: each part at most once, and a token that is none of them — a
|
|
2964
|
+
* string, `red\;`, a second colour — makes the declaration invalid, and it
|
|
2965
|
+
* is dropped rather than resetting the background it meant to replace
|
|
2966
|
+
* (CSS 2.1 4.2). What CSS3 adds is read too, so that a declaration a
|
|
2967
|
+
* browser keeps is kept: `/ cover` after the position, `space` and `round`,
|
|
2968
|
+
* a gradient — drawn as nothing, over the layer's colour.
|
|
2969
|
+
*/
|
|
2970
|
+
function readBackgroundLayer(text, ctx, last) {
|
|
2971
|
+
const parts = splitValue(text).flatMap(splitSlash);
|
|
2972
|
+
const layer = {
|
|
2973
|
+
color: null,
|
|
2974
|
+
image: null,
|
|
2975
|
+
gradient: null,
|
|
2976
|
+
repeat: REPEAT,
|
|
2977
|
+
size: 'auto',
|
|
2978
|
+
attachment: 'scroll',
|
|
2979
|
+
origin: 'padding-box',
|
|
2980
|
+
clip: 'border-box',
|
|
2981
|
+
position: null,
|
|
2982
|
+
};
|
|
2983
|
+
let seen = 0;
|
|
2984
|
+
const once = (bit) => {
|
|
2985
|
+
if (seen & bit)
|
|
2986
|
+
return false;
|
|
2987
|
+
seen |= bit;
|
|
2988
|
+
return true;
|
|
2989
|
+
};
|
|
2990
|
+
let boxes = 0;
|
|
2991
|
+
for (let i = 0; i < parts.length;) {
|
|
2992
|
+
const part = parts[i];
|
|
935
2993
|
const v = part.toLowerCase();
|
|
936
|
-
if (v.startsWith('url(')) {
|
|
937
|
-
|
|
938
|
-
|
|
2994
|
+
if (v === 'none' || v.startsWith('url(') || IMAGE_FUNCTION.test(v)) {
|
|
2995
|
+
if (!once(1))
|
|
2996
|
+
return null;
|
|
2997
|
+
if (v.startsWith('url(')) {
|
|
2998
|
+
const url = parseUrl(part);
|
|
2999
|
+
if (url === undefined)
|
|
3000
|
+
return null;
|
|
3001
|
+
layer.image = url;
|
|
3002
|
+
}
|
|
3003
|
+
else if (v !== 'none')
|
|
3004
|
+
layer.gradient = parseLinearGradient(part, ctx);
|
|
3005
|
+
i += 1;
|
|
939
3006
|
}
|
|
940
|
-
if (v === 'repeat' ||
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
v
|
|
944
|
-
|
|
945
|
-
|
|
3007
|
+
else if (v === 'repeat-x' || v === 'repeat-y' || REPEATS.has(v)) {
|
|
3008
|
+
if (!once(2))
|
|
3009
|
+
return null;
|
|
3010
|
+
const pair = REPEATS.has(v) && REPEATS.has(parts[i + 1]?.toLowerCase());
|
|
3011
|
+
const repeat = readRepeat(pair ? [v, parts[i + 1].toLowerCase()] : [v]);
|
|
3012
|
+
if (!repeat)
|
|
3013
|
+
return null;
|
|
3014
|
+
layer.repeat = repeat;
|
|
3015
|
+
i += pair ? 2 : 1;
|
|
946
3016
|
}
|
|
947
|
-
if (v === '
|
|
948
|
-
|
|
949
|
-
|
|
3017
|
+
else if (v === 'scroll' || v === 'fixed' || v === 'local') {
|
|
3018
|
+
if (!once(4))
|
|
3019
|
+
return null;
|
|
3020
|
+
layer.attachment = v;
|
|
3021
|
+
i += 1;
|
|
950
3022
|
}
|
|
951
|
-
if (v
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
3023
|
+
else if (isVisualBox(v)) {
|
|
3024
|
+
// one box is both the origin and the clip, and a second the clip —
|
|
3025
|
+
// which `border-area` is instead of, and the one box the origin
|
|
3026
|
+
if ((boxes += 1) > (seen & 32 ? 1 : 2))
|
|
3027
|
+
return null;
|
|
3028
|
+
if (boxes === 1)
|
|
3029
|
+
layer.origin = v;
|
|
3030
|
+
if (!(seen & 32))
|
|
3031
|
+
layer.clip = v;
|
|
3032
|
+
i += 1;
|
|
958
3033
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
3034
|
+
else if (v === 'border-area') {
|
|
3035
|
+
if (boxes > 1 || !once(32))
|
|
3036
|
+
return null;
|
|
3037
|
+
layer.clip = v;
|
|
3038
|
+
i += 1;
|
|
3039
|
+
}
|
|
3040
|
+
else if (isPositionPart(v, ctx)) {
|
|
3041
|
+
if (!once(8))
|
|
3042
|
+
return null;
|
|
3043
|
+
let end = i;
|
|
3044
|
+
while (end < parts.length &&
|
|
3045
|
+
isPositionPart(parts[end].toLowerCase(), ctx)) {
|
|
3046
|
+
end += 1;
|
|
3047
|
+
}
|
|
3048
|
+
layer.position = positionPair(parts.slice(i, end), ctx);
|
|
3049
|
+
if (!layer.position)
|
|
3050
|
+
return null;
|
|
3051
|
+
i = end;
|
|
3052
|
+
if (parts[i] !== '/')
|
|
3053
|
+
continue;
|
|
3054
|
+
// `/ <size>`: `cover`, `contain`, or one or two lengths
|
|
3055
|
+
const first = parts[i + 1]?.toLowerCase();
|
|
3056
|
+
if (first === 'cover' || first === 'contain') {
|
|
3057
|
+
layer.size = first;
|
|
3058
|
+
i += 2;
|
|
3059
|
+
}
|
|
3060
|
+
else if (first !== undefined && isSizePart(first, ctx)) {
|
|
3061
|
+
const two = i + 2 < parts.length && isSizePart(parts[i + 2].toLowerCase(), ctx);
|
|
3062
|
+
layer.size =
|
|
3063
|
+
backgroundSizeOf(parts.slice(i + 1, i + (two ? 3 : 2)), ctx) ??
|
|
3064
|
+
'auto';
|
|
3065
|
+
i += two ? 3 : 2;
|
|
3066
|
+
}
|
|
3067
|
+
else {
|
|
3068
|
+
return null;
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
else {
|
|
3072
|
+
const color = parseColor(part);
|
|
3073
|
+
if (color === null || !last || !once(16))
|
|
3074
|
+
return null;
|
|
3075
|
+
layer.color = color;
|
|
3076
|
+
i += 1;
|
|
963
3077
|
}
|
|
964
|
-
const pos = backgroundPosition(part, ctx);
|
|
965
|
-
if (pos !== null)
|
|
966
|
-
positions.push(pos);
|
|
967
3078
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
3079
|
+
return layer;
|
|
3080
|
+
}
|
|
3081
|
+
/**
|
|
3082
|
+
* A `linear-gradient()`, or null for any other image function and for one
|
|
3083
|
+
* that is no gradient: a direction by angle, by side or by corner, with a
|
|
3084
|
+
* colour interpolation method (`in oklab`, which Tailwind 4 writes) read and
|
|
3085
|
+
* not honoured, and at least two stops, each with no position, one, or two;
|
|
3086
|
+
* a bare percentage between stops, a hint, is passed over.
|
|
3087
|
+
*/
|
|
3088
|
+
function parseLinearGradient(text, ctx) {
|
|
3089
|
+
const m = /^linear-gradient\((.*)\)$/is.exec(text.trim());
|
|
3090
|
+
if (!m)
|
|
3091
|
+
return null;
|
|
3092
|
+
const args = splitCommas(m[1]).map((a) => a.trim());
|
|
3093
|
+
const out = { angle: Math.PI, corner: null, stops: [] };
|
|
3094
|
+
const first = (args[0] ?? '')
|
|
3095
|
+
.toLowerCase()
|
|
3096
|
+
.replace(/\bin\s+[a-z-]+(?:\s+(?:shorter|longer|increasing|decreasing)\s+hue)?/, '')
|
|
3097
|
+
.trim();
|
|
3098
|
+
let from = 0;
|
|
3099
|
+
if (!first || first.startsWith('to ')) {
|
|
3100
|
+
from = 1;
|
|
3101
|
+
const sides = first.split(/\s+/).slice(1).sort();
|
|
3102
|
+
const side = sides.join(' ');
|
|
3103
|
+
const SIDES = {
|
|
3104
|
+
top: 0,
|
|
3105
|
+
right: Math.PI / 2,
|
|
3106
|
+
bottom: Math.PI,
|
|
3107
|
+
left: (3 * Math.PI) / 2,
|
|
3108
|
+
};
|
|
3109
|
+
if (sides.length === 1 && side in SIDES)
|
|
3110
|
+
out.angle = SIDES[side];
|
|
3111
|
+
else if (sides.length === 2) {
|
|
3112
|
+
const [a, b] = sides;
|
|
3113
|
+
const vertical = a === 'bottom' || a === 'top' ? a : b;
|
|
3114
|
+
const horizontal = a === 'left' || a === 'right' ? a : b;
|
|
3115
|
+
if (vertical === horizontal)
|
|
3116
|
+
return null;
|
|
3117
|
+
out.corner = `${vertical} ${horizontal}`;
|
|
3118
|
+
}
|
|
3119
|
+
else if (sides.length)
|
|
3120
|
+
return null;
|
|
3121
|
+
}
|
|
3122
|
+
else {
|
|
3123
|
+
const angle = /^(-?\d*\.?\d+)(deg|rad|grad|turn)?$/.exec(first);
|
|
3124
|
+
if (angle) {
|
|
3125
|
+
const n = Number(angle[1]);
|
|
3126
|
+
const unit = angle[2] ?? (n === 0 ? 'deg' : '');
|
|
3127
|
+
const turns = {
|
|
3128
|
+
deg: 1 / 360,
|
|
3129
|
+
rad: 1 / (2 * Math.PI),
|
|
3130
|
+
grad: 1 / 400,
|
|
3131
|
+
turn: 1,
|
|
3132
|
+
};
|
|
3133
|
+
if (!(unit in turns))
|
|
3134
|
+
return null;
|
|
3135
|
+
out.angle = n * turns[unit] * 2 * Math.PI;
|
|
3136
|
+
from = 1;
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
for (const arg of args.slice(from)) {
|
|
3140
|
+
const parts = splitValue(arg);
|
|
3141
|
+
const color = parseColor(parts[0] ?? '');
|
|
3142
|
+
if (color === null) {
|
|
3143
|
+
// a hint, the midpoint of the transition, which this does not move
|
|
3144
|
+
if (parts.length === 1 && parseLength(parts[0], ctx) !== null)
|
|
3145
|
+
continue;
|
|
3146
|
+
return null;
|
|
3147
|
+
}
|
|
3148
|
+
if (parts.length > 3)
|
|
3149
|
+
return null;
|
|
3150
|
+
const at = parts.slice(1).map((p) => parseLength(p, ctx));
|
|
3151
|
+
if (at.some((p) => p === null || p === AUTO))
|
|
3152
|
+
return null;
|
|
3153
|
+
if (!at.length)
|
|
3154
|
+
out.stops.push({ color, at: null });
|
|
3155
|
+
for (const p of at)
|
|
3156
|
+
out.stops.push({ color, at: p });
|
|
971
3157
|
}
|
|
3158
|
+
return out.stops.length >= 2 ? out : null;
|
|
3159
|
+
}
|
|
3160
|
+
/** The images CSS3 has beyond `url()`, which a layer may name: a linear
|
|
3161
|
+
* gradient is drawn, and the rest draw as nothing. */
|
|
3162
|
+
const IMAGE_FUNCTION = /^(?:-(?:webkit|moz|o|ms)-)?(?:(?:repeating-)?(?:linear|radial|conic)-gradient|gradient|image-set|cross-fade|element|paint)\(/;
|
|
3163
|
+
function isPositionPart(word, ctx) {
|
|
3164
|
+
return (word in HORIZONTAL || word in VERTICAL || positionLength(word, ctx) !== null);
|
|
3165
|
+
}
|
|
3166
|
+
function isSizePart(word, ctx) {
|
|
3167
|
+
if (word === 'auto')
|
|
3168
|
+
return true;
|
|
3169
|
+
const len = positionLength(word, ctx);
|
|
3170
|
+
if (len === null || len === AUTO)
|
|
3171
|
+
return false;
|
|
3172
|
+
return typeof len === 'number' ? len >= 0 : len.pct >= 0;
|
|
3173
|
+
}
|
|
3174
|
+
/** What each `white-space` keeps of the white space (CSS Text 4, 3.1). */
|
|
3175
|
+
const COLLAPSE_OF = {
|
|
3176
|
+
normal: 'collapse',
|
|
3177
|
+
nowrap: 'collapse',
|
|
3178
|
+
pre: 'preserve',
|
|
3179
|
+
'pre-wrap': 'preserve',
|
|
3180
|
+
'pre-line': 'preserve-breaks',
|
|
3181
|
+
};
|
|
3182
|
+
function wrapsIn(whiteSpace) {
|
|
3183
|
+
return whiteSpace !== 'nowrap' && whiteSpace !== 'pre';
|
|
3184
|
+
}
|
|
3185
|
+
/** The `white-space` of a collapse and a wrap. Kept breaks and no wrapping
|
|
3186
|
+
* have no keyword of their own, and keep their breaks, as `pre-line`. */
|
|
3187
|
+
function whiteSpaceOf(collapse, wrap) {
|
|
3188
|
+
if (collapse === 'collapse')
|
|
3189
|
+
return wrap ? 'normal' : 'nowrap';
|
|
3190
|
+
if (collapse === 'preserve')
|
|
3191
|
+
return wrap ? 'pre-wrap' : 'pre';
|
|
3192
|
+
return 'pre-line';
|
|
3193
|
+
}
|
|
3194
|
+
/**
|
|
3195
|
+
* The translation a `transform` makes: the sum of its translate functions'
|
|
3196
|
+
* and its matrices' — `translate(-50%, -50%)`, Tailwind 3's `translate(
|
|
3197
|
+
* var(--tw-translate-x), var(--tw-translate-y)) rotate(…) skewX(…) …` —
|
|
3198
|
+
* with the functions that rotate, scale, skew or add perspective read and
|
|
3199
|
+
* not drawn. Null for `none`, and undefined for what is no transform list,
|
|
3200
|
+
* which drops the declaration.
|
|
3201
|
+
*/
|
|
3202
|
+
function transformTranslation(value, ctx) {
|
|
3203
|
+
if (value.trim().toLowerCase() === 'none')
|
|
3204
|
+
return null;
|
|
3205
|
+
let x = 0;
|
|
3206
|
+
let y = 0;
|
|
3207
|
+
const parts = splitValue(value);
|
|
3208
|
+
if (!parts.length)
|
|
3209
|
+
return undefined;
|
|
3210
|
+
for (const part of parts) {
|
|
3211
|
+
const m = /^([a-z0-9]+)\((.*)\)$/is.exec(part.trim());
|
|
3212
|
+
if (!m)
|
|
3213
|
+
return undefined;
|
|
3214
|
+
const name = m[1].toLowerCase();
|
|
3215
|
+
const args = splitCommas(m[2]).map((a) => a.trim());
|
|
3216
|
+
let dx = 0;
|
|
3217
|
+
let dy = 0;
|
|
3218
|
+
if (name === 'translate' || name === 'translate3d') {
|
|
3219
|
+
dx = parseLength(args[0] ?? '', ctx);
|
|
3220
|
+
dy = args[1] === undefined ? 0 : parseLength(args[1], ctx);
|
|
3221
|
+
}
|
|
3222
|
+
else if (name === 'translatex')
|
|
3223
|
+
dx = parseLength(args[0] ?? '', ctx);
|
|
3224
|
+
else if (name === 'translatey')
|
|
3225
|
+
dy = parseLength(args[0] ?? '', ctx);
|
|
3226
|
+
else if (name === 'matrix' || name === 'matrix3d') {
|
|
3227
|
+
// the translation is the last column's
|
|
3228
|
+
const n = args.map(Number);
|
|
3229
|
+
if (n.length !== (name === 'matrix' ? 6 : 16) || n.some(isNaN)) {
|
|
3230
|
+
return undefined;
|
|
3231
|
+
}
|
|
3232
|
+
[dx, dy] = name === 'matrix' ? [n[4], n[5]] : [n[12], n[13]];
|
|
3233
|
+
}
|
|
3234
|
+
else if (!TRANSFORMS.has(name))
|
|
3235
|
+
return undefined;
|
|
3236
|
+
if (dx === null || dx === AUTO || dy === null || dy === AUTO) {
|
|
3237
|
+
return undefined;
|
|
3238
|
+
}
|
|
3239
|
+
const sx = addLen(x, dx);
|
|
3240
|
+
const sy = addLen(y, dy);
|
|
3241
|
+
if (sx === null || sy === null)
|
|
3242
|
+
return undefined;
|
|
3243
|
+
x = sx;
|
|
3244
|
+
y = sy;
|
|
3245
|
+
}
|
|
3246
|
+
return [x, y];
|
|
3247
|
+
}
|
|
3248
|
+
/** The transform functions read and not drawn. */
|
|
3249
|
+
const TRANSFORMS = new Set([
|
|
3250
|
+
'translatez',
|
|
3251
|
+
'rotate',
|
|
3252
|
+
'rotatex',
|
|
3253
|
+
'rotatey',
|
|
3254
|
+
'rotatez',
|
|
3255
|
+
'rotate3d',
|
|
3256
|
+
'scale',
|
|
3257
|
+
'scalex',
|
|
3258
|
+
'scaley',
|
|
3259
|
+
'scalez',
|
|
3260
|
+
'scale3d',
|
|
3261
|
+
'skew',
|
|
3262
|
+
'skewx',
|
|
3263
|
+
'skewy',
|
|
3264
|
+
'perspective',
|
|
3265
|
+
]);
|
|
3266
|
+
/** Two lengths added, a percentage and a length kept apart; null where
|
|
3267
|
+
* one is a `min()` or a `max()`, which a sum cannot be kept of. */
|
|
3268
|
+
function addLen(a, b) {
|
|
3269
|
+
if (a === AUTO || b === AUTO)
|
|
3270
|
+
return null;
|
|
3271
|
+
if (typeof a === 'number' && typeof b === 'number')
|
|
3272
|
+
return a + b;
|
|
3273
|
+
if (a === 0)
|
|
3274
|
+
return b;
|
|
3275
|
+
if (b === 0)
|
|
3276
|
+
return a;
|
|
3277
|
+
const pa = typeof a === 'number' ? { pct: 0, px: a } : a;
|
|
3278
|
+
const pb = typeof b === 'number' ? { pct: 0, px: b } : b;
|
|
3279
|
+
if (pa.of || pb.of)
|
|
3280
|
+
return null;
|
|
3281
|
+
return { pct: pa.pct + pb.pct, px: (pa.px ?? 0) + (pb.px ?? 0) };
|
|
3282
|
+
}
|
|
3283
|
+
/** An intrinsic size keyword, with the prefixes browsers still read, or
|
|
3284
|
+
* `fit-content()` of a length or a percentage (CSS Sizing 3, 3.1) — a
|
|
3285
|
+
* width's, where a height's is its content height as the keyword is;
|
|
3286
|
+
* null for anything else. */
|
|
3287
|
+
function contentSizeOf(value, ctx, inline) {
|
|
3288
|
+
const word = value.trim().toLowerCase();
|
|
3289
|
+
if (word === 'stretch' ||
|
|
3290
|
+
word === '-webkit-fill-available' ||
|
|
3291
|
+
word === '-moz-available') {
|
|
3292
|
+
return 'stretch';
|
|
3293
|
+
}
|
|
3294
|
+
const v = word.replace(/^-(?:webkit|moz)-/, '');
|
|
3295
|
+
if (v === 'min-content' || v === 'max-content' || v === 'fit-content') {
|
|
3296
|
+
return v;
|
|
3297
|
+
}
|
|
3298
|
+
const m = /^fit-content\((.*)\)$/s.exec(v);
|
|
3299
|
+
if (!m)
|
|
3300
|
+
return null;
|
|
3301
|
+
const fit = parseLength(m[1], ctx);
|
|
3302
|
+
if (fit === null || fit === AUTO || !notNegative(fit))
|
|
3303
|
+
return null;
|
|
3304
|
+
return inline ? { fit } : 'fit-content';
|
|
3305
|
+
}
|
|
3306
|
+
/** A `background-size`: `cover`, `contain`, `auto`, or one or two of a
|
|
3307
|
+
* length, a percentage and `auto`, the second `auto` where there is none;
|
|
3308
|
+
* null for what is none of these. */
|
|
3309
|
+
function backgroundSizeOf(parts, ctx) {
|
|
3310
|
+
const words = parts.map((p) => p.toLowerCase());
|
|
3311
|
+
if (words.length === 1 && (words[0] === 'cover' || words[0] === 'contain')) {
|
|
3312
|
+
return words[0];
|
|
3313
|
+
}
|
|
3314
|
+
if (!words.length || words.length > 2)
|
|
3315
|
+
return null;
|
|
3316
|
+
if (!words.every((w) => isSizePart(w, ctx)))
|
|
3317
|
+
return null;
|
|
3318
|
+
const axis = (w) => {
|
|
3319
|
+
if (w === undefined || w === 'auto')
|
|
3320
|
+
return 'auto';
|
|
3321
|
+
const len = positionLength(w, ctx);
|
|
3322
|
+
return len === null || len === AUTO ? 'auto' : len;
|
|
3323
|
+
};
|
|
3324
|
+
const width = axis(words[0]);
|
|
3325
|
+
const height = axis(words[1]);
|
|
3326
|
+
return width === 'auto' && height === 'auto' ? 'auto' : [width, height];
|
|
3327
|
+
}
|
|
3328
|
+
/** `center/cover` is three tokens, as `center / cover` is. */
|
|
3329
|
+
function splitSlash(part) {
|
|
3330
|
+
if (!part.includes('/') || part.includes('('))
|
|
3331
|
+
return [part];
|
|
3332
|
+
return part.split(/(\/)/).filter(Boolean);
|
|
3333
|
+
}
|
|
3334
|
+
/** Each `font-variant` keyword: the longhand it belongs to and the features
|
|
3335
|
+
* it stands for (CSS Fonts 3, 6). */
|
|
3336
|
+
const VARIANTS = {
|
|
3337
|
+
'lining-nums': ['numeric', 'lnum=1'],
|
|
3338
|
+
'oldstyle-nums': ['numeric', 'onum=1'],
|
|
3339
|
+
'proportional-nums': ['numeric', 'pnum=1'],
|
|
3340
|
+
'tabular-nums': ['numeric', 'tnum=1'],
|
|
3341
|
+
'diagonal-fractions': ['numeric', 'frac=1'],
|
|
3342
|
+
'stacked-fractions': ['numeric', 'afrc=1'],
|
|
3343
|
+
ordinal: ['numeric', 'ordn=1'],
|
|
3344
|
+
'slashed-zero': ['numeric', 'zero=1'],
|
|
3345
|
+
'small-caps': ['caps', 'smcp=1'],
|
|
3346
|
+
'all-small-caps': ['caps', 'c2sc=1,smcp=1'],
|
|
3347
|
+
'petite-caps': ['caps', 'pcap=1'],
|
|
3348
|
+
'all-petite-caps': ['caps', 'c2pc=1,pcap=1'],
|
|
3349
|
+
unicase: ['caps', 'unic=1'],
|
|
3350
|
+
'titling-caps': ['caps', 'titl=1'],
|
|
3351
|
+
'common-ligatures': ['ligatures', 'liga=1,clig=1'],
|
|
3352
|
+
'no-common-ligatures': ['ligatures', 'liga=0,clig=0'],
|
|
3353
|
+
'discretionary-ligatures': ['ligatures', 'dlig=1'],
|
|
3354
|
+
'no-discretionary-ligatures': ['ligatures', 'dlig=0'],
|
|
3355
|
+
'historical-ligatures': ['ligatures', 'hlig=1'],
|
|
3356
|
+
'no-historical-ligatures': ['ligatures', 'hlig=0'],
|
|
3357
|
+
contextual: ['ligatures', 'calt=1'],
|
|
3358
|
+
'no-contextual': ['ligatures', 'calt=0'],
|
|
3359
|
+
sub: ['position', 'subs=1'],
|
|
3360
|
+
super: ['position', 'sups=1'],
|
|
3361
|
+
};
|
|
3362
|
+
const VARIANT_FIELDS = {
|
|
3363
|
+
numeric: 'fontVariantNumeric',
|
|
3364
|
+
caps: 'fontVariantCaps',
|
|
3365
|
+
ligatures: 'fontVariantLigatures',
|
|
3366
|
+
position: 'fontVariantPosition',
|
|
3367
|
+
};
|
|
3368
|
+
const ALL_VARIANTS = [
|
|
3369
|
+
'numeric',
|
|
3370
|
+
'caps',
|
|
3371
|
+
'ligatures',
|
|
3372
|
+
'position',
|
|
3373
|
+
];
|
|
3374
|
+
/**
|
|
3375
|
+
* A `font-variant` longhand's value, or the shorthand's (`kind` null), as
|
|
3376
|
+
* the fields it sets; null for a value it does not take. The shorthand
|
|
3377
|
+
* sets all four, the ones it names nothing of back to `normal`.
|
|
3378
|
+
*/
|
|
3379
|
+
function parseFontVariant(value, kind) {
|
|
3380
|
+
const words = value.trim().toLowerCase().split(/\s+/);
|
|
3381
|
+
const kinds = kind ? [kind] : ALL_VARIANTS;
|
|
3382
|
+
const named = {};
|
|
3383
|
+
if (words.length === 1 && words[0] === 'normal') {
|
|
3384
|
+
// every longhand the value sets goes back to none
|
|
3385
|
+
}
|
|
3386
|
+
else if (words.length === 1 &&
|
|
3387
|
+
words[0] === 'none' &&
|
|
3388
|
+
(kind === null || kind === 'ligatures')) {
|
|
3389
|
+
named.ligatures = ['liga=0,clig=0,dlig=0,hlig=0,calt=0'];
|
|
3390
|
+
}
|
|
3391
|
+
else {
|
|
3392
|
+
for (const word of words) {
|
|
3393
|
+
const known = VARIANTS[word];
|
|
3394
|
+
if (!known || !kinds.includes(known[0]))
|
|
3395
|
+
return null;
|
|
3396
|
+
(named[known[0]] ??= []).push(known[1]);
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
const fields = {};
|
|
3400
|
+
for (const k of kinds) {
|
|
3401
|
+
fields[VARIANT_FIELDS[k]] = (named[k] ?? []).join(',');
|
|
3402
|
+
}
|
|
3403
|
+
return fields;
|
|
3404
|
+
}
|
|
3405
|
+
/** `font-feature-settings`: `"tnum", "liga" 0, "ss01" on` as `tnum=1,…`,
|
|
3406
|
+
* '' for `normal`, and null for a value that is neither. */
|
|
3407
|
+
function parseFeatureSettings(value) {
|
|
3408
|
+
const v = value.trim();
|
|
3409
|
+
if (v.toLowerCase() === 'normal')
|
|
3410
|
+
return '';
|
|
3411
|
+
const pairs = [];
|
|
3412
|
+
for (const part of splitCommas(v)) {
|
|
3413
|
+
const m = /^(["'])([\x20-\x7e]{4})\1(?:\s+(on|off|\d+))?$/i.exec(part.trim());
|
|
3414
|
+
if (!m)
|
|
3415
|
+
return null;
|
|
3416
|
+
const setting = (m[3] ?? 'on').toLowerCase();
|
|
3417
|
+
const n = setting === 'on' ? 1 : setting === 'off' ? 0 : Number(setting);
|
|
3418
|
+
pairs.push(`${m[2]}=${n}`);
|
|
3419
|
+
}
|
|
3420
|
+
return pairs.join(',');
|
|
972
3421
|
}
|
|
973
3422
|
function applyFontShorthand(style, parent, value, ctx) {
|
|
974
3423
|
// The `font: caption | menu | …` system forms name a font this renderer
|
|
@@ -976,26 +3425,58 @@ function applyFontShorthand(style, parent, value, ctx) {
|
|
|
976
3425
|
const parts = splitValue(value);
|
|
977
3426
|
if (parts.length < 2)
|
|
978
3427
|
return;
|
|
3428
|
+
let fontStyle = 'normal';
|
|
3429
|
+
let weight = null;
|
|
3430
|
+
let smallCaps = false;
|
|
979
3431
|
let i = 0;
|
|
980
3432
|
for (; i < parts.length; i += 1) {
|
|
981
3433
|
const v = parts[i].toLowerCase();
|
|
982
3434
|
if (v === 'italic' || v === 'oblique')
|
|
983
|
-
|
|
3435
|
+
fontStyle = v;
|
|
984
3436
|
else if (v === 'bold' ||
|
|
985
3437
|
v === 'bolder' ||
|
|
986
3438
|
v === 'lighter' ||
|
|
987
3439
|
/^\d{3}$/.test(v)) {
|
|
988
|
-
|
|
3440
|
+
weight = v;
|
|
989
3441
|
}
|
|
990
|
-
else if (v === '
|
|
3442
|
+
else if (v === 'small-caps')
|
|
3443
|
+
smallCaps = true;
|
|
3444
|
+
else if (v === 'normal')
|
|
991
3445
|
continue;
|
|
992
3446
|
else
|
|
993
3447
|
break;
|
|
994
3448
|
}
|
|
995
|
-
|
|
996
|
-
|
|
3449
|
+
// `12px/1.5`, or the same with space round the slash
|
|
3450
|
+
let [sizeText, lineText] = (parts[i] ?? '').split('/');
|
|
3451
|
+
let next = i + 1;
|
|
3452
|
+
if (lineText === '' || (lineText === undefined && parts[next]?.[0] === '/')) {
|
|
3453
|
+
const slash = lineText === undefined ? parts[next++].slice(1) : '';
|
|
3454
|
+
lineText = slash || parts[next++];
|
|
3455
|
+
}
|
|
3456
|
+
const family = parts.slice(next).join(' ');
|
|
3457
|
+
// A size and a family, or the value is not a font and the declaration is
|
|
3458
|
+
// dropped whole, as CSS drops any value it cannot read — a line height
|
|
3459
|
+
// below nought included: `font: 4em/-2em serif` set the text at 4em.
|
|
3460
|
+
const size = keywordFontSize(sizeText ?? '', parent.fontSize, ctx.rem) ??
|
|
3461
|
+
parseLength(sizeText ?? '', { ...ctx, em: parent.fontSize });
|
|
3462
|
+
if (!family || size === null || size === AUTO)
|
|
997
3463
|
return;
|
|
998
|
-
|
|
3464
|
+
if (lineText && negativeLength(lineText, ctx))
|
|
3465
|
+
return;
|
|
3466
|
+
// What the shorthand does not name goes back to its initial value rather
|
|
3467
|
+
// than keeping the parent's (CSS 2.1 15.8): `p { font: 12pt serif }`
|
|
3468
|
+
// inside a document set at `20px/1em` has lines of normal height, not 20px.
|
|
3469
|
+
style.fontStyle = fontStyle;
|
|
3470
|
+
style.fontWeight = weight ? parseWeight(weight, parent.fontWeight) : 400;
|
|
3471
|
+
// the variants too, save the small capitals it may name; not the
|
|
3472
|
+
// features `font-feature-settings` sets, which the shorthand leaves
|
|
3473
|
+
style.fontVariantNumeric = '';
|
|
3474
|
+
style.fontVariantCaps = smallCaps ? 'smcp=1' : '';
|
|
3475
|
+
style.fontVariantLigatures = '';
|
|
3476
|
+
style.fontVariantPosition = '';
|
|
3477
|
+
style.fontKerning = '';
|
|
3478
|
+
style.lineHeight = 'normal';
|
|
3479
|
+
style.lineHeightIsLength = false;
|
|
999
3480
|
applyDeclaration(style, parent, 'font-size', sizeText, ctx);
|
|
1000
3481
|
if (lineText) {
|
|
1001
3482
|
applyDeclaration(style, parent, 'line-height', lineText, {
|
|
@@ -1003,9 +3484,17 @@ function applyFontShorthand(style, parent, value, ctx) {
|
|
|
1003
3484
|
em: style.fontSize,
|
|
1004
3485
|
});
|
|
1005
3486
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
3487
|
+
applyDeclaration(style, parent, 'font-family', family, ctx);
|
|
3488
|
+
}
|
|
3489
|
+
/** Whether a line height is a number or a length below nought. */
|
|
3490
|
+
function negativeLength(value, ctx) {
|
|
3491
|
+
const n = parseNumber(value);
|
|
3492
|
+
if (n !== null)
|
|
3493
|
+
return n < 0;
|
|
3494
|
+
const len = parseLength(value, ctx);
|
|
3495
|
+
if (typeof len === 'number')
|
|
3496
|
+
return len < 0;
|
|
3497
|
+
return !!len && typeof len === 'object' && resolve(len, 100) < 0;
|
|
1009
3498
|
}
|
|
1010
3499
|
function applyFlexShorthand(style, value, ctx) {
|
|
1011
3500
|
const v = value.trim().toLowerCase();
|
|
@@ -1025,6 +3514,10 @@ function applyFlexShorthand(style, value, ctx) {
|
|
|
1025
3514
|
const numbers = [];
|
|
1026
3515
|
let basis = null;
|
|
1027
3516
|
for (const part of parts) {
|
|
3517
|
+
if (part.toLowerCase() === 'content') {
|
|
3518
|
+
basis = 'content';
|
|
3519
|
+
continue;
|
|
3520
|
+
}
|
|
1028
3521
|
const n = parseNumber(part);
|
|
1029
3522
|
if (n !== null && numbers.length < 2 && !part.includes('%')) {
|
|
1030
3523
|
numbers.push(n);
|
|
@@ -1042,11 +3535,644 @@ function applyFlexShorthand(style, value, ctx) {
|
|
|
1042
3535
|
style.flexShrink = numbers.length > 1 ? numbers[1] : 1;
|
|
1043
3536
|
style.flexBasis = basis ?? (numbers.length ? 0 : AUTO);
|
|
1044
3537
|
}
|
|
3538
|
+
// --- grid ------------------------------------------------------------------
|
|
3539
|
+
const AUTO_TRACK = { min: 'auto', max: 'auto' };
|
|
3540
|
+
/** One track's sizing, or null for what is not one. */
|
|
3541
|
+
function parseGridTrack(token, ctx) {
|
|
3542
|
+
const t = token.trim().toLowerCase();
|
|
3543
|
+
if (!t)
|
|
3544
|
+
return null;
|
|
3545
|
+
if (t === 'auto')
|
|
3546
|
+
return AUTO_TRACK;
|
|
3547
|
+
if (t === 'min-content' || t === 'max-content')
|
|
3548
|
+
return { min: t, max: t };
|
|
3549
|
+
const fr = /^(\d*\.?\d+)fr$/.exec(t);
|
|
3550
|
+
if (fr)
|
|
3551
|
+
return { min: 'auto', max: { fr: Number(fr[1]) } };
|
|
3552
|
+
const fn = /^(minmax|fit-content)\((.*)\)$/s.exec(t);
|
|
3553
|
+
if (fn) {
|
|
3554
|
+
if (fn[1] === 'fit-content') {
|
|
3555
|
+
const fit = parseLength(fn[2].trim(), ctx);
|
|
3556
|
+
if (fit === null || fit === AUTO)
|
|
3557
|
+
return null;
|
|
3558
|
+
if (typeof fit === 'number' && fit < 0)
|
|
3559
|
+
return null;
|
|
3560
|
+
return { min: AUTO, max: { fit } };
|
|
3561
|
+
}
|
|
3562
|
+
const [a, b] = splitCommas(fn[2]).map((p) => p.trim());
|
|
3563
|
+
if (a === undefined || b === undefined)
|
|
3564
|
+
return null;
|
|
3565
|
+
const min = parseGridTrack(a, ctx);
|
|
3566
|
+
const max = parseGridTrack(b, ctx);
|
|
3567
|
+
// an `fr` is no minimum
|
|
3568
|
+
if (!min ||
|
|
3569
|
+
!max ||
|
|
3570
|
+
(typeof min.max === 'object' && min.max !== null && 'fr' in min.max))
|
|
3571
|
+
return null;
|
|
3572
|
+
return { min: min.min, max: max.max };
|
|
3573
|
+
}
|
|
3574
|
+
const len = parseLength(token.trim(), ctx);
|
|
3575
|
+
if (len === null || len === AUTO)
|
|
3576
|
+
return null;
|
|
3577
|
+
return { min: len, max: len };
|
|
3578
|
+
}
|
|
3579
|
+
/**
|
|
3580
|
+
* A track list's parts: a bracketed group of line names as one part, a
|
|
3581
|
+
* quoted string as one, and a size or a function as one — a group of two
|
|
3582
|
+
* names was two parts, and the second was no size. Undefined where a
|
|
3583
|
+
* bracket or a quote is not closed.
|
|
3584
|
+
*/
|
|
3585
|
+
function trackParts(value) {
|
|
3586
|
+
const out = [];
|
|
3587
|
+
const text = value.trim();
|
|
3588
|
+
let i = 0;
|
|
3589
|
+
while (i < text.length) {
|
|
3590
|
+
const ch = text[i];
|
|
3591
|
+
if (/\s/.test(ch)) {
|
|
3592
|
+
i += 1;
|
|
3593
|
+
continue;
|
|
3594
|
+
}
|
|
3595
|
+
const close = ch === '[' ? ']' : ch === '"' || ch === "'" ? ch : null;
|
|
3596
|
+
if (close) {
|
|
3597
|
+
const end = text.indexOf(close, i + 1);
|
|
3598
|
+
if (end < 0)
|
|
3599
|
+
return undefined;
|
|
3600
|
+
out.push(text.slice(i, end + 1));
|
|
3601
|
+
i = end + 1;
|
|
3602
|
+
continue;
|
|
3603
|
+
}
|
|
3604
|
+
let depth = 0;
|
|
3605
|
+
let j = i;
|
|
3606
|
+
for (; j < text.length; j += 1) {
|
|
3607
|
+
const c = text[j];
|
|
3608
|
+
if (c === '(')
|
|
3609
|
+
depth += 1;
|
|
3610
|
+
else if (c === ')')
|
|
3611
|
+
depth -= 1;
|
|
3612
|
+
else if (depth === 0 && /[\s["']/.test(c))
|
|
3613
|
+
break;
|
|
3614
|
+
}
|
|
3615
|
+
out.push(text.slice(i, j));
|
|
3616
|
+
i = j;
|
|
3617
|
+
}
|
|
3618
|
+
return out;
|
|
3619
|
+
}
|
|
3620
|
+
/** The names in a bracketed group of line names, or undefined for a part
|
|
3621
|
+
* that is not one or names what no line may be called. */
|
|
3622
|
+
function lineNames(part) {
|
|
3623
|
+
if (!part.startsWith('[') || !part.endsWith(']'))
|
|
3624
|
+
return undefined;
|
|
3625
|
+
const names = part.slice(1, -1).trim().split(/\s+/).filter(Boolean);
|
|
3626
|
+
return names.every(isLineName) ? names : undefined;
|
|
3627
|
+
}
|
|
3628
|
+
/** A `<custom-ident>` a grid line may take: not `span` nor `auto`. */
|
|
3629
|
+
function isLineName(word) {
|
|
3630
|
+
return (/^-?[a-zA-Z_\u0080-\uffff][\w\u0080-\uffff-]*$/.test(word) &&
|
|
3631
|
+
!/^(span|auto|inherit|initial|unset|default)$/i.test(word));
|
|
3632
|
+
}
|
|
3633
|
+
/**
|
|
3634
|
+
* A track list (CSS Grid 1, 7.2): lengths, `fr`s, `auto`, `minmax()`,
|
|
3635
|
+
* `fit-content()`, `repeat()` with a count or with `auto-fill`/`auto-fit`,
|
|
3636
|
+
* whose count the width decides, and the names of the lines between them.
|
|
3637
|
+
* Undefined for a list that is none.
|
|
3638
|
+
*/
|
|
3639
|
+
function parseGridTemplate(value, ctx) {
|
|
3640
|
+
const out = { tracks: [], names: [[]], repeat: null };
|
|
3641
|
+
// where a group of names goes: the line after the last track, or the
|
|
3642
|
+
// line after an auto repeat until a track follows it
|
|
3643
|
+
let pending = null;
|
|
3644
|
+
const read = (parts, into, names, top) => {
|
|
3645
|
+
for (const part of parts) {
|
|
3646
|
+
if (part.startsWith('[')) {
|
|
3647
|
+
const group = lineNames(part);
|
|
3648
|
+
if (!group)
|
|
3649
|
+
return false;
|
|
3650
|
+
(top && pending ? pending : names[names.length - 1]).push(...group);
|
|
3651
|
+
continue;
|
|
3652
|
+
}
|
|
3653
|
+
const repeat = /^repeat\((.*)\)$/is.exec(part);
|
|
3654
|
+
if (repeat) {
|
|
3655
|
+
const [count, ...rest] = splitCommas(repeat[1]);
|
|
3656
|
+
const inner = [];
|
|
3657
|
+
const innerNames = [[]];
|
|
3658
|
+
const innerParts = trackParts(rest.join(','));
|
|
3659
|
+
if (!innerParts ||
|
|
3660
|
+
!read(innerParts, inner, innerNames, false) ||
|
|
3661
|
+
!inner.length) {
|
|
3662
|
+
return false;
|
|
3663
|
+
}
|
|
3664
|
+
const n = count.trim().toLowerCase();
|
|
3665
|
+
if (n === 'auto-fill' || n === 'auto-fit') {
|
|
3666
|
+
if (out.repeat || !top)
|
|
3667
|
+
return false;
|
|
3668
|
+
out.repeat = {
|
|
3669
|
+
at: into.length,
|
|
3670
|
+
tracks: inner,
|
|
3671
|
+
names: innerNames,
|
|
3672
|
+
after: [],
|
|
3673
|
+
fit: n === 'auto-fit',
|
|
3674
|
+
};
|
|
3675
|
+
pending = out.repeat.after;
|
|
3676
|
+
continue;
|
|
3677
|
+
}
|
|
3678
|
+
const times = Number(n);
|
|
3679
|
+
if (!Number.isInteger(times) || times < 1 || times > 1000)
|
|
3680
|
+
return false;
|
|
3681
|
+
for (let i = 0; i < times; i += 1) {
|
|
3682
|
+
names[names.length - 1].push(...innerNames[0]);
|
|
3683
|
+
for (let t = 0; t < inner.length; t += 1) {
|
|
3684
|
+
into.push(inner[t]);
|
|
3685
|
+
names.push([...innerNames[t + 1]]);
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
if (top)
|
|
3689
|
+
pending = null;
|
|
3690
|
+
continue;
|
|
3691
|
+
}
|
|
3692
|
+
const track = parseGridTrack(part, ctx);
|
|
3693
|
+
if (!track)
|
|
3694
|
+
return false;
|
|
3695
|
+
into.push(track);
|
|
3696
|
+
names.push([]);
|
|
3697
|
+
if (top)
|
|
3698
|
+
pending = null;
|
|
3699
|
+
}
|
|
3700
|
+
return true;
|
|
3701
|
+
};
|
|
3702
|
+
const parts = trackParts(value);
|
|
3703
|
+
if (!parts || !read(parts, out.tracks, out.names, true))
|
|
3704
|
+
return undefined;
|
|
3705
|
+
if (!out.tracks.length && !out.repeat)
|
|
3706
|
+
return undefined;
|
|
3707
|
+
return out;
|
|
3708
|
+
}
|
|
3709
|
+
/**
|
|
3710
|
+
* `grid-template-areas`' strings (CSS Grid 1, 7.3): a row each, a cell to a
|
|
3711
|
+
* word or a run of dots, every row as wide as the first and every name a
|
|
3712
|
+
* rectangle. Undefined where they are not.
|
|
3713
|
+
*/
|
|
3714
|
+
function parseAreas(strings) {
|
|
3715
|
+
const grid = [];
|
|
3716
|
+
for (const quoted of strings) {
|
|
3717
|
+
const cells = [];
|
|
3718
|
+
const text = quoted.slice(1, -1);
|
|
3719
|
+
const re = /\s*(?:(\.+)|([\w\u0080-\uffff-]+)|(\S))/g;
|
|
3720
|
+
let m;
|
|
3721
|
+
while ((m = re.exec(text)) && m[0].trim()) {
|
|
3722
|
+
if (m[3])
|
|
3723
|
+
return undefined;
|
|
3724
|
+
cells.push(m[1] ? null : m[2]);
|
|
3725
|
+
}
|
|
3726
|
+
if (!cells.length)
|
|
3727
|
+
return undefined;
|
|
3728
|
+
if (grid.length && cells.length !== grid[0].length)
|
|
3729
|
+
return undefined;
|
|
3730
|
+
grid.push(cells);
|
|
3731
|
+
}
|
|
3732
|
+
const areas = new Map();
|
|
3733
|
+
grid.forEach((cells, r) => cells.forEach((name, c) => {
|
|
3734
|
+
if (name === null)
|
|
3735
|
+
return;
|
|
3736
|
+
const area = areas.get(name);
|
|
3737
|
+
if (!area)
|
|
3738
|
+
areas.set(name, { rows: [r, r + 1], columns: [c, c + 1] });
|
|
3739
|
+
else {
|
|
3740
|
+
area.rows[1] = Math.max(area.rows[1], r + 1);
|
|
3741
|
+
area.columns[1] = Math.max(area.columns[1], c + 1);
|
|
3742
|
+
}
|
|
3743
|
+
}));
|
|
3744
|
+
// each name fills the rectangle its first and last cells make, and
|
|
3745
|
+
// nothing else
|
|
3746
|
+
let cellsNamed = 0;
|
|
3747
|
+
for (const cells of grid)
|
|
3748
|
+
for (const name of cells)
|
|
3749
|
+
if (name)
|
|
3750
|
+
cellsNamed += 1;
|
|
3751
|
+
let rectangles = 0;
|
|
3752
|
+
for (const [name, { rows, columns }] of areas) {
|
|
3753
|
+
for (let r = rows[0]; r < rows[1]; r += 1) {
|
|
3754
|
+
for (let c = columns[0]; c < columns[1]; c += 1) {
|
|
3755
|
+
if (grid[r][c] !== name)
|
|
3756
|
+
return undefined;
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
rectangles += (rows[1] - rows[0]) * (columns[1] - columns[0]);
|
|
3760
|
+
}
|
|
3761
|
+
if (rectangles !== cellsNamed)
|
|
3762
|
+
return undefined;
|
|
3763
|
+
return { rows: grid.length, columns: grid[0].length, areas };
|
|
3764
|
+
}
|
|
3765
|
+
/**
|
|
3766
|
+
* `grid-template`'s rows and columns either side of a slash (7.4): the
|
|
3767
|
+
* rows as track list, or as the strings of `grid-template-areas`, each
|
|
3768
|
+
* string's row the size after it or `auto`, with the names of the lines
|
|
3769
|
+
* before and after it. Undefined for no value.
|
|
3770
|
+
*/
|
|
3771
|
+
function parseTemplateShorthand(value, ctx) {
|
|
3772
|
+
const parts = splitTopLevelSlash(value);
|
|
3773
|
+
if (parts.length !== 2)
|
|
3774
|
+
return undefined;
|
|
3775
|
+
const columnsNone = parts[1].trim().toLowerCase() === 'none';
|
|
3776
|
+
const columns = columnsNone ? null : parseGridTemplate(parts[1], ctx);
|
|
3777
|
+
if (columns === undefined)
|
|
3778
|
+
return undefined;
|
|
3779
|
+
if (!/["']/.test(parts[0])) {
|
|
3780
|
+
const rows = parts[0].trim().toLowerCase() === 'none'
|
|
3781
|
+
? null
|
|
3782
|
+
: parseGridTemplate(parts[0], ctx);
|
|
3783
|
+
return rows === undefined ? undefined : { rows, columns, areas: null };
|
|
3784
|
+
}
|
|
3785
|
+
// the strings' form takes no `repeat()` for its columns
|
|
3786
|
+
if (columns?.repeat)
|
|
3787
|
+
return undefined;
|
|
3788
|
+
const pieces = trackParts(parts[0]);
|
|
3789
|
+
if (!pieces)
|
|
3790
|
+
return undefined;
|
|
3791
|
+
const tracks = [];
|
|
3792
|
+
const names = [[]];
|
|
3793
|
+
const strings = [];
|
|
3794
|
+
// after a string, and before its size, if it has one
|
|
3795
|
+
let open = false;
|
|
3796
|
+
for (const piece of pieces) {
|
|
3797
|
+
if (piece.startsWith('[')) {
|
|
3798
|
+
const group = lineNames(piece);
|
|
3799
|
+
if (!group)
|
|
3800
|
+
return undefined;
|
|
3801
|
+
if (open) {
|
|
3802
|
+
tracks.push(AUTO_TRACK);
|
|
3803
|
+
names.push([]);
|
|
3804
|
+
open = false;
|
|
3805
|
+
}
|
|
3806
|
+
names[names.length - 1].push(...group);
|
|
3807
|
+
continue;
|
|
3808
|
+
}
|
|
3809
|
+
if (/^["']/.test(piece)) {
|
|
3810
|
+
if (open) {
|
|
3811
|
+
tracks.push(AUTO_TRACK);
|
|
3812
|
+
names.push([]);
|
|
3813
|
+
}
|
|
3814
|
+
strings.push(piece);
|
|
3815
|
+
open = true;
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
if (!open)
|
|
3819
|
+
return undefined;
|
|
3820
|
+
const track = parseGridTrack(piece, ctx);
|
|
3821
|
+
if (!track)
|
|
3822
|
+
return undefined;
|
|
3823
|
+
tracks.push(track);
|
|
3824
|
+
names.push([]);
|
|
3825
|
+
open = false;
|
|
3826
|
+
}
|
|
3827
|
+
if (open) {
|
|
3828
|
+
tracks.push(AUTO_TRACK);
|
|
3829
|
+
names.push([]);
|
|
3830
|
+
}
|
|
3831
|
+
const areas = parseAreas(strings);
|
|
3832
|
+
if (!areas)
|
|
3833
|
+
return undefined;
|
|
3834
|
+
if (columns && columns.tracks.length !== areas.columns)
|
|
3835
|
+
return undefined;
|
|
3836
|
+
return { rows: { tracks, names, repeat: null }, columns, areas };
|
|
3837
|
+
}
|
|
3838
|
+
/**
|
|
3839
|
+
* A grid line (CSS Grid 1, 8.3): `auto`; a number; a name; a number and a
|
|
3840
|
+
* name, in either order; or `span` with a number, a name or both.
|
|
3841
|
+
* Undefined for a value that is none of them.
|
|
3842
|
+
*/
|
|
3843
|
+
function parseGridLine(value) {
|
|
3844
|
+
if (value === undefined)
|
|
3845
|
+
return null;
|
|
3846
|
+
const words = value.trim().split(/\s+/).filter(Boolean);
|
|
3847
|
+
if (words.length === 1 && words[0].toLowerCase() === 'auto')
|
|
3848
|
+
return null;
|
|
3849
|
+
let span = false;
|
|
3850
|
+
let count = null;
|
|
3851
|
+
let name = null;
|
|
3852
|
+
for (const word of words) {
|
|
3853
|
+
if (word.toLowerCase() === 'span') {
|
|
3854
|
+
if (span)
|
|
3855
|
+
return undefined;
|
|
3856
|
+
span = true;
|
|
3857
|
+
}
|
|
3858
|
+
else if (/^[+-]?\d+$/.test(word)) {
|
|
3859
|
+
if (count !== null)
|
|
3860
|
+
return undefined;
|
|
3861
|
+
count = Number(word);
|
|
3862
|
+
}
|
|
3863
|
+
else if (isLineName(word)) {
|
|
3864
|
+
if (name !== null)
|
|
3865
|
+
return undefined;
|
|
3866
|
+
name = word;
|
|
3867
|
+
}
|
|
3868
|
+
else
|
|
3869
|
+
return undefined;
|
|
3870
|
+
}
|
|
3871
|
+
// `span` goes first or last, never between a number and a name
|
|
3872
|
+
if (span && words.length === 3 && words[1].toLowerCase() === 'span') {
|
|
3873
|
+
return undefined;
|
|
3874
|
+
}
|
|
3875
|
+
if (span) {
|
|
3876
|
+
if (count !== null && count < 1)
|
|
3877
|
+
return undefined;
|
|
3878
|
+
const out = { span: count ?? 1 };
|
|
3879
|
+
if (name !== null)
|
|
3880
|
+
out.name = name;
|
|
3881
|
+
return out;
|
|
3882
|
+
}
|
|
3883
|
+
if (count !== null) {
|
|
3884
|
+
if (count === 0)
|
|
3885
|
+
return undefined;
|
|
3886
|
+
return name === null ? { line: count } : { line: count, name };
|
|
3887
|
+
}
|
|
3888
|
+
return name === null ? undefined : { name };
|
|
3889
|
+
}
|
|
3890
|
+
/** A grid line that is a name alone, which a shorthand copies to the
|
|
3891
|
+
* line it leaves out; auto for any other. */
|
|
3892
|
+
function named(line) {
|
|
3893
|
+
return line && 'name' in line && !('line' in line) && !('span' in line)
|
|
3894
|
+
? line
|
|
3895
|
+
: null;
|
|
3896
|
+
}
|
|
3897
|
+
/** A value's parts either side of a top-level `/`. */
|
|
3898
|
+
function splitTopLevelSlash(value) {
|
|
3899
|
+
const out = [];
|
|
3900
|
+
let depth = 0;
|
|
3901
|
+
let start = 0;
|
|
3902
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
3903
|
+
const c = value[i];
|
|
3904
|
+
if (c === '(')
|
|
3905
|
+
depth += 1;
|
|
3906
|
+
else if (c === ')')
|
|
3907
|
+
depth -= 1;
|
|
3908
|
+
else if (c === '/' && depth === 0) {
|
|
3909
|
+
out.push(value.slice(start, i).trim());
|
|
3910
|
+
start = i + 1;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
out.push(value.slice(start).trim());
|
|
3914
|
+
return out;
|
|
3915
|
+
}
|
|
3916
|
+
// --- logical properties ----------------------------------------------------
|
|
3917
|
+
const OBJECT_FITS = new Set(['fill', 'contain', 'cover', 'none', 'scale-down']);
|
|
3918
|
+
/**
|
|
3919
|
+
* `aspect-ratio` (CSS Sizing 4, 5.1): `auto`, a ratio — one number, or
|
|
3920
|
+
* two with a `/` between — or both, in either order. Undefined where it is
|
|
3921
|
+
* none of that, and the declaration is dropped; a ratio with a nought in it
|
|
3922
|
+
* is none, and `auto` is what is left.
|
|
3923
|
+
*/
|
|
3924
|
+
function parseAspectRatio(value) {
|
|
3925
|
+
let auto = false;
|
|
3926
|
+
let rest = value.trim().toLowerCase();
|
|
3927
|
+
const word = /(?:^auto\s+|\s+auto$|^auto$)/.exec(rest);
|
|
3928
|
+
if (word) {
|
|
3929
|
+
auto = true;
|
|
3930
|
+
rest = rest.replace(word[0], '').trim();
|
|
3931
|
+
if (!rest)
|
|
3932
|
+
return null;
|
|
3933
|
+
}
|
|
3934
|
+
const m = /^(\d*\.?\d+(?:e[+-]?\d+)?)(?:\s*\/\s*(\d*\.?\d+(?:e[+-]?\d+)?))?$/.exec(rest);
|
|
3935
|
+
if (!m)
|
|
3936
|
+
return undefined;
|
|
3937
|
+
const w = Number(m[1]);
|
|
3938
|
+
const h = m[2] === undefined ? 1 : Number(m[2]);
|
|
3939
|
+
if (!(w > 0 && h > 0))
|
|
3940
|
+
return null;
|
|
3941
|
+
return { ratio: w / h, auto };
|
|
3942
|
+
}
|
|
3943
|
+
/**
|
|
3944
|
+
* A `box-shadow` (CSS Backgrounds 3, 7.1): `none`, or shadows front to
|
|
3945
|
+
* back, each two offsets and then a blur and a spread, with a colour and
|
|
3946
|
+
* `inset` on either side of the lengths. Undefined where the value is none
|
|
3947
|
+
* of that, and the declaration is dropped. A shadow no colour can be seen
|
|
3948
|
+
* in is left out — Tailwind writes four of them, `0 0 #0000`, under every
|
|
3949
|
+
* one it means.
|
|
3950
|
+
*/
|
|
3951
|
+
/** `box-shadow`'s shadows, or with `text` `text-shadow`'s, which have no
|
|
3952
|
+
* spread and are never inset (CSS Text Decoration 3, 4): null for none,
|
|
3953
|
+
* and undefined for a value that is not one. */
|
|
3954
|
+
function parseBoxShadow(value, ctx, text = false) {
|
|
3955
|
+
if (value.trim().toLowerCase() === 'none')
|
|
3956
|
+
return null;
|
|
3957
|
+
const out = [];
|
|
3958
|
+
for (const part of splitCommas(value)) {
|
|
3959
|
+
let inset = false;
|
|
3960
|
+
let color = null;
|
|
3961
|
+
const lengths = [];
|
|
3962
|
+
// the lengths are written together, with nothing between them
|
|
3963
|
+
let closed = false;
|
|
3964
|
+
for (const token of splitValue(part.trim())) {
|
|
3965
|
+
if (token.toLowerCase() === 'inset') {
|
|
3966
|
+
if (inset || text)
|
|
3967
|
+
return undefined;
|
|
3968
|
+
inset = true;
|
|
3969
|
+
closed = lengths.length > 0;
|
|
3970
|
+
continue;
|
|
3971
|
+
}
|
|
3972
|
+
const len = parseLength(token, ctx);
|
|
3973
|
+
if (typeof len === 'number') {
|
|
3974
|
+
if (closed || lengths.length === (text ? 3 : 4))
|
|
3975
|
+
return undefined;
|
|
3976
|
+
lengths.push(len);
|
|
3977
|
+
continue;
|
|
3978
|
+
}
|
|
3979
|
+
const c = parseColor(token);
|
|
3980
|
+
if (c === null || color !== null)
|
|
3981
|
+
return undefined;
|
|
3982
|
+
color = c;
|
|
3983
|
+
closed = lengths.length > 0;
|
|
3984
|
+
}
|
|
3985
|
+
if (lengths.length < 2)
|
|
3986
|
+
return undefined;
|
|
3987
|
+
const [x, y, blur = 0, spread = 0] = lengths;
|
|
3988
|
+
if (blur < 0)
|
|
3989
|
+
return undefined;
|
|
3990
|
+
const ink = color ?? 'currentColor';
|
|
3991
|
+
if (alphaOf(ink) === 0 || isTransparent(ink))
|
|
3992
|
+
continue;
|
|
3993
|
+
out.push({ x, y, blur, spread, color: ink, inset });
|
|
3994
|
+
}
|
|
3995
|
+
return out.length ? out : null;
|
|
3996
|
+
}
|
|
3997
|
+
/** A corner's radius: a length or a percentage, and neither negative nor
|
|
3998
|
+
* `auto`; null where it is none. */
|
|
3999
|
+
function radiusOf(text, ctx) {
|
|
4000
|
+
const len = parseLength(text, ctx);
|
|
4001
|
+
if (len === null || len === AUTO)
|
|
4002
|
+
return null;
|
|
4003
|
+
if (typeof len === 'number')
|
|
4004
|
+
return len < 0 ? null : len;
|
|
4005
|
+
return len.pct < 0 && !len.px && !len.of ? null : len;
|
|
4006
|
+
}
|
|
4007
|
+
/** Whether two corners' radii are the same values. */
|
|
4008
|
+
function sameRadii(a, b) {
|
|
4009
|
+
for (let i = 0; i < 4; i += 1) {
|
|
4010
|
+
const x = a[i];
|
|
4011
|
+
const y = b[i];
|
|
4012
|
+
if (x === y)
|
|
4013
|
+
continue;
|
|
4014
|
+
if (typeof x !== 'object' || typeof y !== 'object')
|
|
4015
|
+
return false;
|
|
4016
|
+
if (x.pct !== y.pct || x.px !== y.px || x.of || y.of)
|
|
4017
|
+
return false;
|
|
4018
|
+
}
|
|
4019
|
+
return true;
|
|
4020
|
+
}
|
|
4021
|
+
/** The four corners, in `border-radius`'s order. */
|
|
4022
|
+
const CORNERS = [
|
|
4023
|
+
'border-top-left-radius',
|
|
4024
|
+
'border-top-right-radius',
|
|
4025
|
+
'border-bottom-right-radius',
|
|
4026
|
+
'border-bottom-left-radius',
|
|
4027
|
+
];
|
|
4028
|
+
/**
|
|
4029
|
+
* CSS Logical Properties 1, in the horizontal writing mode `<Html>` lays
|
|
4030
|
+
* out: the inline axis is the line's, its start the left of a
|
|
4031
|
+
* left-to-right element and the right of a right-to-left one, and the block
|
|
4032
|
+
* axis runs down. Tailwind 4 writes its spacing in them — `px-4` is
|
|
4033
|
+
* `padding-inline`, `mx-auto` is `margin-inline: auto` — and every modern
|
|
4034
|
+
* reset writes some.
|
|
4035
|
+
*/
|
|
4036
|
+
const LOGICAL = (() => {
|
|
4037
|
+
const out = {};
|
|
4038
|
+
const length = (part, ctx) => parseLength(part, ctx) !== null;
|
|
4039
|
+
const groups = [
|
|
4040
|
+
['margin', (side) => `margin-${side}`, length],
|
|
4041
|
+
[
|
|
4042
|
+
'padding',
|
|
4043
|
+
(side) => `padding-${side}`,
|
|
4044
|
+
(part, ctx) => validPadding(parseLength(part, ctx)),
|
|
4045
|
+
],
|
|
4046
|
+
['inset', (side) => side, length],
|
|
4047
|
+
[
|
|
4048
|
+
'border-*-width',
|
|
4049
|
+
(side) => `border-${side}-width`,
|
|
4050
|
+
(part, ctx) => borderWidth(part, ctx) !== null,
|
|
4051
|
+
],
|
|
4052
|
+
[
|
|
4053
|
+
'border-*-style',
|
|
4054
|
+
(side) => `border-${side}-style`,
|
|
4055
|
+
(part) => BORDER_STYLES.has(part.toLowerCase()),
|
|
4056
|
+
],
|
|
4057
|
+
[
|
|
4058
|
+
'border-*-color',
|
|
4059
|
+
(side) => `border-${side}-color`,
|
|
4060
|
+
(part) => parseColor(part) !== null,
|
|
4061
|
+
],
|
|
4062
|
+
];
|
|
4063
|
+
for (const [group, physical, valid] of groups) {
|
|
4064
|
+
for (const axis of ['inline', 'block']) {
|
|
4065
|
+
const name = group.includes('*')
|
|
4066
|
+
? group.replace('*', axis)
|
|
4067
|
+
: `${group}-${axis}`;
|
|
4068
|
+
out[name] = { kind: 'axis', axis, same: false, physical, valid };
|
|
4069
|
+
for (const end of ['start', 'end']) {
|
|
4070
|
+
const side = `${axis}-${end}`;
|
|
4071
|
+
const longhand = group.includes('*')
|
|
4072
|
+
? group.replace('*', side)
|
|
4073
|
+
: `${group}-${side}`;
|
|
4074
|
+
out[longhand] = { kind: 'side', side, physical };
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4078
|
+
for (const axis of ['inline', 'block']) {
|
|
4079
|
+
out[`border-${axis}`] = {
|
|
4080
|
+
kind: 'axis',
|
|
4081
|
+
axis,
|
|
4082
|
+
same: true,
|
|
4083
|
+
physical: (side) => `border-${side}`,
|
|
4084
|
+
valid: () => true,
|
|
4085
|
+
};
|
|
4086
|
+
for (const end of ['start', 'end']) {
|
|
4087
|
+
out[`border-${axis}-${end}`] = {
|
|
4088
|
+
kind: 'side',
|
|
4089
|
+
side: `${axis}-${end}`,
|
|
4090
|
+
physical: (side) => `border-${side}`,
|
|
4091
|
+
};
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
const sizes = [
|
|
4095
|
+
['inline-size', 'width'],
|
|
4096
|
+
['block-size', 'height'],
|
|
4097
|
+
['min-inline-size', 'min-width'],
|
|
4098
|
+
['min-block-size', 'min-height'],
|
|
4099
|
+
['max-inline-size', 'max-width'],
|
|
4100
|
+
['max-block-size', 'max-height'],
|
|
4101
|
+
];
|
|
4102
|
+
for (const [name, physical] of sizes)
|
|
4103
|
+
out[name] = { kind: 'size', physical };
|
|
4104
|
+
for (const block of ['start', 'end']) {
|
|
4105
|
+
for (const inline of ['start', 'end']) {
|
|
4106
|
+
out[`border-${block}-${inline}-radius`] = {
|
|
4107
|
+
kind: 'corner',
|
|
4108
|
+
block,
|
|
4109
|
+
inline,
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
return out;
|
|
4114
|
+
})();
|
|
4115
|
+
/** The physical side a logical one is, by the element's direction. */
|
|
4116
|
+
function physicalSide(side, rtl) {
|
|
4117
|
+
switch (side) {
|
|
4118
|
+
case 'inline-start':
|
|
4119
|
+
return rtl ? 'right' : 'left';
|
|
4120
|
+
case 'inline-end':
|
|
4121
|
+
return rtl ? 'left' : 'right';
|
|
4122
|
+
case 'block-start':
|
|
4123
|
+
return 'top';
|
|
4124
|
+
default:
|
|
4125
|
+
return 'bottom';
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
/**
|
|
4129
|
+
* A logical declaration, as the physical ones it stands for. The direction
|
|
4130
|
+
* is the element's as far as the cascade has got: its parent's, or one it
|
|
4131
|
+
* set itself earlier in cascade order, which is where HTML's `dir` puts it.
|
|
4132
|
+
* A two-value shorthand with a part that is no value for its property is
|
|
4133
|
+
* none, and the declaration goes, as a physical shorthand's does.
|
|
4134
|
+
*/
|
|
4135
|
+
function applyLogical(style, parent, logical, value, ctx) {
|
|
4136
|
+
const rtl = style.direction === 'rtl';
|
|
4137
|
+
switch (logical.kind) {
|
|
4138
|
+
case 'side':
|
|
4139
|
+
applyDeclaration(style, parent, logical.physical(physicalSide(logical.side, rtl)), value, ctx);
|
|
4140
|
+
return;
|
|
4141
|
+
case 'size':
|
|
4142
|
+
applyDeclaration(style, parent, logical.physical, value, ctx);
|
|
4143
|
+
return;
|
|
4144
|
+
case 'corner': {
|
|
4145
|
+
const vertical = logical.block === 'start' ? 'top' : 'bottom';
|
|
4146
|
+
const left = (logical.inline === 'start') !== rtl;
|
|
4147
|
+
applyDeclaration(style, parent, `border-${vertical}-${left ? 'left' : 'right'}-radius`, value, ctx);
|
|
4148
|
+
return;
|
|
4149
|
+
}
|
|
4150
|
+
default: {
|
|
4151
|
+
const start = physicalSide(`${logical.axis}-start`, rtl);
|
|
4152
|
+
const end = physicalSide(`${logical.axis}-end`, rtl);
|
|
4153
|
+
let parts;
|
|
4154
|
+
if (logical.same || CSS_WIDE.has(value.toLowerCase())) {
|
|
4155
|
+
parts = [value, value];
|
|
4156
|
+
}
|
|
4157
|
+
else {
|
|
4158
|
+
parts = splitValue(value);
|
|
4159
|
+
if (parts.length === 1)
|
|
4160
|
+
parts.push(parts[0]);
|
|
4161
|
+
if (parts.length !== 2 || !parts.every((p) => logical.valid(p, ctx)))
|
|
4162
|
+
return;
|
|
4163
|
+
}
|
|
4164
|
+
applyDeclaration(style, parent, logical.physical(start), parts[0], ctx);
|
|
4165
|
+
applyDeclaration(style, parent, logical.physical(end), parts[1], ctx);
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
const CSS_WIDE = new Set(['inherit', 'initial', 'unset', 'revert']);
|
|
1045
4170
|
function camel(name) {
|
|
1046
4171
|
return name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1047
4172
|
}
|
|
1048
4173
|
const INHERITED_NAMES = new Set([
|
|
1049
4174
|
'color',
|
|
4175
|
+
'-webkit-text-fill-color',
|
|
1050
4176
|
'font',
|
|
1051
4177
|
'font-family',
|
|
1052
4178
|
'font-size',
|
|
@@ -1054,60 +4180,448 @@ const INHERITED_NAMES = new Set([
|
|
|
1054
4180
|
'font-style',
|
|
1055
4181
|
'line-height',
|
|
1056
4182
|
'text-align',
|
|
4183
|
+
'text-align-last',
|
|
4184
|
+
'text-justify',
|
|
4185
|
+
'block-ellipsis',
|
|
1057
4186
|
'text-indent',
|
|
1058
4187
|
'text-transform',
|
|
1059
4188
|
'letter-spacing',
|
|
1060
4189
|
'word-spacing',
|
|
1061
4190
|
'white-space',
|
|
4191
|
+
'overflow-wrap',
|
|
4192
|
+
'word-wrap',
|
|
4193
|
+
'word-break',
|
|
4194
|
+
'line-break',
|
|
4195
|
+
'white-space-collapse',
|
|
4196
|
+
'text-wrap',
|
|
4197
|
+
'text-wrap-mode',
|
|
4198
|
+
'text-wrap-style',
|
|
1062
4199
|
'direction',
|
|
1063
4200
|
'visibility',
|
|
1064
4201
|
'list-style',
|
|
1065
4202
|
'list-style-type',
|
|
1066
4203
|
'list-style-position',
|
|
4204
|
+
'list-style-image',
|
|
1067
4205
|
'cursor',
|
|
1068
4206
|
'border-collapse',
|
|
1069
4207
|
'border-spacing',
|
|
4208
|
+
'empty-cells',
|
|
4209
|
+
'quotes',
|
|
1070
4210
|
]);
|
|
1071
|
-
function isInherited(name) {
|
|
4211
|
+
export function isInherited(name) {
|
|
1072
4212
|
return INHERITED_NAMES.has(name);
|
|
1073
4213
|
}
|
|
1074
4214
|
/** `prop: inherit` — take the parent's computed value for whatever longhands
|
|
1075
4215
|
* the property names. Shorthands copy each of their longhands. */
|
|
4216
|
+
/** A property back to its initial value: what a declaration whose `var()`
|
|
4217
|
+
* has no value comes to, where the property is not inherited. */
|
|
4218
|
+
export function initialOne(style, initial, name) {
|
|
4219
|
+
const keys = INHERIT_TARGETS[name];
|
|
4220
|
+
if (!keys)
|
|
4221
|
+
return;
|
|
4222
|
+
for (const key of keys) {
|
|
4223
|
+
style[key] = initial[key];
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
1076
4226
|
function inheritOne(style, parent, name) {
|
|
1077
4227
|
const keys = INHERIT_TARGETS[name];
|
|
1078
4228
|
if (!keys)
|
|
1079
4229
|
return;
|
|
4230
|
+
// A border colour left to `currentColor` inherits as the keyword and
|
|
4231
|
+
// takes the child's own colour (CSS Color 4) — not the parent's colour,
|
|
4232
|
+
// which is what CSS 2.1 computed it to.
|
|
1080
4233
|
for (const key of keys) {
|
|
1081
4234
|
style[key] = parent[key];
|
|
1082
4235
|
}
|
|
1083
4236
|
}
|
|
4237
|
+
const SIDES = ['Top', 'Right', 'Bottom', 'Left'];
|
|
4238
|
+
const sides = (make) => SIDES.map(make);
|
|
1084
4239
|
const INHERIT_TARGETS = {
|
|
1085
4240
|
color: ['color'],
|
|
4241
|
+
'-webkit-text-fill-color': ['textFillColor'],
|
|
4242
|
+
'background-clip': [
|
|
4243
|
+
'backgroundClipText',
|
|
4244
|
+
'backgroundClip',
|
|
4245
|
+
'backgroundClips',
|
|
4246
|
+
],
|
|
4247
|
+
'-webkit-background-clip': [
|
|
4248
|
+
'backgroundClipText',
|
|
4249
|
+
'backgroundClip',
|
|
4250
|
+
'backgroundClips',
|
|
4251
|
+
],
|
|
4252
|
+
'background-origin': ['backgroundOrigin', 'backgroundOrigins'],
|
|
4253
|
+
'border-image': ['borderImage'],
|
|
4254
|
+
'border-image-source': ['borderImage'],
|
|
4255
|
+
'border-image-slice': ['borderImage'],
|
|
4256
|
+
'border-image-width': ['borderImage'],
|
|
4257
|
+
'border-image-outset': ['borderImage'],
|
|
4258
|
+
'border-image-repeat': ['borderImage'],
|
|
1086
4259
|
'font-family': ['fontFamily'],
|
|
1087
4260
|
'font-size': ['fontSize'],
|
|
1088
4261
|
'font-weight': ['fontWeight'],
|
|
1089
4262
|
'font-style': ['fontStyle'],
|
|
1090
|
-
|
|
4263
|
+
// the unit travels with the height: a length inherited as a bare number
|
|
4264
|
+
// would be read as a multiple of the font size
|
|
4265
|
+
font: [
|
|
4266
|
+
'fontFamily',
|
|
4267
|
+
'fontSize',
|
|
4268
|
+
'fontWeight',
|
|
4269
|
+
'fontStyle',
|
|
4270
|
+
'lineHeight',
|
|
4271
|
+
'lineHeightIsLength',
|
|
4272
|
+
'fontVariantNumeric',
|
|
4273
|
+
'fontVariantCaps',
|
|
4274
|
+
'fontVariantLigatures',
|
|
4275
|
+
'fontVariantPosition',
|
|
4276
|
+
'fontKerning',
|
|
4277
|
+
],
|
|
1091
4278
|
'line-height': ['lineHeight', 'lineHeightIsLength'],
|
|
1092
|
-
'text-align': ['textAlign'],
|
|
4279
|
+
'text-align': ['textAlign', 'alignBlocks'],
|
|
4280
|
+
'text-align-last': ['textAlignLast'],
|
|
4281
|
+
'text-justify': ['textJustify'],
|
|
1093
4282
|
'text-indent': ['textIndent'],
|
|
1094
4283
|
'text-transform': ['textTransform'],
|
|
1095
4284
|
'letter-spacing': ['letterSpacing'],
|
|
1096
4285
|
'word-spacing': ['wordSpacing'],
|
|
4286
|
+
'font-variant': [
|
|
4287
|
+
'fontVariantNumeric',
|
|
4288
|
+
'fontVariantCaps',
|
|
4289
|
+
'fontVariantLigatures',
|
|
4290
|
+
'fontVariantPosition',
|
|
4291
|
+
],
|
|
4292
|
+
'font-variant-numeric': ['fontVariantNumeric'],
|
|
4293
|
+
'font-variant-caps': ['fontVariantCaps'],
|
|
4294
|
+
'font-variant-ligatures': ['fontVariantLigatures'],
|
|
4295
|
+
'font-variant-position': ['fontVariantPosition'],
|
|
4296
|
+
'font-kerning': ['fontKerning'],
|
|
4297
|
+
'font-feature-settings': ['fontFeatureSettings'],
|
|
4298
|
+
'tab-size': ['tabSize', 'tabSizeIsLength'],
|
|
4299
|
+
'-moz-tab-size': ['tabSize', 'tabSizeIsLength'],
|
|
1097
4300
|
'white-space': ['whiteSpace'],
|
|
4301
|
+
'overflow-wrap': ['overflowWrap'],
|
|
4302
|
+
'word-wrap': ['overflowWrap'],
|
|
4303
|
+
'word-break': ['wordBreak'],
|
|
4304
|
+
'line-break': ['lineBreakAnywhere'],
|
|
4305
|
+
'white-space-collapse': ['whiteSpace'],
|
|
4306
|
+
'text-wrap': ['whiteSpace', 'textWrapStyle'],
|
|
4307
|
+
'text-wrap-mode': ['whiteSpace'],
|
|
4308
|
+
'text-wrap-style': ['textWrapStyle'],
|
|
1098
4309
|
direction: ['direction'],
|
|
4310
|
+
'unicode-bidi': ['unicodeBidi'],
|
|
1099
4311
|
visibility: ['visibility'],
|
|
1100
|
-
'list-style': ['listStyleType', 'listStylePosition'],
|
|
4312
|
+
'list-style': ['listStyleType', 'listStylePosition', 'listStyleImage'],
|
|
1101
4313
|
'list-style-type': ['listStyleType'],
|
|
1102
4314
|
'list-style-position': ['listStylePosition'],
|
|
4315
|
+
'list-style-image': ['listStyleImage'],
|
|
1103
4316
|
cursor: ['cursor'],
|
|
1104
4317
|
'border-collapse': ['borderCollapse'],
|
|
1105
|
-
'
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
4318
|
+
'caption-side': ['captionSide'],
|
|
4319
|
+
'empty-cells': ['emptyCells'],
|
|
4320
|
+
quotes: ['quotes'],
|
|
4321
|
+
content: ['content'],
|
|
4322
|
+
'counter-reset': ['counterReset'],
|
|
4323
|
+
'counter-increment': ['counterIncrement'],
|
|
4324
|
+
'counter-set': ['counterSet'],
|
|
4325
|
+
'border-spacing': ['borderSpacing', 'borderSpacingY'],
|
|
4326
|
+
display: ['display', 'flowRoot', 'webkitBox', 'grid'],
|
|
4327
|
+
width: ['width', 'widthKeyword'],
|
|
4328
|
+
height: ['height', 'heightKeyword'],
|
|
4329
|
+
'min-width': ['minWidth', 'minWidthKeyword'],
|
|
4330
|
+
'max-width': ['maxWidth', 'maxWidthKeyword'],
|
|
4331
|
+
'min-height': ['minHeight', 'minHeightKeyword'],
|
|
4332
|
+
'max-height': ['maxHeight', 'maxHeightKeyword'],
|
|
4333
|
+
'box-sizing': ['boxSizing'],
|
|
4334
|
+
margin: sides((s) => `margin${s}`),
|
|
4335
|
+
'margin-top': ['marginTop'],
|
|
4336
|
+
'margin-right': ['marginRight'],
|
|
4337
|
+
'margin-bottom': ['marginBottom'],
|
|
4338
|
+
'margin-left': ['marginLeft'],
|
|
4339
|
+
padding: sides((s) => `padding${s}`),
|
|
4340
|
+
'padding-top': ['paddingTop'],
|
|
4341
|
+
'padding-right': ['paddingRight'],
|
|
4342
|
+
'padding-bottom': ['paddingBottom'],
|
|
4343
|
+
'padding-left': ['paddingLeft'],
|
|
4344
|
+
border: [
|
|
4345
|
+
...sides((s) => `border${s}Width`),
|
|
4346
|
+
...sides((s) => `border${s}Style`),
|
|
4347
|
+
...sides((s) => `border${s}Color`),
|
|
4348
|
+
],
|
|
4349
|
+
'border-width': sides((s) => `border${s}Width`),
|
|
4350
|
+
'border-style': sides((s) => `border${s}Style`),
|
|
4351
|
+
'border-color': sides((s) => `border${s}Color`),
|
|
4352
|
+
...Object.fromEntries(SIDES.flatMap((s) => {
|
|
4353
|
+
const side = s.toLowerCase();
|
|
4354
|
+
return [
|
|
4355
|
+
[
|
|
4356
|
+
`border-${side}`,
|
|
4357
|
+
[`border${s}Width`, `border${s}Style`, `border${s}Color`],
|
|
4358
|
+
],
|
|
4359
|
+
[`border-${side}-width`, [`border${s}Width`]],
|
|
4360
|
+
[`border-${side}-style`, [`border${s}Style`]],
|
|
4361
|
+
[`border-${side}-color`, [`border${s}Color`]],
|
|
4362
|
+
];
|
|
4363
|
+
})),
|
|
4364
|
+
'box-shadow': ['boxShadow'],
|
|
4365
|
+
outline: ['outlineStyle', 'outlineWidth', 'outlineColor'],
|
|
4366
|
+
'outline-style': ['outlineStyle'],
|
|
4367
|
+
'outline-width': ['outlineWidth'],
|
|
4368
|
+
'outline-color': ['outlineColor'],
|
|
4369
|
+
'outline-offset': ['outlineOffset'],
|
|
4370
|
+
'text-shadow': ['textShadow'],
|
|
4371
|
+
'line-clamp': ['maxLines', 'clampContinue', 'blockEllipsis'],
|
|
4372
|
+
'-webkit-line-clamp': ['maxLines', 'clampContinue', 'blockEllipsis'],
|
|
4373
|
+
'max-lines': ['maxLines'],
|
|
4374
|
+
continue: ['clampContinue'],
|
|
4375
|
+
'block-ellipsis': ['blockEllipsis'],
|
|
4376
|
+
'-webkit-box-orient': ['webkitBoxOrient'],
|
|
4377
|
+
'-webkit-box-direction': ['webkitBoxDirection'],
|
|
4378
|
+
'-webkit-box-pack': ['webkitBoxPack'],
|
|
4379
|
+
'-webkit-box-align': ['webkitBoxAlign'],
|
|
4380
|
+
'-webkit-box-flex': ['webkitBoxFlex'],
|
|
4381
|
+
columns: ['columns'],
|
|
4382
|
+
'column-count': ['columns'],
|
|
4383
|
+
'column-width': ['columns'],
|
|
4384
|
+
'text-overflow': ['textOverflow'],
|
|
4385
|
+
// a flex box's and its items', and a grid's
|
|
4386
|
+
'flex-direction': ['flexDirection'],
|
|
4387
|
+
'flex-wrap': ['flexWrap'],
|
|
4388
|
+
'flex-flow': ['flexDirection', 'flexWrap'],
|
|
4389
|
+
'justify-content': ['justifyContent'],
|
|
4390
|
+
'align-items': ['alignItems'],
|
|
4391
|
+
'align-self': ['alignSelf'],
|
|
4392
|
+
'align-content': ['alignContent'],
|
|
4393
|
+
flex: ['flexGrow', 'flexShrink', 'flexBasis'],
|
|
4394
|
+
'flex-grow': ['flexGrow'],
|
|
4395
|
+
'flex-shrink': ['flexShrink'],
|
|
4396
|
+
'flex-basis': ['flexBasis'],
|
|
4397
|
+
order: ['order'],
|
|
4398
|
+
gap: ['rowGap', 'columnGap'],
|
|
4399
|
+
'row-gap': ['rowGap'],
|
|
4400
|
+
'column-gap': ['columnGap'],
|
|
4401
|
+
'grid-gap': ['rowGap', 'columnGap'],
|
|
4402
|
+
'grid-row-gap': ['rowGap'],
|
|
4403
|
+
'grid-column-gap': ['columnGap'],
|
|
4404
|
+
'grid-template-columns': ['gridColumns'],
|
|
4405
|
+
'grid-template-rows': ['gridRows'],
|
|
4406
|
+
'grid-auto-rows': ['gridAutoRows'],
|
|
4407
|
+
'grid-auto-columns': ['gridAutoColumns'],
|
|
4408
|
+
'grid-template-areas': ['gridAreas'],
|
|
4409
|
+
'grid-auto-flow': ['gridAutoFlow'],
|
|
4410
|
+
'grid-template': ['gridRows', 'gridColumns', 'gridAreas'],
|
|
4411
|
+
grid: [
|
|
4412
|
+
'gridRows',
|
|
4413
|
+
'gridColumns',
|
|
4414
|
+
'gridAreas',
|
|
4415
|
+
'gridAutoRows',
|
|
4416
|
+
'gridAutoColumns',
|
|
4417
|
+
'gridAutoFlow',
|
|
4418
|
+
],
|
|
4419
|
+
'grid-area': [
|
|
4420
|
+
'gridRowStart',
|
|
4421
|
+
'gridColumnStart',
|
|
4422
|
+
'gridRowEnd',
|
|
4423
|
+
'gridColumnEnd',
|
|
4424
|
+
],
|
|
4425
|
+
'grid-row': ['gridRowStart', 'gridRowEnd'],
|
|
4426
|
+
'grid-row-start': ['gridRowStart'],
|
|
4427
|
+
'grid-row-end': ['gridRowEnd'],
|
|
4428
|
+
'grid-column': ['gridColumnStart', 'gridColumnEnd'],
|
|
4429
|
+
'grid-column-start': ['gridColumnStart'],
|
|
4430
|
+
'grid-column-end': ['gridColumnEnd'],
|
|
4431
|
+
'justify-items': ['justifyItems'],
|
|
4432
|
+
'justify-self': ['justifySelf'],
|
|
4433
|
+
'place-items': ['alignItems', 'justifyItems'],
|
|
4434
|
+
'place-self': ['alignSelf', 'justifySelf'],
|
|
4435
|
+
'border-radius': ['borderRadius', 'borderRadiusY'],
|
|
4436
|
+
'border-top-left-radius': ['borderRadius', 'borderRadiusY'],
|
|
4437
|
+
'border-top-right-radius': ['borderRadius', 'borderRadiusY'],
|
|
4438
|
+
'border-bottom-right-radius': ['borderRadius', 'borderRadiusY'],
|
|
4439
|
+
'border-bottom-left-radius': ['borderRadius', 'borderRadiusY'],
|
|
4440
|
+
inset: ['top', 'right', 'bottom', 'left'],
|
|
4441
|
+
background: [
|
|
4442
|
+
'backgroundColor',
|
|
4443
|
+
'backgroundImage',
|
|
4444
|
+
'backgroundGradient',
|
|
4445
|
+
'backgroundRepeat',
|
|
4446
|
+
'backgroundSize',
|
|
4447
|
+
'backgroundAttachment',
|
|
4448
|
+
'backgroundPositionX',
|
|
4449
|
+
'backgroundPositionY',
|
|
4450
|
+
'backgroundImages',
|
|
4451
|
+
'backgroundRepeats',
|
|
4452
|
+
'backgroundSizes',
|
|
4453
|
+
'backgroundAttachments',
|
|
4454
|
+
'backgroundPositions',
|
|
4455
|
+
'backgroundClipText',
|
|
4456
|
+
'backgroundClip',
|
|
4457
|
+
'backgroundOrigin',
|
|
4458
|
+
'backgroundClips',
|
|
4459
|
+
'backgroundOrigins',
|
|
4460
|
+
],
|
|
1109
4461
|
'background-color': ['backgroundColor'],
|
|
4462
|
+
'background-image': [
|
|
4463
|
+
'backgroundImage',
|
|
4464
|
+
'backgroundGradient',
|
|
4465
|
+
'backgroundImages',
|
|
4466
|
+
],
|
|
4467
|
+
'background-repeat': ['backgroundRepeat', 'backgroundRepeats'],
|
|
4468
|
+
'background-size': ['backgroundSize', 'backgroundSizes'],
|
|
4469
|
+
'background-attachment': ['backgroundAttachment', 'backgroundAttachments'],
|
|
4470
|
+
'background-position': [
|
|
4471
|
+
'backgroundPositionX',
|
|
4472
|
+
'backgroundPositionY',
|
|
4473
|
+
'backgroundPositions',
|
|
4474
|
+
],
|
|
4475
|
+
'aspect-ratio': ['aspectRatio'],
|
|
4476
|
+
'object-fit': ['objectFit'],
|
|
4477
|
+
appearance: ['appearance'],
|
|
4478
|
+
'-webkit-appearance': ['appearance'],
|
|
4479
|
+
'-moz-appearance': ['appearance'],
|
|
4480
|
+
contain: ['contain'],
|
|
4481
|
+
'content-visibility': ['contentVisibility'],
|
|
4482
|
+
'overflow-clip-margin': ['overflowClipBox', 'overflowClipMargin'],
|
|
4483
|
+
'contain-intrinsic-size': ['containIntrinsicWidth', 'containIntrinsicHeight'],
|
|
4484
|
+
'contain-intrinsic-width': ['containIntrinsicWidth'],
|
|
4485
|
+
'contain-intrinsic-height': ['containIntrinsicHeight'],
|
|
4486
|
+
'contain-intrinsic-inline-size': ['containIntrinsicWidth'],
|
|
4487
|
+
'contain-intrinsic-block-size': ['containIntrinsicHeight'],
|
|
4488
|
+
'object-position': ['objectPositionX', 'objectPositionY'],
|
|
4489
|
+
position: ['position'],
|
|
4490
|
+
top: ['top'],
|
|
4491
|
+
right: ['right'],
|
|
4492
|
+
bottom: ['bottom'],
|
|
4493
|
+
left: ['left'],
|
|
4494
|
+
float: ['float'],
|
|
4495
|
+
clear: ['clear'],
|
|
4496
|
+
overflow: ['overflowX', 'overflowY'],
|
|
4497
|
+
clip: ['clip'],
|
|
4498
|
+
'overflow-x': ['overflowX'],
|
|
4499
|
+
'overflow-y': ['overflowY'],
|
|
4500
|
+
opacity: ['opacity'],
|
|
4501
|
+
translate: ['translate'],
|
|
4502
|
+
transform: ['transformTranslate'],
|
|
4503
|
+
'z-index': ['zIndex'],
|
|
4504
|
+
'vertical-align': ['verticalAlign'],
|
|
4505
|
+
'text-decoration': [
|
|
4506
|
+
'textDecorationLine',
|
|
4507
|
+
'textDecorationColor',
|
|
4508
|
+
'textDecorationStyle',
|
|
4509
|
+
],
|
|
4510
|
+
'text-decoration-line': ['textDecorationLine'],
|
|
4511
|
+
'text-decoration-style': ['textDecorationStyle'],
|
|
4512
|
+
'text-decoration-thickness': ['textDecorationThickness'],
|
|
4513
|
+
'text-underline-offset': ['textUnderlineOffset'],
|
|
4514
|
+
'table-layout': ['tableLayout'],
|
|
1110
4515
|
};
|
|
4516
|
+
/**
|
|
4517
|
+
* A decoration's thickness or an underline's offset: a length, a
|
|
4518
|
+
* percentage of the font size, or null for `auto` (and `alsoAuto`, which is
|
|
4519
|
+
* `from-font` for a thickness); undefined for a value that is none.
|
|
4520
|
+
*/
|
|
4521
|
+
function decorationLength(value, ctx, alsoAuto) {
|
|
4522
|
+
const v = value.trim().toLowerCase();
|
|
4523
|
+
if (v === 'auto' || v === alsoAuto)
|
|
4524
|
+
return null;
|
|
4525
|
+
const pct = /^(-?\d*\.?\d+)%$/.exec(v);
|
|
4526
|
+
if (pct)
|
|
4527
|
+
return (Number(pct[1]) / 100) * ctx.em;
|
|
4528
|
+
const len = parseLength(v, ctx);
|
|
4529
|
+
return typeof len === 'number' ? len : undefined;
|
|
4530
|
+
}
|
|
4531
|
+
/**
|
|
4532
|
+
* The decorations a box's text is drawn with (CSS 2.1 16.3.1): those its
|
|
4533
|
+
* ancestors propagate to it, and its own, each in the colour of the box that
|
|
4534
|
+
* set it, so an underlined link's `<strong>` is underlined in the link's
|
|
4535
|
+
* colour. A float, an absolutely positioned box and an atomic inline box —
|
|
4536
|
+
* an inline block, table or flex box — take none from above, and `none`
|
|
4537
|
+
* takes none away. After `blockify`, whose `float` and `position` it reads.
|
|
4538
|
+
*/
|
|
4539
|
+
export function decorate(style) {
|
|
4540
|
+
const display = style.display;
|
|
4541
|
+
if (style.float !== 'none' ||
|
|
4542
|
+
style.position === 'absolute' ||
|
|
4543
|
+
style.position === 'fixed' ||
|
|
4544
|
+
display === 'inline-block' ||
|
|
4545
|
+
display === 'inline-table' ||
|
|
4546
|
+
display === 'inline-flex') {
|
|
4547
|
+
style.underline = null;
|
|
4548
|
+
style.lineThrough = null;
|
|
4549
|
+
}
|
|
4550
|
+
const own = style.textDecorationLine;
|
|
4551
|
+
if (own === 'none')
|
|
4552
|
+
return;
|
|
4553
|
+
const lines = own.split(' ');
|
|
4554
|
+
if (lines.includes('underline')) {
|
|
4555
|
+
style.underline = inkColor(style.textDecorationColor ?? 'currentColor', style.color);
|
|
4556
|
+
style.underlineStyle = style.textDecorationStyle;
|
|
4557
|
+
style.underlineThickness = style.textDecorationThickness;
|
|
4558
|
+
style.underlineOffset = style.textUnderlineOffset;
|
|
4559
|
+
}
|
|
4560
|
+
if (lines.includes('line-through')) {
|
|
4561
|
+
style.lineThrough = inkColor(style.textDecorationColor ?? 'currentColor', style.color);
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
/**
|
|
4565
|
+
* The absolutely positioned styles that were inline-level before they were
|
|
4566
|
+
* made blocks: where such a box would have been in flow is on its line,
|
|
4567
|
+
* where a block's is under it (CSS 2.1 10.3.7, `staticPositions`).
|
|
4568
|
+
*/
|
|
4569
|
+
export const INLINE_BEFORE_ABSOLUTE = new WeakSet();
|
|
4570
|
+
/**
|
|
4571
|
+
* `line-clamp`'s longhands and the legacy flexible box they are written
|
|
4572
|
+
* with, settled once the cascade is done, since each reads the others
|
|
4573
|
+
* (CSS Overflow 4, 5.1.1 and 5.3). `continue: collapse` makes a block
|
|
4574
|
+
* container a line-clamp container, and `-webkit-line-clamp`'s
|
|
4575
|
+
* `-webkit-legacy` does only in a `display: -webkit-box` whose
|
|
4576
|
+
* `-webkit-box-orient` is vertical — which is then a block of its own
|
|
4577
|
+
* formatting context, as Tailwind's `line-clamp-2` writes all four. Any
|
|
4578
|
+
* other `-webkit-box` is a flex box, in its orient's direction, packed and
|
|
4579
|
+
* aligned as it says, whose items grow and shrink by `-webkit-box-flex`
|
|
4580
|
+
* alone (as Blink lays one out). Before `blockify`, whose `display` this
|
|
4581
|
+
* decides.
|
|
4582
|
+
*/
|
|
4583
|
+
export function settleClamp(style, parent) {
|
|
4584
|
+
const box = style.webkitBox;
|
|
4585
|
+
const vertical = style.webkitBoxOrient === 'vertical';
|
|
4586
|
+
const collapses = style.clampContinue === 'collapse' ||
|
|
4587
|
+
(style.clampContinue === 'legacy' && box !== null && vertical);
|
|
4588
|
+
if (box !== null) {
|
|
4589
|
+
if (collapses && vertical) {
|
|
4590
|
+
style.display = box === 'inline' ? 'inline-block' : 'block';
|
|
4591
|
+
style.flowRoot = box === 'block';
|
|
4592
|
+
}
|
|
4593
|
+
else {
|
|
4594
|
+
const reverse = style.webkitBoxDirection === 'reverse';
|
|
4595
|
+
style.flexDirection = vertical
|
|
4596
|
+
? reverse
|
|
4597
|
+
? 'column-reverse'
|
|
4598
|
+
: 'column'
|
|
4599
|
+
: reverse
|
|
4600
|
+
? 'row-reverse'
|
|
4601
|
+
: 'row';
|
|
4602
|
+
style.flexWrap = 'nowrap';
|
|
4603
|
+
style.justifyContent = style.webkitBoxPack ?? 'flex-start';
|
|
4604
|
+
style.alignItems = style.webkitBoxAlign ?? 'stretch';
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
if (parent?.webkitBox &&
|
|
4608
|
+
(parent.display === 'flex' || parent.display === 'inline-flex')) {
|
|
4609
|
+
style.flexGrow = style.webkitBoxFlex;
|
|
4610
|
+
style.flexShrink = style.webkitBoxFlex;
|
|
4611
|
+
}
|
|
4612
|
+
style.lineClamp =
|
|
4613
|
+
collapses && CLAMPS.has(style.display) && !style.columns
|
|
4614
|
+
? (style.maxLines ?? Infinity)
|
|
4615
|
+
: null;
|
|
4616
|
+
}
|
|
4617
|
+
/** The displays of a block container, which `continue` applies to. */
|
|
4618
|
+
const CLAMPS = new Set([
|
|
4619
|
+
'block',
|
|
4620
|
+
'inline-block',
|
|
4621
|
+
'list-item',
|
|
4622
|
+
'table-cell',
|
|
4623
|
+
'table-caption',
|
|
4624
|
+
]);
|
|
1111
4625
|
/**
|
|
1112
4626
|
* The blockification the box tree depends on: a floated or absolutely
|
|
1113
4627
|
* positioned element is a block whatever `display` said, and a flex item's
|
|
@@ -1123,6 +4637,13 @@ export function blockify(style, inFlexContainer) {
|
|
|
1123
4637
|
style.position === 'fixed';
|
|
1124
4638
|
if (!isOutOfFlow && !inFlexContainer)
|
|
1125
4639
|
return;
|
|
4640
|
+
if ((style.position === 'absolute' || style.position === 'fixed') &&
|
|
4641
|
+
(out === 'inline' ||
|
|
4642
|
+
out === 'inline-block' ||
|
|
4643
|
+
out === 'inline-table' ||
|
|
4644
|
+
out === 'inline-flex')) {
|
|
4645
|
+
INLINE_BEFORE_ABSOLUTE.add(style);
|
|
4646
|
+
}
|
|
1126
4647
|
switch (out) {
|
|
1127
4648
|
case 'inline':
|
|
1128
4649
|
case 'inline-block':
|
|
@@ -1132,6 +4653,8 @@ export function blockify(style, inFlexContainer) {
|
|
|
1132
4653
|
case 'table-header-group':
|
|
1133
4654
|
case 'table-footer-group':
|
|
1134
4655
|
case 'table-caption':
|
|
4656
|
+
case 'table-column':
|
|
4657
|
+
case 'table-column-group':
|
|
1135
4658
|
style.display = 'block';
|
|
1136
4659
|
return;
|
|
1137
4660
|
case 'inline-flex':
|