package nl.astraeus.css.style import nl.astraeus.css.properties.* import nl.astraeus.logging.log typealias Css = Style.() -> Unit @DslMarker annotation class CssTagMarker private fun toProp(vararg css: CssValue): List { val result = mutableListOf() for (c in css) { result.add(CssProperty(c.css())) } return result } abstract class CssGenerator { val definitions: MutableMap = mutableMapOf() val properties: MutableMap> = mutableMapOf() abstract fun getValidator(name: String): List? private fun propertyCss(indent: String, name: String, props: List, minified: Boolean): String { val builder = StringBuilder() getValidator(name)?.forEach { if (!it.validate(props)) { log.warn { "Validate error '$name' - ${it.getMessage(name)}" } } } for (prop in props) { if (builder.isNotEmpty()) { builder.append(",") if (!minified) { builder.append(" ") } } builder.append(prop.css()) } return if (!minified) { val paddedName = StringBuilder() paddedName.append(indent) paddedName.append(name) paddedName.append(":") while (paddedName.length < 32) { paddedName.append(' ') } "$paddedName$builder;\n" } else { "$name:$builder;" } } fun generatePropertyCss(indent: String, minified: Boolean): String { val builder = StringBuilder() for ((name, prop) in properties) { builder.append(propertyCss(indent, name, prop, minified)) } return builder.toString() } open fun generateCss(namespace: String = "", indent: String = " ", minified: Boolean = false): String { val builder = StringBuilder() for ((name, prop) in definitions) { val css = StringBuilder() val finalStyle = Style() prop(finalStyle) css.append(finalStyle.generatePropertyCss(indent, minified)) if (css.isNotBlank()) { builder.append("\n$namespace $name".trim()) if (!minified) { builder.append(" ") } builder.append("{") if (!minified) { builder.append("\n") } finalStyle.fontFace?.let { ff -> if (!minified) { builder.append(indent) } builder.append("@font-face {") if (!minified) { builder.append("\n") } builder.append(ff.generatePropertyCss( " $indent", minified)) builder.append(indent) builder.append("}") if (!minified) { builder.append("\n") } } builder.append(css) builder.append("}") if (!minified) { builder.append("\n\n") } } builder.append(finalStyle.generateCss( "$namespace $name".trim(), indent, minified)) } return builder.toString() } } @CssTagMarker open class Style( /* var animation: TextProperty? = null, var animationDelay: List? = null, var animationDirection: List? = null, var animationDuration: List? = null, var animationFillMode: List? = null, var animationIterationCount: List? = null, var animationFrame: AnimationFrame? = null, var animationName: List? = null, var animationPlayState: List? = null, var animationTimingFunction: List? = null, var backfaceVisibility: BackfaceVisibility? = null, var background: TextProperty? = null, var backgroundAttachment: BackgroundAttachment? = null, var backgroundBlendMode: BackgroundBlendMode? = null, var backgroundClip: ClipOrigin? = null, var backgroundImage: Image? = null, var backgroundOrigin: ClipOrigin? = null, var backgroundPosition: List? = null, var backgroundRepeat: List? = null, var backgroundSize: List? = null, var border: TextProperty? = null, var borderBottom: TextProperty? = null, var borderBottomColor: Color? = null, var borderBottomLeftRadius: List? = null, var borderBottomRightRadius: List? = null, var borderBottomStyle: RuleBorderStyle? = null, var borderBottomWidth: BorderWidth? = null, var borderCollapse: BorderCollapse? = null, var borderColor: List? = null, var borderImage: TextProperty? = null, var borderImageOutset: Length? = null, var borderImageRepeat: List? = null, var borderImageSlice: List? = null, var borderImageSource: List? = null, var borderImageWidth: List? = null, var borderLeft: TextProperty? = null, var borderLeftColor: Color? = null, var borderLeftStyle: RuleBorderStyle? = null, var borderLeftWidth: BorderWidth? = null, var borderRight: TextProperty? = null, var borderRightColor: Color? = null, var borderRightStyle: RuleBorderStyle? = null, var borderRightWidth: BorderWidth? = null, var borderSpacing: List? = null, var borderStyle: List? = null, var borderTop: TextProperty? = null, var borderTopColor: Color? = null, var borderTopLeftRadius: BorderRadius? = null, var borderTopRightRadius: BorderRadius? = null, var borderTopStyle: RuleBorderStyle? = null, var borderTopWidth: BorderWidth? = null, var bottom: Measurement? = null, var boxDecorationBreak: BoxDecorationBreak? = null, var boxShadow: BoxShadow? = null, var boxSizing: BoxSizing? = null, var breakAfter: Break? = null, var breakBefore: Break? = null, var breakInside: Break? = null, var captionSide: CaptionSide? = null, var caretColor: Color? = null, //var charset: TextProperty? = null, var clear: Clear? = null, var clip: Clip? = null, var clipPath: ClipPath? = null, var columnCount: Count? = null, var columnFill: Fill? = null, var columnGap: Measurement? = null, var columnRule: TextProperty? = null, var columnRuleColor: Color? = null, var columnRuleStyle: RuleBorderStyle? = null, var columnRuleWidth: Measurement? = null, var columnSpan: Span? = null, var columnWidth: Measurement? = null, var columns: TextProperty? = null, var content: Content? = null, var counterIncrement: TextProperty? = null, var counterReset: TextProperty? = null, var cursor: TextProperty? = null, var direction: Direction? = null, var display: Display? = null, var emptyCells: EmptyCells? = null, var filter: TextProperty? = null, var flex: TextProperty? = null, var flexBasis: Measurement? = null, var flexDirection: FlexDirection? = null, var flexFlow: TextProperty? = null, var flexGrow: FlexGrowShrink? = null, var flexShrink: FlexGrowShrink? = null, var flexWrap: FlexWrap? = null, var float: Float? = null, var font: TextProperty? = null, var color: Color? = null, var fontSize: FontSize? = null, var height: Measurement? = null, var left: Measurement? = null, var margin: List? = null, var top: Measurement? = null, var transitionDelay: DelayDuration? = null, var transitionDuration: DelayDuration? = null, var width: Measurement? = null */ ) : CssGenerator() { var fontFace: FontFace? = null private val validators = mapOf>( "background-position" to listOf(InitialInheritSingleValue()), "background-size" to listOf(MaxCountValidator(2)), "border-radius" to listOf( MaxCountValidator(4), InitialInheritSingleValue() ), "animation-timing-function" to listOf(MaxCountValidator(4)), "border-image-repeat" to listOf(MaxCountValidator(2)), "border-image-slice" to listOf(MaxCountValidator(4)), "border-spacing" to listOf(MaxCountValidator(4)), "border-style" to listOf(MaxCountValidator(4)) ) override fun getValidator(name: String) = validators[name] /* override fun getMapping(): Map = mapOf( "animation" to animation, "animation-delay" to animationDelay, "animation-direction" to animationDirection, "animation-duration" to animationDuration, "animation-fill-mode" to animationFillMode, "animation-iteration-count" to animationIterationCount, "animation-frame" to animationFrame, "animation-name" to animationName, "animation-play-state" to animationPlayState, "animation-timing-function" to animationTimingFunction, "backface-visibility" to backfaceVisibility, "background" to background, "background-attachment" to backgroundAttachment, "background-blend-mode" to backgroundBlendMode, "background-clip" to backgroundClip, "background-color" to backgroundColor, "background-image" to backgroundImage, "background-origin" to backgroundOrigin, "background-position" to backgroundPosition, "background-repeat" to backgroundRepeat, "background-size" to backgroundSize, "border" to border, "border-bottom" to borderBottom, "border-bottom-color" to borderBottomColor, "border-bottom-left-radius" to borderBottomLeftRadius, "border-bottom-right-radius" to borderBottomRightRadius, "border-bottom-style" to borderBottomStyle, "border-bottom-width" to borderBottomWidth, "border-collapse" to borderCollapse, "border-color" to borderColor, "border-image" to borderImage, "border-image-outset" to borderImageOutset, "border-image-repeat" to borderImageRepeat, "border-image-slice" to borderImageSlice, "border-image-source" to borderImageSource, "border-image-width" to borderImageWidth, "border-left" to borderLeft, "border-left-color" to borderLeftColor, "border-left-style" to borderLeftStyle, "border-left-width" to borderLeftWidth, "border-right" to borderRight, "border-right-color" to borderRightColor, "border-right-style" to borderRightStyle, "border-right-width" to borderRightWidth, "border-spacing" to borderSpacing, "border-style" to borderStyle, "border-top" to borderTop, "border-top-color" to borderTopColor, "border-top-left-radius" to borderTopLeftRadius, "border-top-right-radius" to borderTopRightRadius, "border-top-style" to borderTopStyle, "border-top-width" to borderTopWidth, "bottom" to bottom, "box-decoration-break" to boxDecorationBreak, "box-shadow" to boxShadow, "box-sizing" to boxSizing, "break-after" to breakAfter, "break-before" to breakBefore, "break-inside" to breakInside, "caption-side" to captionSide, "caret-color" to caretColor, //"@charset" to charset, "clear" to clear, "clip" to clip, "clip-path" to clipPath, "color" to color, "column-count" to columnCount, "column-fill" to columnFill, "column-gap" to columnGap, "column-rule" to columnRule, "column-rule-color" to columnRuleColor, "column-rule-style" to columnRuleStyle, "column-rule-width" to columnRuleWidth, "column-span" to columnSpan, "column-width" to columnWidth, "columns" to columns, "content" to content, "counter-increment" to counterIncrement, "counter-reset" to counterReset, "cursor" to cursor, "direction" to direction, "display" to display, "empty-cells" to emptyCells, "filter" to filter, "flex" to flex, "float" to float, "font" to font, "font-family" to fontFamily, "font-size" to fontSize, "height" to height, "left" to left, "margin" to margin, "top" to top, "transition-delay" to transitionDelay, "transition-duration" to transitionDuration, "width" to width ) */ fun select(selector: String, style: Css) { definitions[selector] = style } fun apply(style: Css) { style(this) } fun fontFace(face: FontFace.() -> Unit) { fontFace = FontFace() face.invoke(fontFace!!) } fun alignContent(value: AlignContentValue) { properties["align-content"] = toProp(value) } fun alignItems(alignItems: AlignItems) { properties["align-items"] = toProp(alignItems) } fun all(all: All) { properties["all"] = toProp(all) } fun alignSelf(alignSelf: AlignSelf) { properties["align-self"] = toProp(alignSelf) } fun animation(text: String) { properties["animation"] = listOf(CssProperty(text)) } fun backgroundColor(color: Color) { properties["background-color"] = listOf(color) } fun borderRadius(radius: Measurement) { properties["border-radius"] = listOf(radius) } fun borderRadius( topLeftBottomRight: Measurement, topRightBottomLeft: Measurement ) { properties["border-radius"] = listOf( topLeftBottomRight, topRightBottomLeft ) } fun borderRadius( topLeft: Measurement, topRightBottomLeft: Measurement, bottomRight: Measurement ) { properties["border-radius"] = listOf( topLeft, topRightBottomLeft, bottomRight ) } fun borderRadius( topLeft: Measurement, topRight: Measurement, bottomRight: Measurement, bottomLeft: Measurement ) { properties["border-radius"] = listOf( topLeft, topRight, bottomRight, bottomLeft ) } fun bottom(bottom: Measurement) { properties["bottom"] = listOf(bottom) } fun fontFamily(font: String) { properties["font-family"] = listOf(CssProperty(font)) } fun fontSize(size: Measurement) { properties["font-size"] = listOf(size) } fun height(height: Measurement) { properties["height"] = listOf(height) } fun left(left: Measurement) { properties["left"] = listOf(left) } fun margin(all: Measurement) { properties["margin"] = listOf(all) } fun margin(topBottom: Measurement, leftRight: Measurement) { properties["margin"] = listOf(topBottom, leftRight) } fun margin(top: Measurement, right: Measurement, bottom: Measurement, left: Measurement) { properties["margin"] = listOf(top, right, bottom, left) } fun right(right: Measurement) { properties["right"] = listOf(right) } fun top(top: Measurement) { properties["top"] = listOf(top) } fun width(width: Measurement) { properties["width"] = listOf(width) } }