
Reactã§èšè¿°ãããããŒãžãååŸããŠå°å·ããããšã¯ã§ããŸãããããŒãžã»ãã¬ãŒã¿ãŒãå ¥åãã£ãŒã«ãããããŸãã ããã«ãReactDomãšPDFã«å€æå¯èœãªãã¬ãŒã³HTMLã®äž¡æ¹ãçæããããã«ãã¬ã³ããªã³ã°ã1åäœæããŸãã
æãé£ããã®ã¯ãReactã«ã¯ç¬èªã®dslããããhtmlã«ã¯ç¬èªã®dslãããããšã§ãã ãã®åé¡ã解決ããã«ã¯ïŒ å¥ã®ãã®ãæžããŠãã ããïŒ
ç§ã¯ã»ãšãã©å¿ããŠããŸãããããã¯ãã¹ãŠKotlinã§æžãããŠããã®ã§ãããã¯å®éã«ã¯Kotlin dslã«é¢ããèšäºã§ãã
ãªãç¬èªã®ãŠã«ã¯ãã€ãå¿ èŠãªã®ã§ããïŒ
ç§ã®ãããžã§ã¯ãã«ã¯å€ãã®ã¬ããŒããããããããã¯ãã¹ãŠå°å·å¯èœã§ãªããã°ãªããŸããã ãããè¡ãæ¹æ³ã«ã¯ããã€ãã®ãªãã·ã§ã³ããããŸãã
- å°å·ã¹ã¿ã€ã«ãè©ŠããŠãå¿ èŠã®ãªããã®ããã¹ãŠé ãããã¹ãŠãããŸãããããšãæã¿ãŸãã ãã¿ã³ããã£ã«ã¿ãŒãªã©ã®ã¿ããã®ãŸãŸå°å·ãããŸãã ãŸããããŒãã«ãå€æ°ããå Žåã¯ããããããå¥ã ã®ããŒãžã«ããå¿ èŠããããŸãã ãããŠå人çã«ããµã€ãããå°å·ãããšãã«åºãŠããè¿œå ããããªã³ã¯ãæ¥ä»ãªã©ã¯ç§ãæãããŠããŸã
- PDFãã¬ã³ããªã³ã°ã§ããç¹æ®ãªåå¿ã©ã€ãã©ãªã䜿çšããŠã¿ãŠãã ããã ç§ã¯ãããèŠã€ããŸãããããã¯ããŒã¿çã§ããããã®äžã§ãéåžžã®åå¿ã³ã³ããŒãã³ããåå©çšã§ããªãããã§ãã
- HTMLããã£ã³ãã¹ã«å€æããPDFãäœæããŸãã ãã ãããã®ããã«ã¯ãã¿ã³ãªã©ã®ãªãHTMLãå¿ èŠã§ãã åŸã§å°å·ããã«ã¯ãé衚瀺ã®èŠçŽ ã§ã¬ã³ããªã³ã°ããå¿ èŠããããŸãã ãããããã®ãªãã·ã§ã³ã§ã¯æ¹ããŒãžãå¶åŸ¡ã§ãããšã¯æããŸããã
æçµçã«ãReactDomãšHTMLã®äž¡æ¹ãçæã§ããã³ãŒããæžãããšã«ããŸããã éäžã§æ¹ããŒãžã«é¢ããç¹å¥ãªã¿ã°ãæ¿å ¥ããŠãHTMLãããã¯ãšã³ãã«éä¿¡ããŠPDFãå°å·ããŸãã
Kotlinã«ã¯ãReactãæäœããããã®ã¿ã€ãã»ãŒããªdslãæäŸããReactãæäœããããã®ã¬ã€ã€ãŒã©ã€ãã©ãªããããŸãã äžè¬çã«ã©ã®ããã«èŠãããã¯ã以åã®èšäºã§èŠãããšãã§ããŸãã
JetBrainsã¯ã HTMLãçæããããã®ã©ã€ãã©ãªãäœæããŸãã ã ã¯ãã¹ãã©ãããã©ãŒã ãã€ãŸã JavaãšJSã®äž¡æ¹ã§äœ¿çšã§ããŸãã ãããdslã§ãæ§é ãéåžžã«äŒŒãŠããŸãã
ReactDomãŸãã¯çŽç²ãªHTMLã®ã©ã¡ããå¿ èŠãã«ãã£ãŠãã©ã€ãã©ãªãåãæ¿ããæ¹æ³ãèŠã€ããå¿ èŠããããŸãã
ã©ããªææããããŸããïŒ
ããšãã°ãããããŒã«æ€çŽ¢ããã¯ã¹ãããããŒãã«ãèããŸãã ããã¯ãReactãšHTMLã§ã®ããŒãã«ã¬ã³ããªã³ã°ã®æ§åã§ãã
åå¿ãã
| html
|
---|---|
|
|
ç§ãã¡ã®ã¿ã¹ã¯ã¯ãããŒãã«ã®å·ŠåŽãšå³åŽãçµåããããšã§ãã
ãŸããéããäœã§ããããç解ããŸãããã
- htmlã§ã¯ã
style
ãšcolSpan
ãReactã®attrãã¹ãããããªããžã§ã¯ãã®ãããã¬ãã«ã§å²ãåœãŠãããŸãã - ã¹ã¿ã€ã«ã®å¡ãæ¹ã¯ç°ãªããŸãã HTMLã§ãããæååãšããŠã®éåžžã®cssã§ããå ŽåãReactã§ã¯ãJSã®å¶éã«ãããã£ãŒã«ãåãæšæºã®cssãšãããã«ç°ãªãjsãªããžã§ã¯ãã§ãã
- ReactããŒãžã§ã³ã§ã¯ãæ€çŽ¢ã«å ¥åã䜿çšããHTMLã§ã¯åã«ããã¹ãã衚瀺ããŸãã ããã¯ãã§ã«åé¡ã®ã¹ããŒãã¡ã³ãããå§ãŸã£ãŠããŸãã
ãŸããæãéèŠãªããšïŒãããã¯ç°ãªãæ¶è²»è ãšç°ãªãAPIãæã€ç°ãªãDSLã§ãã ã³ã³ãã€ã©ãŒã®å Žåããããã¯ãŸã£ããç°ãªããŸãã ããããçŽæ¥äº€å·®ãããããšã¯äžå¯èœã§ãããããã»ãŒåãããã«èŠããããReact APIãšHTML APIã®äž¡æ¹ã§åäœã§ããã¬ã€ã€ãŒãèšè¿°ããå¿ èŠããããŸãã
ã¹ã±ã«ãã³ãçµã¿ç«ãŠã
ãšããããã1ã€ã®ç©ºã®ã»ã«ããã¿ãã¬ãããæç»ããŸãã
table { thead { tr { th { } } } }
HTMLããªãŒãšããããåŠçãã2ã€ã®æ¹æ³ããããŸãã åŸæ¥ã®ãœãªã¥ãŒã·ã§ã³ã¯ãè€åãã¿ãŒã³ãšèšªåè ãã¿ãŒã³ãå®è£ ããããšã§ãã 蚪åè çšã®ã€ã³ã¿ãŒãã§ãŒã¹ã¯ãããŸããã ãªã-ããã¯åŸã§èŠãããŸãã
ã¡ã€ã³ãŠãããã¯ParentTagãšTagWithParentã§ãã ParentTagã¯Kotlin apiã®HTMLã¿ã°ã«ãã£ãŠçæããïŒHTMLãšReact apiã®äž¡æ¹ã§äœ¿çšãããŸãïŒãTagWithParentã¯ã¿ã°èªäœãš2ã€ã®APIããªã¢ã³ãã®èŠªã«æ¿å ¥ãã2ã€ã®é¢æ°ãæ ŒçŽããŸãã
abstract class ParentTag<T : HTMLTag> { val tags: MutableList<TagWithParent<*, T>> = mutableListOf() // protected fun RDOMBuilder<T>.withChildren() { ... } // reactAppender protected fun T.withChildren() { ... } // htmlAppender } class TagWithParent<T, P : HTMLTag>( val tag: T, val htmlAppender: (T, P) -> Unit, val reactAppender: (T, RDOMBuilder<P>) -> Unit )
ãªããããªã«å€ãã®ãžã§ããªãã¯ãå¿ èŠãªã®ã§ããïŒ åé¡ã¯ãã³ã³ãã€ã«æã«HTMLã®dslãéåžžã«å³ããããšã§ãã Reactã§divããã§ãã©ãããã§ãtdãåŒã³åºãããšãã§ããå ŽåãHTMLã®å Žåã¯trã®ã³ã³ããã¹ãããã®ã¿åŒã³åºãããšãã§ããŸãã ãããã£ãŠãã©ãã«ã§ããžã§ããªãã¯ã®åœ¢ã§ã³ã³ãã€ã«ããããã«ã³ã³ããã¹ãããã©ãã°ããå¿ èŠããããŸãã
ã»ãšãã©ã®ã¿ã°ã¯ã»ãŒåãæ¹æ³ã§èšè¿°ãããŸãã
- 2ã€ã®èšªåã¡ãœãããå®è£ ããŸãã 1ã€ã¯Reactçšããã1ã€ã¯HTMLçšã§ãã æçµçãªã¬ã³ããªã³ã°ãæ åœããŸãã ãããã®ã¡ãœããã¯ãã¹ã¿ã€ã«ãã¯ã©ã¹ãªã©ãè¿œå ããŸãã
- ã¿ã°ã芪ã«æ¿å ¥ããæ¡åŒµæ©èœãäœæããŸãã
ããã«theadã®äŸããããŸã
class THead : ParentTag<THEAD>() { fun visit(builder: RDOMBuilder<TABLE>) { builder.thead { withChildren() } } fun visit(builder: TABLE) { builder.thead { withChildren() } } } fun Table.thead(block: THead.() -> Unit) { tags += TagWithParent(THead().also(block), THead::visit, THead::visit) }
æåŸã«ã蚪åè çšã®ã€ã³ã¿ãŒãã§ãŒã¹ã䜿çšãããªãã£ãçç±ã説æã§ããŸãã åé¡ã¯ãtrãtheadãštbodyã®äž¡æ¹ã«æ¿å ¥ã§ããããšã§ãã 1ã€ã®ã€ã³ã¿ãŒãã§ã€ã¹ã®ãã¬ãŒã ã¯ãŒã¯å ã§ãããè¡šçŸã§ããŸããã§ããã 蚪åæ©èœã®4ã€ã®ãªãŒããŒããŒããçºçããŸããã
åé¿ã§ããªãéè€ã®æ
class Tr( val classes: String? ) : ParentTag<TR>() { fun visit(builder: RDOMBuilder<THEAD>) { builder.tr(classes) { withChildren() } } fun visit(builder: THEAD) { builder.tr(classes) { withChildren() } } fun visit(builder: RDOMBuilder<TBODY>) { builder.tr(classes) { withChildren() } } fun visit(builder: TBODY) { builder.tr(classes) { withChildren() } } }
èãäœã
ã»ã«ã«ããã¹ããè¿œå ããŸãã
table { thead { tr { th { +": " } } } }
ã+ãã䜿çšãããã©ãŒã«ã¹ã¯éåžžã«ç°¡åã§ããããã¹ããå«ãå¯èœæ§ã®ããã¿ã°ã§unaryPlusãåå®çŸ©ããã ãã§ãã
abstract class TableCell<T : HTMLTag> : ParentTag<T>() { operator fun String.unaryPlus() { ... } }
ããã«ãããtdãŸãã¯thã®ã³ã³ããã¹ã㧠'+'ãåŒã³åºãããšãã§ããããã¹ããå«ãã¿ã°ãããªãŒã«è¿œå ãããŸãã
ç®ããã
ããã§ãHTMLãç°ãªãå Žæãšapiããªã¢ã¯ã·ã§ã³ããå¿ èŠããããŸãã colSpanãšã®å°ããªéãã¯ããèªäœã§è§£æ±ºãããŸãããã¹ã¿ã€ã«ã®åœ¢æã®éãã¯ããè€éã§ãã Reactã§èª°ãç¥ããªãå Žåãã¹ã¿ã€ã«ã¯JSãªããžã§ã¯ãã§ããããã£ãŒã«ãåã«ãã€ãã³ã䜿çšããããšã¯ã§ããŸããã ãã®ããã代ããã«camelCaseã䜿çšãããŸãã HTML APIã§ã¯ãéåžžã®CSSãå¿ èŠã§ãã åã³ãããšãã®äž¡æ¹ãå¿ èŠã§ãã
èªåçã«camelCaseããã€ãããŒã·ã§ã³ã«ããReact APIã®ããã«ãã®ãŸãŸã«ããŠããããšãã§ããŸãããåžžã«æ©èœãããã©ããã¯ããããŸããã ãããã£ãŠãå¥ã®ã¬ã€ã€ãŒãäœæããŸããã
æ zyã§ã¯ãªã人ã¯ããããã©ã®ããã«èŠããããèŠãããšãã§ããŸã
class Style { var border: String? = null var borderColor: String? = null var width: String? = null var padding: String? = null var background: String? = null operator fun invoke(callback: Style.() -> Unit) { callback() } fun toHtmlStyle(): String = properties .map { it.html to it.property(this) } .filter { (_, value) -> value != null } .joinToString("; ") { (name, value) -> "$name: $value" } fun toReactStyle(): String { val result = js("{}") properties .map { it.react to it.property(this) } .filter { (_, value) -> value != null } .forEach { (name, value) -> result[name] = value.toString() } return result.unsafeCast<String>() } class StyleProperty( val html: String, val react: String, val property: Style.() -> Any? ) companion object { val properties = listOf( StyleProperty("border", "border") { border }, StyleProperty("border-color", "borderColor") { borderColor }, StyleProperty("width", "width") { width }, StyleProperty("padding", "padding") { padding }, StyleProperty("background", "background") { background } ) } }
ã¯ãããã1ã€ã®cssããããã£ãå¿ èŠãªå Žåã¯ããã®ã¯ã©ã¹ã«è¿œå ããŸãã ã¯ããã³ã³ããŒã¿ã䜿çšããããããå®è£ ããæ¹ãç°¡åã§ãã ããããã¿ã€ãã»ãŒãã§ãã ç§ãå Žæã§åæã䜿çšããŸãã ãããããèªåã§æžããŠããªãã®ã§ããã°ãã©ãã«ãããŠè³ªåãå¥ã®æ¹æ³ã§è§£æ±ºããã§ãããã
ç§ã¯å°ãã ãŸããŠãçµæã®ã¯ã©ã¹ã®ãã®äœ¿çšãèš±å¯ããŸããïŒ
th { attrs.style { border = "solid" borderColor = "red" } }
æ¹æ³ïŒattr.styleãã£ãŒã«ãã«ã¯ãããã©ã«ãã§ç©ºã®StyleïŒïŒããã§ã«æšªã«ãªã£ãŠããŸãã æŒç®åfun invokeãå®çŸ©ãããšããªããžã§ã¯ããé¢æ°ãšããŠäœ¿çšã§ããŸãã ã¹ã¿ã€ã«ã¯é¢æ°ã§ã¯ãªããã£ãŒã«ãã§ããã
attrs.style()
ãåŒã³åºãããšãã§ããŸãã ãã®ãããªåŒã³åºãã§ã¯ãæŒç®åfun invokeã§æå®ããããã©ã¡ãŒã¿ãŒã転éããå¿ èŠããããŸãã ãã®å Žåãããã¯1ã€ã®ãã©ã¡ãŒã¿ãŒ-ã³ãŒã«ããã¯ïŒã¹ã¿ã€ã«ïŒïŒ->ãŠãããã ããã¯ã©ã ããªã®ã§ãïŒæ¬åŒ§ïŒã¯ãªãã·ã§ã³ã§ãã
å¥ã®é§ãè©Šçãã
Reactã§å ¥åãæç»ããæ¹æ³ãšãHTMLã§ããã¹ããæç»ããæ¹æ³ãåŠã¶ããšã¯æ®ã£ãŠããŸãã ãã®æ§æãååŸãããïŒ
react { search(search, onChangeSearch) } html { +(search?:"") }
ä»çµã¿ïŒåå¿é¢æ°ã¯ãRreact APIã®ã©ã ããåãåããæ¿å ¥ãããã¿ã°ãè¿ããŸãã ã¿ã°ã§ãäžçœ®é¢æ°ãåŒã³åºããã©ã ããHTML APIã«æž¡ãããšãã§ããŸãã äžçœ®ä¿®é£Ÿåã䜿çšãããšãhtmlãããããªãã§åŒã³åºãããšãã§ããŸãã if {} else {}ãšéåžžã«äŒŒãŠããŸãã ãŸããif-elseã®å Žåãšåæ§ã«ãhtmlåŒã³åºãã¯ãªãã·ã§ã³ã§ãããæ°å圹ã«ç«ã¡ãŸããã
å®è£
class ReactTag<T : HTMLTag>( private val block: RBuilder.() -> Unit = {} ) { private var htmlAppender: (T) -> Unit = {} infix fun html(block: (T).() -> Unit) { htmlAppender = block } ... } fun <T : HTMLTag> ParentTag<T>.react(block: RBuilder.() -> Unit): ReactTag<T> { val reactTag = ReactTag<T>(block) tags += TagWithParent<ReactTag<T>, T>(reactTag, ReactTag<T>::visit, ReactTag<T>::visit) return reactTag }
ãµã«ãã³ã®ããŒã¯
å¥ã®ã¿ããã ãã§ã«èšèªã®ã³ã¢ããç¹å¥ãªã¢ãããŒã·ã§ã³@DslMarkerãç«ã£ãŠããç¹å¥ãªå·ã®ã¢ãããŒã·ã§ã³ãæã€ç¹å¥ã«å·ã€ããã€ã³ã¿ãŒãã§ãŒã¹ããParentTagãšTagWithParentãç¶æ¿ããå¿ èŠããããŸãã
@DslMarker annotation class StyledTableMarker @StyledTableMarker interface Tag
ããã¯ãã³ã³ãã€ã©ã次ã®ãããªå¥åŠãªåŒã³åºããèšè¿°ã§ããªãããã«ããããã«å¿ èŠã§ãã
td { td { } } tr { thead { } }
ãããã誰ããã®ãããªããšãæžãããšãèããã ãããã¯äžæã§ã...
æŠãã«ïŒ
èšäºã®æåããè¡šãäœæããæºåã¯ãã¹ãŠæŽã£ãŠããŸããããã®ã³ãŒãã¯ãã§ã«ReactDomãšHTMLã®äž¡æ¹ãçæããŸãã ã©ãã§ãäžåºŠå®è¡ããŠæžããŠãã ããïŒ
fun Table.renderUniversalTable(search: String?, onChangeSearch: (String?) -> Unit) { thead { tr { th { attrs.colSpan = 2 attrs.style { border = "solid" borderColor = "red" } +":" react { search(search, onChangeSearch) //(*) } html { +(search?:"") } } } tr { th { +"" } th { +"" } } } tbody { tr { td { +"" } td { +"" } } tr { td { +"" } td { +"" } } } }
ïŒ*ïŒã«æ³šæããŠãã ãã-ããã§ãæ€çŽ¢æ©èœã¯Reactã®ããŒãã«ã®å ã®ããŒãžã§ã³ãšãŸã£ããåãã§ãã ãã¹ãŠãæ°ããdslã«è»¢éããå¿ èŠã¯ãªããäžè¬çãªã¿ã°ã®ã¿ã転éããŸãã
ãã®ãããªã³ãŒãã®çµæã¯ã©ã®ããã«æ©èœããŸããïŒ ããã«ç§ã®ãããžã§ã¯ãããã®ã¬ããŒãã®PDFå°å·ã®äŸã瀺ããŸãã åœç¶ããã¹ãŠã®æ°åãšååãã©ã³ãã ã«çœ®ãæããŸããã æ¯èŒã®ããã« ãåãããŒãžã®PDFããªã³ãã¢ãŠãã§ããããã©ãŠã¶ã«ãããã®ã§ãã ããŒãžéã§ããŒãã«ãåå²ããŠããããã¹ãããªãŒããŒã¬ã€ããããšã«ããã¢ãŒãã£ãã¡ã¯ãã
dslãäœæãããšã䜿çšåœ¢æ ã®ã¿ãç®çãšããå€ãã®ã³ãŒããè¿œå ãããŸãã ããã«ãå€ãã®Kotlinæ©èœã䜿çšãããŠããŸãããããã¯æ¥åžžç掻ã§ãèããããŸããã
ããããä»ã®å Žåã§ã¯ç°ãªããããããŸãããããã®å Žåãç§ã¯åãé€ãããšãã§ããªãã£ãå€ãã®éè€ããããŸããïŒç§ãç¥ãéããJetBarinsã¯ã³ãŒãçæã䜿çšããŠHTMLã©ã€ãã©ãªãèšè¿°ããŸãïŒã
ããããReact and HTML apiãšå€èŠ³ãã»ãŒåãdslãäœæããããšãããããŸããïŒã»ãšãã©èŠãèŠãŸããã§ããïŒã èå³æ·±ãããšã«ãdslã®äŸ¿å©ããšãšãã«ãã¬ã³ããªã³ã°ãå®å šã«å¶åŸ¡ã§ããŸãã å¥ã®ããŒãžã«ããŒãžã¿ã°ãè¿œå ã§ããŸãã å°å·æã«ã ã¢ã³ãŒãã£ãªã³ ããå±éã§ããŸãã ãããŠããµãŒããŒäžã§ãã®ã³ãŒããåå©çšããæ€çŽ¢ãšã³ãžã³çšã«ãã§ã«HTMLãçæããæ¹æ³ãèŠã€ããããšãã§ããŸãã
PS確ãã«ãPDFãç°¡åã«å°å·ããæ¹æ³ããããŸã
èšäºã®ãœãŒã¹ãšã«ã