Shagen Ogandzhanian
3 years ago
committed by
GitHub
7 changed files with 1246 additions and 0 deletions
@ -0,0 +1,49 @@ |
|||||||
|
plugins { |
||||||
|
kotlin("multiplatform") |
||||||
|
id("org.jetbrains.compose") |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
kotlin { |
||||||
|
js(IR) { |
||||||
|
browser() { |
||||||
|
testTask { |
||||||
|
testLogging.showStandardStreams = true |
||||||
|
useKarma { |
||||||
|
useChromeHeadless() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
binaries.executable() |
||||||
|
} |
||||||
|
|
||||||
|
sourceSets { |
||||||
|
val commonMain by getting { |
||||||
|
dependencies { |
||||||
|
implementation(compose.runtime) |
||||||
|
implementation(kotlin("stdlib-common")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
val jsMain by getting { |
||||||
|
dependencies { |
||||||
|
implementation(project(":internal-web-core-runtime")) |
||||||
|
implementation(kotlin("stdlib-js")) |
||||||
|
implementation(project(":web-core")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
val jsTest by getting { |
||||||
|
dependencies { |
||||||
|
implementation(project(":test-utils")) |
||||||
|
implementation(kotlin("test-js")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
all { |
||||||
|
languageSettings { |
||||||
|
useExperimentalAnnotation("org.jetbrains.compose.web.testutils.ComposeWebExperimentalTestsApi") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.web |
||||||
|
|
||||||
|
@RequiresOptIn("This API is experimental and is likely to change in the future.") |
||||||
|
annotation class ExperimentalComposeWebSvgApi |
@ -0,0 +1,729 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.web.svg |
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import kotlinx.browser.document |
||||||
|
import org.jetbrains.compose.web.ExperimentalComposeWebSvgApi |
||||||
|
import org.jetbrains.compose.web.css.CSSLengthOrPercentageValue |
||||||
|
import org.jetbrains.compose.web.dom.* |
||||||
|
import org.w3c.css.masking.SVGClipPathElement |
||||||
|
import org.w3c.css.masking.SVGMaskElement |
||||||
|
import org.w3c.dom.Element |
||||||
|
import org.w3c.dom.svg.* |
||||||
|
|
||||||
|
private open class ElementBuilderNS<TElement : Element>(private val tagName: String, private val namespace: String) : |
||||||
|
ElementBuilder<TElement> { |
||||||
|
private val el: Element by lazy { document.createElementNS(namespace, tagName) } |
||||||
|
override fun create(): TElement = el.cloneNode().unsafeCast<TElement>() |
||||||
|
} |
||||||
|
|
||||||
|
const val SVG_NS = "http://www.w3.org/2000/svg" |
||||||
|
|
||||||
|
private val A = ElementBuilderNS<SVGAElement>("a", SVG_NS) |
||||||
|
private val Animate = ElementBuilderNS<SVGElement>("animate", SVG_NS) |
||||||
|
private val AnimateMotion = ElementBuilderNS<SVGElement>("animateMotion", SVG_NS) |
||||||
|
private val AnimateTransform = ElementBuilderNS<SVGElement>("animateTransform", SVG_NS) |
||||||
|
private val Circle = ElementBuilderNS<SVGCircleElement>("circle", SVG_NS) |
||||||
|
private val ClipPath = ElementBuilderNS<SVGClipPathElement>("clipPath", SVG_NS) |
||||||
|
private val Defs = ElementBuilderNS<SVGDefsElement>("defs", SVG_NS) |
||||||
|
private val Desc = ElementBuilderNS<SVGDescElement>("desc", SVG_NS) |
||||||
|
private val Ellipse = ElementBuilderNS<SVGEllipseElement>("ellipse", SVG_NS) |
||||||
|
private val Filter = ElementBuilderNS<SVGElement>("filter", SVG_NS) |
||||||
|
private val G = ElementBuilderNS<SVGElement>("g", SVG_NS) |
||||||
|
private val Image = ElementBuilderNS<SVGImageElement>("image", SVG_NS) |
||||||
|
private val Line = ElementBuilderNS<SVGLineElement>("line", SVG_NS) |
||||||
|
private val LinearGradient = ElementBuilderNS<SVGLinearGradientElement>("linearGradient", SVG_NS) |
||||||
|
private val Marker = ElementBuilderNS<SVGMarkerElement>("marker", SVG_NS) |
||||||
|
private val Mask = ElementBuilderNS<SVGMaskElement>("mask", SVG_NS) |
||||||
|
private val Mpath = ElementBuilderNS<SVGElement>("mpath", SVG_NS) |
||||||
|
private val Path = ElementBuilderNS<SVGPathElement>("path", SVG_NS) |
||||||
|
private val Pattern = ElementBuilderNS<SVGPatternElement>("pattern", SVG_NS) |
||||||
|
private val Polygon = ElementBuilderNS<SVGPolygonElement>("polygon", SVG_NS) |
||||||
|
private val Polyline = ElementBuilderNS<SVGPolylineElement>("polyline", SVG_NS) |
||||||
|
private val RadialGradient = ElementBuilderNS<SVGRadialGradientElement>("radialGradient", SVG_NS) |
||||||
|
private val Rect = ElementBuilderNS<SVGRectElement>("rect", SVG_NS) |
||||||
|
private val Set = ElementBuilderNS<SVGElement>("set", SVG_NS) |
||||||
|
private val Stop = ElementBuilderNS<SVGStopElement>("stop", SVG_NS) |
||||||
|
private val Svg = ElementBuilderNS<SVGElement>("svg", SVG_NS) |
||||||
|
private val Switch = ElementBuilderNS<SVGSwitchElement>("switch", SVG_NS) |
||||||
|
private val Symbol = ElementBuilderNS<SVGSymbolElement>("symbol", SVG_NS) |
||||||
|
private val Text = ElementBuilderNS<SVGTextElement>("text", SVG_NS) |
||||||
|
private val TextPath = ElementBuilderNS<SVGTextPathElement>("textPath", SVG_NS) |
||||||
|
private val Title = ElementBuilderNS<SVGTitleElement>("title", SVG_NS) |
||||||
|
private val Tspan = ElementBuilderNS<SVGTSpanElement>("tspan", SVG_NS) |
||||||
|
private val Use = ElementBuilderNS<SVGUseElement>("use", SVG_NS) |
||||||
|
private val View = ElementBuilderNS<SVGViewElement>("view", SVG_NS) |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun Svg( |
||||||
|
viewBox: String? = null, |
||||||
|
attrs: AttrBuilderContext<SVGElement>? = null, |
||||||
|
content: ContentBuilder<SVGElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Svg, |
||||||
|
applyAttrs = { |
||||||
|
viewBox?.let { attr("viewBox", it) } |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.SvgA( |
||||||
|
href: String, |
||||||
|
attrs: AttrBuilderContext<SVGAElement>? = null, |
||||||
|
content: ContentBuilder<SVGAElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = A, |
||||||
|
applyAttrs = { |
||||||
|
attr("href", href) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Circle( |
||||||
|
cx: CSSLengthOrPercentageValue, |
||||||
|
cy: CSSLengthOrPercentageValue, |
||||||
|
r: CSSLengthOrPercentageValue, |
||||||
|
attrs: AttrBuilderContext<SVGCircleElement>? = null, |
||||||
|
content: ContentBuilder<SVGCircleElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Circle, |
||||||
|
applyAttrs = { |
||||||
|
attr("cx", cx.toString()) |
||||||
|
attr("cy", cy.toString()) |
||||||
|
attr("r", r.toString()) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Circle( |
||||||
|
cx: Number, |
||||||
|
cy: Number, |
||||||
|
r: Number, |
||||||
|
attrs: AttrBuilderContext<SVGCircleElement>? = null, |
||||||
|
content: ContentBuilder<SVGCircleElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Circle, |
||||||
|
applyAttrs = { |
||||||
|
attr("cx", cx.toString()) |
||||||
|
attr("cy", cy.toString()) |
||||||
|
attr("r", r.toString()) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.SvgText( |
||||||
|
text: String, |
||||||
|
x: Number = 0, |
||||||
|
y: Number = 0, |
||||||
|
attrs: AttrBuilderContext<SVGTextElement>? = null, |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Text, |
||||||
|
applyAttrs = { |
||||||
|
attr("x", x.toString()) |
||||||
|
attr("y", y.toString()) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = { |
||||||
|
Text(text) |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.View( |
||||||
|
id: String, |
||||||
|
viewBox: String, |
||||||
|
attrs: AttrBuilderContext<SVGViewElement>? = null, |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = View, |
||||||
|
applyAttrs = { |
||||||
|
attr("id", id) |
||||||
|
attr("viewBox", viewBox) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = null |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Rect( |
||||||
|
x: Number, |
||||||
|
y: Number, |
||||||
|
width: Number, |
||||||
|
height: Number, |
||||||
|
attrs: AttrBuilderContext<SVGRectElement>? = null, |
||||||
|
content: ContentBuilder<SVGRectElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Rect, |
||||||
|
applyAttrs = { |
||||||
|
attr("x", x.toString()) |
||||||
|
attr("y", y.toString()) |
||||||
|
attr("width", width.toString()) |
||||||
|
attr("height", height.toString()) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Rect( |
||||||
|
x: CSSLengthOrPercentageValue, |
||||||
|
y: CSSLengthOrPercentageValue, |
||||||
|
width: CSSLengthOrPercentageValue, |
||||||
|
height: CSSLengthOrPercentageValue, |
||||||
|
attrs: AttrBuilderContext<SVGRectElement>? = null, |
||||||
|
content: ContentBuilder<SVGRectElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Rect, |
||||||
|
applyAttrs = { |
||||||
|
attr("x", x.toString()) |
||||||
|
attr("y", y.toString()) |
||||||
|
attr("width", width.toString()) |
||||||
|
attr("height", height.toString()) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Ellipse( |
||||||
|
cx: CSSLengthOrPercentageValue, |
||||||
|
cy: CSSLengthOrPercentageValue, |
||||||
|
rx: CSSLengthOrPercentageValue, |
||||||
|
ry: CSSLengthOrPercentageValue, |
||||||
|
attrs: AttrBuilderContext<SVGEllipseElement>? = null, |
||||||
|
content: ContentBuilder<SVGEllipseElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Ellipse, |
||||||
|
applyAttrs = { |
||||||
|
attr("cx", cx.toString()) |
||||||
|
attr("cy", cy.toString()) |
||||||
|
attr("rx", rx.toString()) |
||||||
|
attr("ry", ry.toString()) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Ellipse( |
||||||
|
cx: Number, |
||||||
|
cy: Number, |
||||||
|
rx: Number, |
||||||
|
ry: Number, |
||||||
|
attrs: AttrBuilderContext<SVGEllipseElement>? = null, |
||||||
|
content: ContentBuilder<SVGEllipseElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Ellipse, |
||||||
|
applyAttrs = { |
||||||
|
attr("cx", cx.toString()) |
||||||
|
attr("cy", cy.toString()) |
||||||
|
attr("rx", rx.toString()) |
||||||
|
attr("ry", ry.toString()) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Symbol( |
||||||
|
id: String? = null, |
||||||
|
attrs: AttrBuilderContext<SVGSymbolElement>? = null, |
||||||
|
content: ContentBuilder<SVGSymbolElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Symbol, |
||||||
|
applyAttrs = { |
||||||
|
id?.let { attr("id", it) } |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Use( |
||||||
|
href: String, |
||||||
|
attrs: AttrBuilderContext<SVGUseElement>? = null, |
||||||
|
content: ContentBuilder<SVGUseElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Use, |
||||||
|
applyAttrs = { |
||||||
|
attr("href", href) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Line( |
||||||
|
x1: CSSLengthOrPercentageValue, |
||||||
|
y1: CSSLengthOrPercentageValue, |
||||||
|
x2: CSSLengthOrPercentageValue, |
||||||
|
y2: CSSLengthOrPercentageValue, |
||||||
|
attrs: AttrBuilderContext<SVGLineElement>? = null, |
||||||
|
content: ContentBuilder<SVGLineElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Line, |
||||||
|
applyAttrs = { |
||||||
|
attr("x1", x1.toString()) |
||||||
|
attr("y1", y1.toString()) |
||||||
|
attr("x2", x2.toString()) |
||||||
|
attr("y2", y2.toString()) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Line( |
||||||
|
x1: Number, |
||||||
|
y1: Number, |
||||||
|
x2: Number, |
||||||
|
y2: Number, |
||||||
|
attrs: AttrBuilderContext<SVGLineElement>? = null, |
||||||
|
content: ContentBuilder<SVGLineElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Line, |
||||||
|
applyAttrs = { |
||||||
|
attr("x1", x1.toString()) |
||||||
|
attr("y1", y1.toString()) |
||||||
|
attr("x2", x2.toString()) |
||||||
|
attr("y2", y2.toString()) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.ClipPath( |
||||||
|
id: String, |
||||||
|
attrs: AttrBuilderContext<SVGClipPathElement>? = null, |
||||||
|
content: ContentBuilder<SVGClipPathElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = ClipPath, |
||||||
|
applyAttrs = { |
||||||
|
attr("id", id) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Path( |
||||||
|
d: String, |
||||||
|
attrs: AttrBuilderContext<SVGPathElement>? = null, |
||||||
|
content: ContentBuilder<SVGPathElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Path, |
||||||
|
applyAttrs = { |
||||||
|
attr("d", d) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.G( |
||||||
|
attrs: AttrBuilderContext<SVGElement>? = null, |
||||||
|
content: ContentBuilder<SVGElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = G, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Image( |
||||||
|
href: String, |
||||||
|
attrs: AttrBuilderContext<SVGImageElement>? = null, |
||||||
|
content: ContentBuilder<SVGImageElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Image, |
||||||
|
applyAttrs = { |
||||||
|
attr("href", href) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Mask( |
||||||
|
id: String? = null, |
||||||
|
attrs: AttrBuilderContext<SVGMaskElement>? = null, |
||||||
|
content: ContentBuilder<SVGMaskElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Mask, |
||||||
|
applyAttrs = { |
||||||
|
id?.let { attr("id", it) } |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Defs( |
||||||
|
attrs: AttrBuilderContext<SVGDefsElement>? = null, |
||||||
|
content: ContentBuilder<SVGDefsElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Defs, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Pattern( |
||||||
|
id: String, |
||||||
|
attrs: AttrBuilderContext<SVGPatternElement>? = null, |
||||||
|
content: ContentBuilder<SVGPatternElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Pattern, |
||||||
|
applyAttrs = { |
||||||
|
attr("id", id) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Polygon( |
||||||
|
vararg points: Number, |
||||||
|
attrs: AttrBuilderContext<SVGPolygonElement>? = null, |
||||||
|
content: ContentBuilder<SVGPolygonElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Polygon, |
||||||
|
applyAttrs = { |
||||||
|
attr("points", points.toList().chunked(2).joinToString(" ") { it.joinToString(",") }) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Polyline( |
||||||
|
vararg points: Number, |
||||||
|
attrs: AttrBuilderContext<SVGPolylineElement>? = null, |
||||||
|
content: ContentBuilder<SVGPolylineElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Polyline, |
||||||
|
applyAttrs = { |
||||||
|
attr("points", points.toList().chunked(2).joinToString(" ") { it.joinToString(",") }) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.TextPath( |
||||||
|
href: String, |
||||||
|
text: String, |
||||||
|
attrs: AttrBuilderContext<SVGTextPathElement>? = null, |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = TextPath, |
||||||
|
applyAttrs = { |
||||||
|
attr("href", href) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = { |
||||||
|
Text(text) |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Animate( |
||||||
|
attrs: AttrBuilderContext<SVGElement>? = null, |
||||||
|
content: ContentBuilder<SVGElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Animate, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.AnimateMotion( |
||||||
|
attrs: AttrBuilderContext<SVGElement>? = null, |
||||||
|
content: ContentBuilder<SVGElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = AnimateMotion, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.AnimateTransform( |
||||||
|
attrs: AttrBuilderContext<SVGElement>? = null, |
||||||
|
content: ContentBuilder<SVGElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = AnimateTransform, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.LinearGradient( |
||||||
|
id: String? = null, |
||||||
|
attrs: AttrBuilderContext<SVGLinearGradientElement>? = null, |
||||||
|
content: ContentBuilder<SVGLinearGradientElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = LinearGradient, |
||||||
|
applyAttrs = { |
||||||
|
id?.let { attr("id", it) } |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.RadialGradient( |
||||||
|
id: String? = null, |
||||||
|
attrs: AttrBuilderContext<SVGRadialGradientElement>? = null, |
||||||
|
content: ContentBuilder<SVGRadialGradientElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = RadialGradient, |
||||||
|
applyAttrs = { |
||||||
|
id?.let { attr("id", it) } |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Stop( |
||||||
|
attrs: AttrBuilderContext<SVGStopElement>? = null, |
||||||
|
content: ContentBuilder<SVGStopElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Stop, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Switch( |
||||||
|
attrs: AttrBuilderContext<SVGSwitchElement>? = null, |
||||||
|
content: ContentBuilder<SVGSwitchElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Switch, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Title( |
||||||
|
text: String, |
||||||
|
attrs: AttrBuilderContext<SVGTitleElement>? = null, |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Title, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = { |
||||||
|
Text(text) |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Tspan( |
||||||
|
attrs: AttrBuilderContext<SVGTSpanElement>? = null, |
||||||
|
content: ContentBuilder<SVGTSpanElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Tspan, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Desc( |
||||||
|
content: String, |
||||||
|
attrs: AttrBuilderContext<SVGDescElement>? = null, |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Desc, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = { |
||||||
|
Text(content) |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Marker( |
||||||
|
attrs: AttrBuilderContext<SVGMarkerElement>? = null, |
||||||
|
content: ContentBuilder<SVGMarkerElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Marker, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Mpath( |
||||||
|
attrs: AttrBuilderContext<SVGElement>? = null, |
||||||
|
content: ContentBuilder<SVGElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Mpath, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Filter( |
||||||
|
attrs: AttrBuilderContext<SVGElement>? = null, |
||||||
|
content: ContentBuilder<SVGElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Filter, |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun ElementScope<SVGElement>.Set( |
||||||
|
attributeName: String, |
||||||
|
to: String, |
||||||
|
attrs: AttrBuilderContext<SVGElement>? = null, |
||||||
|
content: ContentBuilder<SVGElement>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = Set, |
||||||
|
applyAttrs = { |
||||||
|
attr("attributeName", attributeName) |
||||||
|
attr("to", to) |
||||||
|
attrs?.invoke(this) |
||||||
|
}, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
fun <T : SVGElement> SvgElement( |
||||||
|
name: String, |
||||||
|
attrs: AttrBuilderContext<T>? = null, |
||||||
|
content: ContentBuilder<T>? = null |
||||||
|
) { |
||||||
|
TagElement( |
||||||
|
elementBuilder = ElementBuilderNS(name, SVG_NS), |
||||||
|
applyAttrs = attrs, |
||||||
|
content = content |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,453 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.web.core.tests.svg |
||||||
|
|
||||||
|
import org.jetbrains.compose.web.ExperimentalComposeWebSvgApi |
||||||
|
import org.jetbrains.compose.web.css.opacity |
||||||
|
import org.jetbrains.compose.web.css.percent |
||||||
|
import org.jetbrains.compose.web.css.px |
||||||
|
import org.jetbrains.compose.web.svg.* |
||||||
|
import org.jetbrains.compose.web.testutils.* |
||||||
|
import org.w3c.dom.svg.SVGCircleElement |
||||||
|
import kotlin.test.Test |
||||||
|
import kotlin.test.assertEquals |
||||||
|
|
||||||
|
@ExperimentalComposeWebSvgApi |
||||||
|
class SvgTests { |
||||||
|
@Test |
||||||
|
fun nodeNames() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Animate() |
||||||
|
AnimateMotion() |
||||||
|
AnimateTransform() |
||||||
|
Defs() |
||||||
|
Filter() |
||||||
|
G() |
||||||
|
Marker() |
||||||
|
Mpath() |
||||||
|
Switch() |
||||||
|
Tspan() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><animate></animate><animateMotion></animateMotion><animateTransform></animateTransform><defs></defs><filter></filter><g></g><marker></marker><mpath></mpath><switch></switch><tspan></tspan></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun clipPathTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
ClipPath("myClip") { |
||||||
|
Circle(40.px, 35.px, 36.px) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><clipPath id=\"myClip\"><circle cx=\"40px\" cy=\"35px\" r=\"36px\"></circle></clipPath></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun maskTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Mask("myMask") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals("<svg><mask id=\"myMask\"></mask></svg>", nextChild<SVGCircleElement>().outerHTML) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
fun svgATest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
SvgA("/docs/Web/SVG/Element/circle", { |
||||||
|
attr("target", "_blank") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><a href=\"/docs/Web/SVG/Element/circle\" target=\"_blank\"></a></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun descTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Desc("some description", { attr("id", "myDesc") }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals("<svg><desc id=\"myDesc\">some description</desc></svg>", nextChild<SVGCircleElement>().outerHTML) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun setTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Rect(0, 0, 10, 10, { attr("id", "rect") }) { |
||||||
|
Set(attributeName = "class", to = "round", { attr("begin", "me.click"); attr("dur", "2s") }) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><rect x=\"0\" y=\"0\" width=\"10\" height=\"10\" id=\"rect\"><set attributeName=\"class\" to=\"round\" begin=\"me.click\" dur=\"2s\"></set></rect></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
fun titleTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Rect(10, 20, 30, 30) { |
||||||
|
Title("some title") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><rect x=\"10\" y=\"20\" width=\"30\" height=\"30\"><title>some title</title></rect></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun svgTextTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
SvgText("some text", 20, 30, { |
||||||
|
classes("small") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><text x=\"20\" y=\"30\" class=\"small\">some text</text></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun textPathTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
TextPath("#someHref", "Some text") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><textPath href=\"#someHref\">Some text</textPath></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
fun ellipseTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Ellipse(50, 60, 70, 20, { |
||||||
|
attr("color", "yellow") |
||||||
|
}) |
||||||
|
Ellipse(50.px, 60.px, 70.percent, 20.px, { |
||||||
|
attr("color", "red") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><ellipse cx=\"50\" cy=\"60\" rx=\"70\" ry=\"20\" color=\"yellow\"></ellipse><ellipse cx=\"50px\" cy=\"60px\" rx=\"70%\" ry=\"20px\" color=\"red\"></ellipse></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
fun circleTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Circle(50, 60, 70, { |
||||||
|
attr("color", "red") |
||||||
|
}) |
||||||
|
Circle(50.px, 60.px, 70.percent, { |
||||||
|
attr("color", "red") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><circle cx=\"50\" cy=\"60\" r=\"70\" color=\"red\"></circle><circle cx=\"50px\" cy=\"60px\" r=\"70%\" color=\"red\"></circle></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun rectTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Rect(0, 20, 100, 200, { |
||||||
|
attr("color", "red") |
||||||
|
}) |
||||||
|
Rect(0.px, 20.px, 100.px, 200.px, { |
||||||
|
attr("color", "red") |
||||||
|
}) |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><rect x=\"0\" y=\"20\" width=\"100\" height=\"200\" color=\"red\"></rect><rect x=\"0px\" y=\"20px\" width=\"100px\" height=\"200px\" color=\"red\"></rect></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
fun imageTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Image("/image.png", { |
||||||
|
attr("preserveAspectRatio", "xMidYMid meet") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><image href=\"/image.png\" preserveAspectRatio=\"xMidYMid meet\"></image></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun lineTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Line(0, 80, 100, 20, { |
||||||
|
attr("stroke", "red") |
||||||
|
}) |
||||||
|
Line(0.px, 80.px, 100.px, 20.px, { |
||||||
|
attr("stroke", "black") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><line x1=\"0\" y1=\"80\" x2=\"100\" y2=\"20\" stroke=\"red\"></line><line x1=\"0px\" y1=\"80px\" x2=\"100px\" y2=\"20px\" stroke=\"black\"></line></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun polylineTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Polyline(0, 100, 50, 25, 50, 75, 100, 0, attrs = { |
||||||
|
attr("stroke", "red") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><polyline points=\"0,100 50,25 50,75 100,0\" stroke=\"red\"></polyline></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
fun polygonTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Polygon(0, 100, 50, 25, 50, 75, 100, 0, attrs = { |
||||||
|
attr("stroke", "red") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><polygon points=\"0,100 50,25 50,75 100,0\" stroke=\"red\"></polygon></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun linearGradientTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
LinearGradient("myGradient") { |
||||||
|
Stop({ |
||||||
|
attr("offset", 10.percent.toString()) |
||||||
|
attr("stop-color", "gold") |
||||||
|
}) |
||||||
|
Stop({ |
||||||
|
attr("offset", 95.percent.toString()) |
||||||
|
attr("stop-color", "red") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><linearGradient id=\"myGradient\"><stop offset=\"10%\" stop-color=\"gold\"></stop><stop offset=\"95%\" stop-color=\"red\"></stop></linearGradient></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun radialGradientTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
RadialGradient("myGradient") { |
||||||
|
Stop({ |
||||||
|
attr("offset", 10.percent.toString()) |
||||||
|
attr("stop-color", "gold") |
||||||
|
}) |
||||||
|
Stop({ |
||||||
|
attr("offset", 95.percent.toString()) |
||||||
|
attr("stop-color", "red") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><radialGradient id=\"myGradient\"><stop offset=\"10%\" stop-color=\"gold\"></stop><stop offset=\"95%\" stop-color=\"red\"></stop></radialGradient></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
fun patternTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Pattern("something") { |
||||||
|
Polygon(0, 100, 50, 25, 50, 75, 100, 0, attrs = { |
||||||
|
attr("stroke", "red") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><pattern id=\"something\"><polygon points=\"0,100 50,25 50,75 100,0\" stroke=\"red\"></polygon></pattern></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun viewTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
View("one", "0 0 100 100") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><view id=\"one\" viewBox=\"0 0 100 100\"></view></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
fun pathTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Path( |
||||||
|
""" |
||||||
|
M 10,30 |
||||||
|
A 20,20 0,0,1 50,30 |
||||||
|
A 20,20 0,0,1 90,30 |
||||||
|
Q 90,60 50,90 |
||||||
|
Q 10,60 10,30 z |
||||||
|
""".trimIndent() |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><path d=\"M 10,30\n" + |
||||||
|
"A 20,20 0,0,1 50,30\n" + |
||||||
|
"A 20,20 0,0,1 90,30\n" + |
||||||
|
"Q 90,60 50,90\n" + |
||||||
|
"Q 10,60 10,30 z\"></path></svg>", nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun useTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
Symbol("myDot", { |
||||||
|
attr("width", "10") |
||||||
|
attr("height", "10") |
||||||
|
attr("viewBox", "0 0 2 2") |
||||||
|
}) { |
||||||
|
Circle(1.px, 1.px, 1.px) |
||||||
|
} |
||||||
|
|
||||||
|
Use("myDot", { |
||||||
|
attr("x", "5") |
||||||
|
attr("y", "5") |
||||||
|
style { |
||||||
|
opacity(1) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><symbol id=\"myDot\" width=\"10\" height=\"10\" viewBox=\"0 0 2 2\"><circle cx=\"1px\" cy=\"1px\" r=\"1px\"></circle></symbol><use href=\"myDot\" x=\"5\" y=\"5\" style=\"opacity: 1;\"></use></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun svgElementTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg { |
||||||
|
SvgElement<SVGCircleElement>("circle", { |
||||||
|
attr("cx", 12.px.toString()) |
||||||
|
attr("cy", 22.px.toString()) |
||||||
|
attr("r", 5.percent.toString()) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg><circle cx=\"12px\" cy=\"22px\" r=\"5%\"></circle></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun svgElementWithViewBoxTest() = runTest { |
||||||
|
composition { |
||||||
|
Svg(viewBox = "0 0 200 200") |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals( |
||||||
|
"<svg viewBox=\"0 0 200 200\"></svg>", |
||||||
|
nextChild<SVGCircleElement>().outerHTML |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue