Files
kotlin-css-generator/src/commonMain/kotlin/nl/astraeus/css/style/Style.kt
2020-03-01 15:22:19 +01:00

414 lines
15 KiB
Kotlin

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<CssProperty> {
val result = mutableListOf<CssProperty>()
for (c in css) {
result.add(CssProperty(c.css()))
}
return result
}
abstract class CssGenerator {
val definitions: MutableMap<String, Css> = mutableMapOf()
val properties: MutableMap<String, List<CssProperty>> = mutableMapOf()
abstract fun getValidator(name: String): List<Validator>?
private fun propertyCss(indent: String, name: String, props: List<CssProperty>, 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<DelayDuration>? = null,
var animationDirection: List<AnimationDirection>? = null,
var animationDuration: List<DelayDuration>? = null,
var animationFillMode: List<AnimationFillMode>? = null,
var animationIterationCount: List<Count>? = null,
var animationFrame: AnimationFrame? = null,
var animationName: List<TextProperty>? = null,
var animationPlayState: List<AnimationPlayState>? = null,
var animationTimingFunction: List<AnimationTimingFunction>? = 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<BackgroundPosition>? = null,
var backgroundRepeat: List<BackgroundRepeat>? = null,
var backgroundSize: List<BackgroundSize>? = null,
var border: TextProperty? = null,
var borderBottom: TextProperty? = null,
var borderBottomColor: Color? = null,
var borderBottomLeftRadius: List<BorderRadius>? = null,
var borderBottomRightRadius: List<BorderRadius>? = null,
var borderBottomStyle: RuleBorderStyle? = null,
var borderBottomWidth: BorderWidth? = null,
var borderCollapse: BorderCollapse? = null,
var borderColor: List<Color>? = null,
var borderImage: TextProperty? = null,
var borderImageOutset: Length? = null,
var borderImageRepeat: List<ImageRepeat>? = null,
var borderImageSlice: List<ImageSlice>? = null,
var borderImageSource: List<ImageSource>? = null,
var borderImageWidth: List<BorderImageWidth>? = 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<BorderSpacing>? = null,
var borderStyle: List<RuleBorderStyle>? = 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<Measurement>? = 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<String, List<Validator>>(
"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<String, Any?> = 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) }
}