Skip to content

Commit

Permalink
Merge pull request #246 from nafg/cover-more-of-mui
Browse files Browse the repository at this point in the history
Cover more of MUI
nafg authored Dec 14, 2023
2 parents dd2220b + 0399240 commit 358b1c0
Showing 5 changed files with 230 additions and 91 deletions.
154 changes: 117 additions & 37 deletions overrides.yml
Original file line number Diff line number Diff line change
@@ -28,24 +28,81 @@ mui:
- ReactMouseEventFromHtml
result: callback
components:
.system:
props:
alignItems:
type:
base: string
presets:
- string: flex-start
- string: flex-end
- string: center
- string: stretch
- string: baseline
color:
type:
base: string
presets:
- string: primary.main
- string: secondary.main
- string: error.main
- string: warning.main
- string: info.main
- string: success.main
- string: text.primary
- string: text.secondary
- string: text.disabled
display:
type:
base: string
presets:
- string: block
- string: inline
justifyContent:
type:
base: string
presets:
- string: flex-start
- string: flex-end
- string: center
- string: space-between
- string: space-around
- string: space-evenly
Alert:
props:
icon:
type:
base:
anyOf:
- bool
- vdomNode
presets: [ false ]
Autocomplete:
props:
filterOptions:
type:
args:
- arrayOf: jsAny
- jsObject
result:
arrayOf: jsAny
getOptionLabel:
type:
args:
- jsAny
result: string
onChange:
isOptionEqualToValue:
type:
args:
- ReactEvent
- jsAny
result: callback
renderOption:
- jsAny
result: bool
onChange:
type:
args:
- ReactEvent
- jsAny
result: vdomNode
result: callback
onInputChange:
type:
args:
@@ -58,17 +115,25 @@ mui:
args:
- jsDictionary
result: vdomNode
filterOptions:
renderOption:
type:
args:
- arrayOf: jsAny
- jsObject
result:
arrayOf: jsAny
args: [ jsObject, jsAny, jsObject, jsObject ]
result: vdomNode
Breadcrumbs:
moduleTrait: FacadeModule.ArrayChildren
ButtonGroup:
moduleTrait: FacadeModule.ArrayChildren
Card:
extends: Paper
props:
raised:
type: bool
Chip:
props:
onDelete:
type:
args: [ ReactMouseEvent ]
result: callback
Container:
props:
children:
@@ -88,18 +153,12 @@ mui:
- string
- bool
presets:
- name: xs
code: '"xs"'
- name: sm
code: '"sm"'
- name: md
code: '"md"'
- name: lg
code: '"lg"'
- name: xl
code: '"xl"'
- name: "false"
code: 'false'
- string: xs
- string: sm
- string: md
- string: lg
- string: xl
- false
Dialog:
props:
onClose:
@@ -114,14 +173,17 @@ mui:
type:
base: string
presets:
- name: inherit
code: '"inherit"'
- name: large
code: '"large"'
- name: medium
code: '"medium"'
- name: small
code: '"small"'
- string: inherit
- string: large
- string: medium
- string: small
Fade:
props:
children:
required: true
type: vdomElement
Grid:
extends: .system
IconButton:
props:
children:
@@ -133,6 +195,8 @@ mui:
args:
- ReactEventFromInput
result: callback
Link:
extends: Typography
List:
moduleTrait: FacadeModule.ArrayChildren
ListItem:
@@ -178,18 +242,24 @@ mui:
anyOf:
- element
- jsObject
RadioGroup:
extends: FormGroup
Select:
props:
renderValue:
type:
args:
- jsAny
result: vdomNode
TableCell:
props:
padding:
type:
base: string
presets:
- name: normal
code: '"normal"'
- name: checkbox
code: '"checkbox"'
- name: none
code: '"none"'
- string: normal
- string: checkbox
- string: none
TablePagination:
props:
page:
@@ -204,6 +274,14 @@ mui:
- ReactEvent
- int
result: callback
Tabs:
props:
onChange:
type:
args:
- ReactEvent
- jsAny
result: callback
TextField:
props:
onChange:
@@ -227,6 +305,8 @@ mui:
children:
type: vdomElement
required: true
Typography:
extends: .system

