
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
|
---|
fun RBuilder.renderReactTable( search: String, onChangeSearch: (String) -> Unit ) { table { thead { tr { th { attrs.colSpan = "2"
| fun TagConsumer<*>.renderHtmlTable( search: String ) { table { thead { tr { th { colSpan = "2"
|
ç§ãã¡ã®ã¿ã¹ã¯ã¯ãããŒãã«ã®å·ŠåŽãšå³åŽãçµåããããšã§ãã
ãŸããéããäœã§ããããçè§£ããŸãããã
- 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()
ãªããããªã«å€ãã®ãžã§ããªãã¯ãå¿
èŠãªã®ã§ããïŒ åé¡ã¯ãã³ã³ãã€ã«æã«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)
ïŒ*ïŒã«æ³šæããŠãã ãã-ããã§ãæ€çŽ¢æ©èœã¯Reactã®ããŒãã«ã®å
ã®ããŒãžã§ã³ãšãŸã£ããåãã§ãã ãã¹ãŠãæ°ããdslã«è»¢éããå¿
èŠã¯ãªããäžè¬çãªã¿ã°ã®ã¿ã転éããŸãã
ãã®ãããªã³ãŒãã®çµæã¯ã©ã®ããã«æ©èœããŸããïŒ ããã«ç§ã®ãããžã§ã¯ããã
ã®ã¬ããŒãã®
PDFå°å·ã®äŸã瀺ããŸãã åœç¶ããã¹ãŠã®æ°åãšååãã©ã³ãã ã«çœ®ãæããŸããã æ¯èŒã®ãã
ã« ãåãããŒãžã®
PDFããªã³ãã¢ãŠãã§ããããã©ãŠã¶ã«ãããã®ã§ãã ããŒãžéã§ããŒãã«ãåå²ããŠããããã¹ãããªãŒããŒã¬ã€ããããšã«ããã¢ãŒãã£ãã¡ã¯ãã
dslãäœæãããšã䜿çšåœ¢æ
ã®ã¿ãç®çãšããå€ãã®ã³ãŒãã远å ãããŸãã ããã«ãå€ãã®Kotlinæ©èœã䜿çšãããŠããŸãããããã¯æ¥åžžç掻ã§ãèããããŸããã
ããããä»ã®å Žåã§ã¯ç°ãªããããããŸãããããã®å Žåãç§ã¯åãé€ãããšãã§ããªãã£ãå€ãã®éè€ããããŸããïŒç§ãç¥ãéããJetBarinsã¯ã³ãŒãçæã䜿çšããŠHTMLã©ã€ãã©ãªãèšè¿°ããŸãïŒã
ããããReact and HTML apiãšå€èгãã»ãŒåãdslãäœæããããšãããããŸããïŒã»ãšãã©èŠãèŠãŸããã§ããïŒã è峿·±ãããšã«ãdslã®äŸ¿å©ããšãšãã«ãã¬ã³ããªã³ã°ãå®å
šã«å¶åŸ¡ã§ããŸãã å¥ã®ããŒãžã«ããŒãžã¿ã°ã远å ã§ããŸãã å°å·æã«ã
ã¢ã³ãŒãã£ãªã³ ããå±éã§ããŸãã ãããŠããµãŒããŒäžã§ãã®ã³ãŒããåå©çšããæ€çŽ¢ãšã³ãžã³çšã«ãã§ã«HTMLãçæããæ¹æ³ãèŠã€ããããšãã§ããŸãã
PS確ãã«ãPDFãç°¡åã«å°å·ããæ¹æ³ããããŸã
èšäºã®ãœãŒã¹ãšã«ãKotlinã«é¢ããä»ã®èšäºïŒ