Minimal styling, powered by Goober.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
2.9 KiB

  1. import { css as gooberCss } from 'goober';
  2. import { PropertiesHyphenFallback } from 'csstype';
  3. interface CssString {
  4. // TODO stricter type checking
  5. toString(): string
  6. }
  7. export class CssStringImpl implements CssString {
  8. private css: string
  9. constructor(s: TemplateStringsArray) {
  10. this.css = s.raw[0]
  11. .trim()
  12. .replace(/[ ][ ]+/g, ' ')
  13. .replace(/:[ ]/g, ':')
  14. .replace(/\n/g, '')
  15. .replace(/;[ ]/g, ';');
  16. }
  17. toString() {
  18. return this.css
  19. }
  20. }
  21. interface CssIf {
  22. (b: boolean): (...a: CssString[]) => CssIfString
  23. }
  24. interface CssElse {
  25. (...c: CssString[]): CssString
  26. if: CssIf
  27. }
  28. interface CssIfString extends CssString {
  29. else: CssElse
  30. }
  31. const cssIf: CssIf = (b: boolean) => (...a: CssString[]) => new CssIfStringImpl(b, ...a);
  32. export class CssIfStringImpl implements CssIfString {
  33. readonly else: CssElse
  34. private readonly cssStrings: CssString[]
  35. constructor(private readonly condition: boolean, ...cssStrings: CssString[]) {
  36. this.cssStrings = cssStrings
  37. const elseFn = (...c: CssString[]) => {
  38. if (this.condition) {
  39. return {
  40. toString: () => {
  41. return this.cssStrings.map((c2) => c2.toString()).join('');
  42. }
  43. }
  44. }
  45. return {
  46. toString: () => {
  47. return c.map((cc) => cc.toString()).join('')
  48. }
  49. };
  50. }
  51. elseFn.if = cssIf
  52. this.else = elseFn
  53. }
  54. toString() {
  55. if (this.condition) {
  56. return this.cssStrings.map((c2) => c2.toString()).join('');
  57. }
  58. return '';
  59. }
  60. }
  61. interface CssNest {
  62. (selector: string): (...a: CssString[]) => CssString
  63. }
  64. const cssNest: CssNest = (selector) => (...a) => {
  65. return {
  66. toString: () => `${selector}{${a.map(aa => aa.toString()).join('')}}`
  67. }
  68. }
  69. interface CssDynamic {
  70. (a: PropertiesHyphenFallback): CssString
  71. }
  72. const cssDynamic: CssDynamic = (a: PropertiesHyphenFallback) => {
  73. return {
  74. toString(): string {
  75. return Object
  76. .entries(a)
  77. .map(([key, value]) => `${key}:${value.toString()};`)
  78. .join('')
  79. }
  80. };
  81. };
  82. interface CssMedia {
  83. (raw: string): any
  84. }
  85. const cssMedia: CssMedia = (arg1: string) => {
  86. return (...body: CssString[]) => {
  87. return {
  88. toString(): string {
  89. return `@media ${arg1}{${body.map(b => b.toString()).join('')}}`
  90. }
  91. }
  92. }
  93. }
  94. const cssCompile = (...strings: CssString[]) => {
  95. return strings
  96. .filter((s) => ['string', 'object'].includes(typeof s))
  97. .map((s) => {
  98. if (typeof s === 'object') {
  99. return gooberCss`${s.toString()}`
  100. }
  101. return s
  102. })
  103. .join(' ')
  104. }
  105. interface Css {
  106. (s: TemplateStringsArray): CssString
  107. if: CssIf
  108. nest: CssNest
  109. dynamic: CssDynamic
  110. media: CssMedia
  111. cx(...strings: CssString[]): string
  112. }
  113. const _css: Partial<Css> = (s: TemplateStringsArray) => new CssStringImpl(s);
  114. _css.if = cssIf;
  115. _css.nest = cssNest;
  116. _css.dynamic = cssDynamic;
  117. _css.media = cssMedia;
  118. _css.cx = cssCompile;
  119. export const css = _css as Css;