mui.lab:
common:
69 changes: 50 additions & 19 deletions project/FacadeGenerator.scala
Original file line number Diff line number Diff line change
@@ -151,8 +151,8 @@ object FacadeGenerator {
s"""object ${propInfo.identifier} extends PropTypes.Prop[$typeCode]("${propInfo.identifier}") {
|${
propInfo.`type`.presets
.map { case PropTypeInfo.Preset(name, presetCode) =>
s" val $name = this := $presetCode.asInstanceOf[$typeCode]"
.map { value =>
s" val ${value.name} = this := ${value.code}.asInstanceOf[$typeCode]"
}
.mkString("\n")
}
@@ -219,24 +219,53 @@ object FacadeGenerator {

val readComponentInfos = ujson.read(docgenOutput).obj.values.toSeq.collect {
case value if Set("props", "displayName").forall(value.obj.contains) =>
val componentInfo = ComponentInfo.read(value.obj)
overrides.getPropInfoOverrides(componentInfo).foldLeft(componentInfo) {
case (componentInfo, (propName, Overrides.PropInfoOverride(typ, required))) =>
componentInfo.propsMap.get(propName)
.map { existingPropInfo =>
existingPropInfo.copy(
required = required.getOrElse(existingPropInfo.required),
`type` = typ.getOrElse(existingPropInfo.`type`)
)
}
.orElse(typ.map(propTypeInfo => PropInfo(propName, propTypeInfo)))
.fold(componentInfo)(componentInfo.withProp)
}
ComponentInfo.read(value.obj)
}

val componentInfos =
readComponentInfos ++
(overrides.components -- readComponentInfos.map(_.name).toSet).map { case (name, overrides) =>
def applyExtends(componentInfo: ComponentInfo): ComponentInfo = {
val name = componentInfo.name
overrides.get(name).`extends` match {
case None =>
logger.info("No extends for " + name)
componentInfo
case Some(parentName) =>
readComponentInfos.find(_.name == parentName)
.fold(componentInfo) { parent =>
logger.info("Extending " + name + " with " + parentName)
val parentExtended = applyExtends(parent)
val map = parentExtended.propsMap ++ componentInfo.propsMap
logger.info("Added props " + (map.keySet -- parentExtended.propsMap.keySet).mkString(", ") + " to " +
name)
componentInfo.copy(propsMap = map)
}
}
}

val inheritedExisting = readComponentInfos.map { componentInfo =>
logger.info(componentInfo.name)
applyExtends(componentInfo)
}

val overriddenExisting = inheritedExisting.map { componentInfo =>
overrides.getPropInfoOverrides(componentInfo).foldLeft(componentInfo) {
case (componentInfo, (propName, Overrides.PropInfoOverride(typ, required))) =>
componentInfo.propsMap.get(propName)
.map { existingPropInfo =>
existingPropInfo.copy(
required = required.getOrElse(existingPropInfo.required),
`type` = typ.getOrElse(existingPropInfo.`type`)
)
}
.orElse(typ.map(propTypeInfo => PropInfo(propName, propTypeInfo)))
.fold(componentInfo)(componentInfo.withProp)
}
}

val readComponentNames = overriddenExisting.map(_.name).toSet

val addedComponentInfos =
(overrides.components -- readComponentNames).collect {
case (name, overrides) if name.headOption.exists(_.isLetter) =>
ComponentInfo(
name = name,
description = "",
@@ -249,7 +278,9 @@ object FacadeGenerator {
)
}
)
}
}

val componentInfos = overriddenExisting ++ addedComponentInfos

for (componentInfo <- componentInfos) yield processComponent(
info = componentInfo,
14 changes: 2 additions & 12 deletions project/FacadeGeneratorPlugin.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import java.io.FileReader

import _root_.io.circe.yaml.v12.Parser
import cats.data.Validated
import cats.implicits.toShow
@@ -22,19 +20,12 @@ object FacadeGeneratorPlugin extends AutoPlugin {
runYarnInstall.value
val logger = streams.value.log
val overrides =
Parser.default.parse(new FileReader("overrides.yml")).toTry.get
.asAccumulating[Map[String, Overrides]] match {
Parser.default.decodeAccumulating[Map[String, Overrides]](IO.read(file("overrides.yml"))) match {
case Validated.Invalid(errors) =>
errors.toList.foreach(failure => logger.error(failure.show))
sys.error(errors.toString)
case Validated.Valid(map) => map(scalaSubPackage)
case Validated.Valid(map) => map(scalaSubPackage).applyExtends
}
val overridesString = pprint.apply(overrides).render
logger.info(
overridesString.linesWithSeparators
.map(s"[${scalaSubPackage}] " + _)
.mkString
)
FacadeGenerator.run(
base = os.Path((Compile / sourceManaged).value),
repoDir = reactDocGenDir.value,
@@ -76,7 +67,6 @@ object FacadeGeneratorPlugin extends AutoPlugin {
.call(cwd = dir, stderr = os.Inherit, stdout = os.Inherit)
}
},
watchSources += file("overrides.yml"),
Compile / packageSrc / mappings ++= {
val base = (Compile / sourceManaged).value
val files = (Compile / managedSources).value
42 changes: 36 additions & 6 deletions project/Overrides.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,52 @@
import scala.collection.immutable.SortedMap

import Overrides.PropInfoOverride
import io.circe.Codec
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.semiauto.deriveConfiguredCodec


case class Overrides(
common: Map[String, PropInfoOverride] = Map.empty,
components: Map[String, Overrides.ComponentOverrides] = Map.empty,
moduleTraits: Map[String, String] = Map.empty) {
common: SortedMap[String, PropInfoOverride] = SortedMap.empty,
components: SortedMap[String, Overrides.ComponentOverrides] = SortedMap.empty) {

def get(name: String): Overrides.ComponentOverrides = components.getOrElse(name, Overrides.ComponentOverrides.empty)

def getPropInfoOverrides(componentInfo: ComponentInfo): Map[String, PropInfoOverride] =
common ++
components.getOrElse(componentInfo.name, Overrides.ComponentOverrides.NoOp).props
get(componentInfo.name).props

def applyExtends: Overrides =
copy(components =
components.map { case (name, componentOverrides) =>
name -> componentOverrides.applyExtends(this)
}
)
}
object Overrides {
case class ComponentOverrides(props: Map[String, PropInfoOverride] = Map.empty, moduleTrait: Option[String] = None)
case class ComponentOverrides(
props: SortedMap[String, PropInfoOverride] = SortedMap.empty,
`extends`: Option[String] = None,
moduleTrait: Option[String] = None) {

def ++(that: ComponentOverrides) =
copy(
props = this.props ++ that.props,
`extends` = this.`extends`.orElse(that.`extends`),
moduleTrait = this.moduleTrait.orElse(that.moduleTrait)
)

def applyExtends(overrides: Overrides): ComponentOverrides =
`extends` match {
case None => this
case Some(parentName) =>
val parent = overrides.get(parentName)
val parentExtended = parent.applyExtends(overrides)
parentExtended ++ this
}
}
object ComponentOverrides {
val NoOp = ComponentOverrides()
val empty = ComponentOverrides()
}

case class PropInfoOverride(`type`: Option[PropTypeInfo] = None, required: Option[Boolean] = None)
42 changes: 25 additions & 17 deletions project/PropTypeInfo.scala
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ sealed trait PropTypeInfo {

def sequence: PropTypeInfo = PropTypeInfo.Simple.Sequence(this)

def |(that: PropTypeInfo) = PropTypeInfo.Union(Seq(this, that)).flatten
def |(that: PropTypeInfo) = PropTypeInfo.Union(Seq(this, that)).simplify
}
object PropTypeInfo {
private implicit val circeConfig =
@@ -102,11 +102,11 @@ object PropTypeInfo {
override def presets = Nil
}
case class Union(anyOf: Seq[PropTypeInfo]) extends PropTypeInfo {
override def code = anyOf.map(_.safeCode).mkString(" | ")
override def code = anyOf.map(_.safeCode).distinct.mkString(" | ")
override def safeCode = s"($code)"
override def imports = anyOf.flatMap(_.imports).toSet ++ CommonImports.|
override def presets = anyOf.flatMap(_.presets)
def flatten =
def simplify =
copy(anyOf =
anyOf
.flatMap {
@@ -122,10 +122,20 @@ object PropTypeInfo {
override def imports = base.imports
}

case class Preset(name: Identifier, code: String)
sealed abstract class Preset(val name: Identifier, val code: String)
object Preset {
def literal(value: String) = Preset(Identifier(value), value)
def string(value: String) = Preset(Identifier(value), '"' + value + '"')
case class Unquoted(value: String) extends Preset(Identifier(value), value)
case class Quoted(string: String) extends Preset(Identifier(string), '"' + string + '"')

implicit val codecQuoted: Codec[Quoted] = deriveConfiguredCodec
implicit val encodePreset: Encoder[Preset] = Encoder.instance[Preset] {
case u: Unquoted => u.value.asJson
case q: Quoted => q.asJson
}
implicit val decodePreset: Decoder[Preset] =
codecQuoted
.or(Decoder.decodeString.map[Preset](Unquoted))
.or(Decoder.decodeBoolean.map(b => Unquoted(b.toString)))
}

val int: PropTypeInfo = Simple.int
@@ -141,7 +151,6 @@ object PropTypeInfo {
val vdomNode: PropTypeInfo = Simple.vdomNode
val vdomElement: PropTypeInfo = Simple.vdomElement
val element: PropTypeInfo = Simple.element
def stringEnum(values: String*): PropTypeInfo = WithPresets(Simple.string, values.map(Preset.string))

private val stringEnumValueRE = "'(.*)'".r
private val litEnumValueRE = """(true|false|-?\d+\.\d+|-?\d+)""".r
@@ -151,27 +160,26 @@ object PropTypeInfo {
case simple: PropType.Simple => Simple.fromPropType(simple)
case PropType.Func => jsAny =>: jsAny
case PropType.ArrayOf(param) => apply(param).sequence
case PropType.Union(types) => Union(types.map(apply))
case PropType.Union(types) => Union(types.map(apply)).simplify
case PropType.Enum(base, values) =>
WithPresets(Simple.fromPropType(base),
WithPresets(apply(base),
values.collect {
case litEnumValueRE(s) => Preset.literal(s)
case stringEnumValueRE(s) => Preset.string(s)
case litEnumValueRE(s) => Preset.Unquoted(s)
case stringEnumValueRE(s) => Preset.Quoted(s)
}
)
}

private implicit val presetCodec: Codec[PropTypeInfo.Preset] = deriveConfiguredCodec
private implicit val enumCodec: Codec[PropTypeInfo.WithPresets] = deriveConfiguredCodec
private implicit val unionCodec: Codec[PropTypeInfo.Union] = deriveConfiguredCodec
private implicit val functionCodec: Codec[PropTypeInfo.Function] = deriveConfiguredCodec
implicit val encodePropTypeInfo: Encoder[PropTypeInfo] = Encoder.instance[PropTypeInfo] {
implicit val enumCodec: Codec[PropTypeInfo.WithPresets] = deriveConfiguredCodec
implicit val unionCodec: Codec[PropTypeInfo.Union] = deriveConfiguredCodec
implicit val functionCodec: Codec[PropTypeInfo.Function] = deriveConfiguredCodec
implicit val encodePropTypeInfo: Encoder[PropTypeInfo] = Encoder.instance[PropTypeInfo] {
case s: Simple => Simple.encodeSimple(s)
case e: WithPresets => enumCodec(e)
case u: Union => unionCodec(u)
case f: Function => functionCodec(f)
}
implicit val decodePropTypeInfo: Decoder[PropTypeInfo] =
implicit val decodePropTypeInfo: Decoder[PropTypeInfo] =
unionCodec
.or(enumCodec.map(identity[PropTypeInfo]))
.or(functionCodec.map(identity[PropTypeInfo]))

0 comments on commit 358b1c0

Please sign in to comment.