外观
Domkit UI 工具包文档(docDomkit)
原文: https://finproducts.atlassian.net/wiki/spaces/JIDD3/pages/34793160794/FIN+5.3.1+docDomkit 中文整理基于 FIN 5.3.1
manual
Domkit UI 工具包文档(docDomkit)
Fantom DomKit HTML5 UI 框架文档
chapters
| Intro | 简介 |
| Dom | 底层 DOM 层文档 |
| Layout | 布局容器文档 |
| Controls | 控件库文档 |
| Modals | 模态框库文档 |
| Drag and Drop | 拖放文档 |
| Theming | 自定义 DomKit CSS |
Controls(控件)
概述
控件是用户在页面上交互的「部件」。Domkit 提供一套标准控件,可用于构建 UI:
- Button
- ButtonGroup
- Checkbox
- Combo
- FilePicker
- Label
- ListButton
- Link
- Menu
- ProgressBar
- RadioButton
- Table
- TextArea
- TextField
- ToggleButton
- Tooltip
- Tree
Button
Button 是按下时触发动作的控件。
Button
{
it.text = "Press me"
it.onAction { echo("Pressed!") }
}
Button
{
it.add(Elem("b") { it.text="Really Press me!" })
it.onAction { echo("Pressed!") }
}![]()
完整 API 见 Button。
另见: ToggleButton, ListButton
ButtonGroup
ButtonGroup 将一组切换按钮或单选按钮编组,并保证组内同时只有一个被选中。
group := ButtonGroup
{
it.add(ToggleButton { ... })
it.add(ToggleButton { ... })
it.add(ToggleButton { ... })
}
group.selIndex = 1 // set group selection
sel := group.selIndex // get current selection完整 API 见 ButtonGroup。
另见: ToggleButton, RadioButton
Checkbox
Checkbox 显示可开关的复选框。
Checkbox {}
Checkbox { it.checked = true }单独使用时只显示复选框本身。通常希望附带文本标签;可用 wrap 方法把点击区域扩展到标签:
Checkbox {}.wrap("You can click here too!")![]()
状态变化时接收回调,请添加 onAction 事件处理:
Checkbox
{
it.onAction |c| { echo("checked: $c.checked") }
}完整 API 见 Checkbox。
Combo
Combo 把 TextField 与 ListButton 合为一体,既可从列表选择也可手工输入。内部 TextField 通过 Combo 暴露;实际使用与 TextField 相同,应在 Combo.field 上注册 onModify、onAction 等回调。
Combo
{
it.items = ["Alpha, "Beta", "Gamma"]
it.field.onAction |f| { echo("value: $f.val") }
}![]()
完整 API 见 Combo。
FilePicker
FilePicker 允许从客户端浏览器选择要上传的文件。
简单表单上传时,FilePicker 底层是 <input type="file">,在 <form> 内只需指定 name 即可:
FilePicker { it->name="upload" }配置可选文件类型,或允许多选:
FilePicker
{
it.accept = "image/*" // allow only images
it.multi = true // allow multiple files to upload
}选中文件时接收回调,请添加 onSelect:
FilePicker
{
it.onSelect |p| { ... }
}已选文件列表可在客户端通过 files 字段查看:
// list of files
files := picker.files
f.name // filename of file
f.size // size of file
f.type // MIME type of file
// async load file contents as a text string client-side
f.readAsText |text| { ... }
// async load file contents and encode as a data:// URI client-side
f.readAsDataUri |uri| { ... }可隐藏真正的 FilePicker DOM,再用 open 编程触发浏览器原生文件选择框:
picker := FilePicker { it.style->display="none" }
button := Button
{
it.text = "Choose Files"
it.onAction { picker.open }
}
parent.add(picker) // make sure FilePicke is actually mounted in DOM
parent.add(button)完整 API 见 FilePicker and DomFile。
Label
Label 仅显示文本,设计上可与 Button 等控件自然垂直对齐:
Label { it.text="My Label" }完整 API 见 Label。
ListButton
ListButton 在按钮按下时弹出列表框,供用户选择一项:
ListButton
{
it.items = ["Alpha", "Beta", "Gamma"]
it.onSelect |b| { echo("Selected $b.sel.item") }
}![]()
默认用 toStr 显示各项。要自定义显示元素,使用 onElem:
ListButton
{
it.items = [1,2,3,4]
it.onElem |v| { "Item #$v" }
}![]()
完整 API 见 ListButton。
Link
Link 创建链接用的 <a> 标签:
Link
{
it.uri = `https://fantom.org`
it.text = "Fantom"
}用 target 指定链接目标:
Link
{
it.uri = `https://fantom.org`
it.text = "Fantom"
it.target = "_blank"
}完整 API 见 Link。
Menu
Menu 显示可选的 MenuItem 菜单。
menu := Menu
{
MenuItem { it.text="Alpha"; it.onAction { ... } },
MenuItem { it.text="Beta"; it.onAction { ... } },
MenuItem { it.text="Gamma"; it.onAction { ... } },
MenuItem { it.text="Delta"; it.onAction { ... } },
}
menu.open(100, 100)![]()
完整 API 见 Menu。
ProgressBar
ProgressBar 可视化长时间操作的进度。
ProgressBar {}
ProgressBar
{
it.val = 25 // set progress value
it.onText |p| { "${p.val}%" } // set bar text
}
ProgressBar
{
it.val = 75
it.onText |p| { "${p.val}%" }
it.onBarColor |p| { "#2ecc71" } // set bar color
}![]()
完整 API 见 ProgressBar。
RadioButton
RadioButton 显示单选按钮:
RadioButton {}
RadioButton { it.checked = true }单独使用时只显示单选圆点。通常附带文本标签;可用 wrap 扩展点击区域:
RadioButton {}.wrap("You can click here too!")![]()
状态变化时接收回调,请添加 onAction 事件处理:
RadioButton
{
it.onAction |c| { echo("checked: $c.checked") }
}需要互斥编组时,见 ButtonGroup。
完整 API 见 RadioButton。
Table
Table 以行列网格显示数据。
@Js class MyTableModel : TableModel
{
override Int numCols() { 100 }
override Int numRows() { 10 }
override Void onCell(Elem cell, Int col, Int row, TableFlags flags)
{
cell.text = "C$col:R$row""
}
}
Table
{
// Note that 'rebuild' is required to display the initial
// table, and to update the table due to any model changes
it.model = MyTableModel()
it.rebuild
}![]()
常用表格操作:
// toggle table header
table.showHeader = false
// sort a column
table.sort(2, Dir.down)
// customize zebra-striping for table rows; use empty list
// to remove background color from all rows
table.stripeClasses = [,]
table.stripeClasses = ["even", "odd"]
// enable multiple selection
table.sel.multi = true
// callback when selection changes
table.onSelect |t| { echo(t.sel.index) }
// callback when row is double-clicked; to access Selection.item
// be sure to override TableModel.item to return backing object
table.onAction |t| { echo(t.sel.item) }
// callback for cell events
table.onTableEvent("mousedown") |e| { echo(e) }
// enable custom header popup located in top-right corner of table
table.onHeaderPopup |t| { return Popup { ... }}完整 API 详见: Table, TableModel, Selection, TableEvent
TextArea
TextArea 支持多行文本输入。
TextArea
{
it.cols = 40
it.rows = 10
it.val = "Some text\n Here\nAnd there"
}![]()
文本修改时用 onModify 接收回调:
TextArea
{
it.onModify |f| { echo(f.val) }
}完整 API 见 TextArea。
TextField
TextField 用于单行文本输入。
TextField {}
TextField { it.val = "Hello, World" }
TextField { it.placeholder = "Search..." }![]()
文本修改时用 onModify 接收回调:
TextField
{
it.onModify |f| { echo(f.val) }
}在 TextField 中按 Enter 时用 onAction 接收回调:
TextField
{
it.onAction |f| { echo(f.val) }
}完整 API 见 TextField。
ToggleButton
ToggleButton 用按钮切换布尔状态:
ToggleButton
{
it.text = "Toggle Me"
it.onAction |b| { echo("state: $b.selected") }
}可根据选中状态用 elemOn / elemOff 切换内容:
ToggleButton
{
it.elemOn = Elem { it.text="On" }
it.elemOff = Elem { it.text="Off" }
it.selected = false // make sure to set default state last
}也可向 elemOn / elemOff 传入任意对象,将用 Obj.toStr 创建 Elem:
ToggleButton
{
it.elemOn = "On"
it.elemOff = "Off"
it.selected = false
}![]()
互斥编组见 ButtonGroup。
完整 API 见 ToggleButton。
Tooltip
Tooltip 在鼠标悬停绑定节点时显示小提示,移出时关闭。
Tooltip
{
it.text = "More info here!"
it.bind(parent)
}完整 API 见 Tooltip。
Tree
Tree 将 TreeNode 显示为可展开节点树。
@Js class MyTreeNode : TreeNode
{
new make(Obj item) { this.item = item }
override TreeNode[] children() { ... }
override Void onElem(Elem elem, TreeFlags flags)
{
elem.text = obj.toStr
}
private Obj obj
}
Tree
{
// Note that 'rebuild' is required to display the initial
// tree, and to update the tree due to any model changes
it.roots = [MyTreeNode(...), MyTreeNode(...), ...]
it.rebuild
}![]()
常用树操作:
// callback when selection changes; item is TreeNode instance
tree.onSelect |t| { echo(t.sel.item) }
// callback when row is double-clicked; item is TreeNode instance
tree.onAction |t| { echo(t.sel.item) }
// callback for node events
tree.onTreeEvent("mousedown") |e| { echo(e) }注意:Selection.index 对 Tree 无效。
惰性加载树节点:
@Js class LazyTreeNode : TreeNode
{
new make(Tree tree, Obj item)
{
this.tree = tree
this.item = item
}
...
override Bool hasChildren()
{
// return true if kids not loaded
kids==null ? true : kids.size > 0
}
override TreeNode[] children()
{
// return kids if already loaded
if (kids != null) return kids
// async load kids
doAsyncLoad(this) |items|
{
this.kids = items.map |i| { LazyTreeNode(tree, i) }
tree.refreshNode(this)
}
// return empty; doAsyncLoad will refresh
return TreeNode#.emptyList
}
private Tree tree
private Obj item
private LazyTreeNode[]? kids
}完整 API 详见: Tree, TreeNode, Selection, TreeEvent
Drag and Drop(拖放)
概述
任意 DOM 节点都可通过 DragTarget 与 DropTarget API 支持拖放。(原文 Dran 为 Drag 笔误)
拖放(拖放)
任意 Elem 可用 DragTarget 变成拖动源:
Box
{
it.text = "Drag me"
DragTarget.bind(it)
{
// get the data payload when a drag event starts
it.onDrag { "Hi :)" }
}
}同样可用 DropTarget 变成放置目标:
Box
{
it.text = "Drop on me"
DropTarget.bind(it)
{
it.onDrop |data| { echo("# drop: $data") }
}
}用 canDrop 判断拖动数据是否可放到该 DropTarget:
DropTarget.bind(elem)
{
it.canDrop |data| { data == "foo" }
it.onDrop |data| { echo("# drop: $data") }
}Dom(底层 DOM)
概述
说到 DomKit,通常也包含底层 dom 程序包(pod)。该 pod 可独立于 DomKit 使用,但为 DomKit 承担了大部分底层工作。
Dom API 大体逐字对齐 W3C DOM 规范,以便复用既有知识与术语,也避免浏览器厂商日后加特性时产生冲突。
本章简要介绍主要的 dom API 特性。拖放、动画等更高级主题见后续章节
Win
Win 对应 W3C Window。多数 API 从当前 Win 实例出发:
// get the current Win instance for this Window
win := Win.curWin 上的部分示例 API:
win.alert("Hello!") // display a modal dialog
win.uri // the URI for this page
win.hyperlink(uri) // hyperlink to new page
win.hisBack // go to previous page in session history
win.viewport // get size of window viewport
// timers
win.setTimeout(10sec) { ... } // invoke func in 10sec
id := win.setInterval(1sec) { ... } // invoke func every 1sec
win.clearInterval(id) // cancel timer with id完整 API 见 Win 完整 Win API.
Doc
Doc 对应 W3C Document。
// get the Doc for the current Win instance
doc := Win.cur.docDoc 上的部分示例 API:
doc.elemById("someId") // return the Elem with id='someId'
doc.createElem("div") // create a new <div> element
doc.querySelector("div.foo") // find first <div> element where class='foo'
doc.querySelectorAll("div.bar") // find all <div> elements where class='bar'完整 API 见 Doc 完整 Doc API.
Elem
Elem 对应 W3C Element。
// create Elems
Elem("div") // create new unattached <div> element
Elem("img") // create new unattached <img> element
// add Elem to parent
p := Elem("p") { it.text="Lorem ipsum" }
parent.add(p)属性(Attributes)
Elem 属性(attribute)访问 API:
elem["alt"] // get an attr value
elem["alt"] = "Alt text" // set an attr value
elem.attr("alt") // get an attr (equivalent to elem["alt"])
elem.setAttr("alt", "...") // set an attr (equivalent to elem["alt"] = "...")
elem.attrs // map of all defined Elem attributes
elem.tagName // tag name of this element
elem.id // 'id' attribute
elem.enabled // 'enabled' attribute
elem.text = "Foo" // set innerText content of this element
elem.html = "<b>Foo</b>" // set innerHTML content of this element属性(Properties)
Elem 的 property API 访问底层 DOM 对象上的 JavaScript 属性:
elem.prop("tabIndex") // get a prop
elem.setProp("tabIndex", true) // set a prop注意:与 attribute 不同,property 名区分大小写。
FFI
trap 运算符可便捷调用 prop / setProp:
elem->tabIndex // get a prop
elem->tabIndex = true // set a prop完整 API 见 Elem.trap for details when using a non-HTML namespace.
invoke 可调用 Elem API 未暴露的 JavaScript 函数:
a.invoke("click")树操作
查询与修改 DOM 树的方法:
elem.parent // parent element
elem.prevSibling // prev sibling
elem.nextSibling // next sibling
elem.children // List of child elements
elem.firstChild // first child, or null if no children
elem.lastChild // last child, or null if no children
elem.containsChild(child) // is child a descendant of parent
elem.add(child) // add a new child element
elem.replace(cur, newChild) // replace a child with new element
elem.remove(child) // remove a child element
elem.querySelector("img") // find first <img> descendant
elem.querySelectorAll("img") // find all <img> descendants完整 API 见 Elem 完整 Elem API.
Style
Style 同时提供内联与 class CSS,通过 style 方法访问:
style := elem.styleCSS 类
操作 CSS class 的方法:
style.classes // return the current class name(s)
style.hasClass("alpha") // does elem have the given class name?
style.addClass("beta") // add a new class to current class list
style.removeClass("gamma") // remove a class, leaving others remaining
style.toggleClass("beta") // add class if missing, remove if exists属性(Properties)
操作 CSS 属性的方法:
style["background-color"] = "#f00" // set style background-color: #f00
style->backgroundColor = "#f00" // set style background-color: #f00
style["color"] // get color property value
style->color // get color property value
style.computed("color") // get the computed color property value
style.setAll(["color":"#f00", "background:"#eee"]) // set list of properties
style.setCss("color: #f00; background: #eee") // set with CSS grammar上例中可用 trap 代替 get/set。带连字符的属性在 trap 中应写成驼峰:
style->backgroundColor == style["background-color"]伪类
addPseudoClass 可在运行时定义 CSS 伪类:
style.addPseudoClass(":hover", "background: #eee")厂商前缀
Style 会自动加厂商前缀,实现代码中不必手写。
完整 API 见 Style 完整 Style API.
Events(事件)
DOM 用户事件用 Event 建模。可用 Win.onEvent 注册回调:
win.onEvent("hashchange", false) |e| { Win.cur.alert("hashchanged!") }Elem 同样可用 Elem.onEvent 注册:
elem.onEvent("mousedown", false) |e| { echo("Pressed $e.target" }移除已注册处理函数时,把函数实例传给 removeEvent:
func := elem.onEvent("mousedown", false) { ... }
elem.removeEvent("mousedown", false, func)完整 API 见 Win, Elem and Event 完整 API details.
HttpReq
HttpReq 用 XmlHttpRequest 在浏览器发起后台 HTTP 请求。同步/异步都会在回调闭包里收到 response:
req := HttpReq { uri=`/foo` }
req.send("POST", "some content") |res|
{
Win.cur.alert(res.content)
}常见请求方法有便捷封装:
HttpReq { uri=`/foo` }.get |res| {...}
HttpReq { uri=`/foo` }.post("some content") |res| {...}
HttpReq { uri=`/foo` }.postForm(["name":"Barney Stinson"]) |res| {...}postForm 会自动编码为普通 HTML 表单提交形式。
设置请求头示例:
req := HttpReq {}
req.uri = `/foo`
req.headers["Content-Type"] = "text/csv; charset=utf-8"
req.post("a,b,c") |res| { ... }完整 API 见 HttpReq and HttpRes 完整 API.
Web Storage(浏览器存储)
Win API 可访问 session 与 local 浏览器 storage:
// local
win.localStorage["bar"] // return value for bar from local storage
win.localStorage["foo"] = 25 // store foo:25 in local storage
win.localStorage.remove("foo") // remove foo from local storage
win.localStorage.clear // clear all contents from local storage
// session
win.sessionStorage["bar"] // return value for bar from session storage
win.sessionStorage["foo"] = 33 // store foo:33 in session storage
win.sessionStorage.remove("foo") // remove foo from session storage
win.sessionStorage.clear // clear all contents from session storage完整 API 见 Storage 完整 API.
MutationObserver
MutationObserver 封装 W3C Mutation 观察 API。先创建实例并提供变更回调:
observer := MutationObserver() |recs|
{
// callback when DOM mutation occurs where
// recs is the list of mutations
}每次变更用 MutationRec 表示。再把观察者挂到 DOM 节点开始监听:
// listen for child add/remove events on elem
observer.observe(elem, ["childList":true])若要停止该观察者的全部事件,调用 disconnect
// detach this observer instance from all mutation events
observer.disconnectIntro(简介)
概述
DomKit 是面向 Web UI 的 HTML5 工具包,整体分两层:
- dom: dom pod provides low-level access to the browser DOM
- domkit:可复用控件与工具库,用于搭建 UI
DomKit 的主要设计目标之一是尽量少在 W3C DOM 上加抽象,好处包括:
- 可随时间直接受益于浏览器新能力
- 减少与第三方 HTML 组件混用时的冲突
- 复用构建 HTML 界面的既有知识与资源
- 保持库更小、更简单
要点:DomKit「控件」就是标准 DOM 元素节点(如 <div>),可放在 DOM 树任意位置;控件子节点也可以是几乎任意合法 HTML。
本手册分 6 大部分:
想快速体验 Domkit 能力与示例,可运行测试套件:
$ fan testDomkit该命令在 8080 端口启动本地 Web 服务器,用浏览器打开:
http://localhost:8080/Layout(布局)
概述
DomKit 控件本质是 HTML + CSS,可放在 DOM 几乎任意位置。面向富 UI,还提供标准布局容器以简化常见界面:
Box
Box 是按特定方式布局子节点的 Elem 基类,默认若干 CSS 以简化组合:
display: block;
box-sizing: border-box;
width: 100%;
height: 100%;
position: relative;GridBox
GridBox 以固定行列的表格方式布局子节点。
示例
GridBox
{
it.cellStyle("*", "*", "padding: 4px")
it.cellStyle("*", "even", "background: #f8f8f8")
it.cellStyle("*", 4, "background: #e8e8e8; border-top: 1px solid #ccc")
it.addRow([Label {
it.text = "Enter User Information"
it.style->fontWeight = "bold"
}], [2])
it.addRow([Label { it.text="First Name:" }, TextField {}])
it.addRow([Label { it.text="Middle Name:" }, TextField {}])
it.addRow([Label { it.text="Last Name:" }, TextField {}])
it.addRow([null, Button { it.text="Submit" }])
}![]()
cellStyle
cellStyle 可为成组单元格设样式。col / row 合法值:
- 具体下标(0、1、2…)
- 下标范围(0..4、7..<8 等)
- "*": apply to all row or columns
- "even":仅偶数行/列
- "odd":仅奇数行/列
cellStyle("*", "*", "padding: 4px")
cellStyle("*", "odd", "padding: 4px")
cellStyle( 0, 1, "padding: 4px")
cellStyle( 3, 2..5, "padding: 4px")addRow
用 addRow 按行添加子节点;某位置传 null 则该格留空:
// leave first cell empty
it.addRow([null, Button { it.text="Submit" }])可选参数可指定子单元格跨多列:
// pass a single child, but specify it should span 2 columns
it.addRow([Label {
it.text = "Enter User Information"
it.style->fontWeight = "bold"
}], [2])colspan 可作用于多个子项,列表值与子项位置对应:
// Add two children, where child1 spans 2 columns, and child2 3 columns
grid.addRow([child1, child2], [2,3])removeRow
可用 removeRow 按下标删除行:
grid.removeRow(2)若该下标没有行,则无操作。
halign
默认各子格用首选尺寸,周围常有空隙。用 GridBox.halign 调整在空隙中的水平位置:
GridBox { it.halign = Align.center }FlowBox
FlowBox 水平并排布局子节点。
FlowBox
{
Button { it.text="Alpha" },
Button { it.text="Beta" },
Button { it.text="Gamma" },
}![]()
子项对齐可用 halign:
FlowBox
{
it.halign = Align.center
Button { it.text="Alpha" },
Button { it.text="Beta" },
Button { it.text="Gamma" },
}![]()
子项间距用 gaps 配置,每项是 index 与 index+1 之间的空隙:
FlowBox
{
it.gaps = ["4px", "16px"]
Button { it.text="Alpha" }, // 4px gap after Alpha
Button { it.text="Beta" }, // 16px gap after Beta
Button { it.text="Gamma" },
}![]()
若子项多于 gaps 长度,则循环使用 gaps:
FlowBox
{
it.gaps = ["4px", "1px"]
Button { ... }, // 4px gap after
Button { ... }, // 1px gap after
Button { ... }, // 4px gap after
Button { ... },
}
FlowBox
{
it.gaps = ["4px"]
... // 4px gap between all children
}SashBox
SashBox 在一个方向上按灵活尺寸分割容器:
SashBox
{
it.sizes = ["25px, "100%", "50px"]
Box { it.text="Child 1" },
Box { it.text="Child 2" },
Box { it.text="Child 3" },
}
+----------+----------------------------------+--------------------+
| | | |
| Child 1 | Child 2 | Child 3 |
| | | |
+----------+----------------------------------+--------------------+可混合固定 px 与 %。先算像素尺寸,剩余按百分比分配;通过 CSS calc() 应用。
默认从左到右;自上而下请设 dir:
SashBox
{
it.dir = Dir.down
it.sizes = ["100px", "100%"]
Box { it.text="Child 1" },
Box { it.text="Child 2" },
}默认分隔固定;可设 resizable 并加入 dividers 以允许拖拽调整:
SashBox
{
it.resizable = true
it.sizes = ["40%", "5px", "60%"]
Box { it.text="Child 1" },
SashBox.div,
Box { it.text="Child 2" },
}resizable 为 true 时,可用 minSize 限制最小尺寸。
CardBox
CardBox 把子元素叠成卡片,同时只显示一张:
CardBox
{
Box { it.style->background="#e74c3c"; it.text="Card #0" },
Box { it.style->background="#2ecc71"; it.text="Card #1" },
Box { it.style->background="#3498db"; it.text="Card #2" },
}用 selIndex 切换可见卡片:
cardBox.selIndex = 2![]()
WellBox
WellBox 以凹陷井样式显示内容:
WellBox { content, }![]()
FlexBox
FlexBox 基于 CSS Flexbox 布局。Flexbox 概念较多,推荐教程:
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
实践中 Domkit 只用 flex 的子集,适合「微布局」。最常见是工具栏:垂直居中,并左/中/右对齐。
下例创建左对齐多按钮的工具栏:
FlexBox
{
it.flex = ["1 1 auto"]
FlowBox {
it.gaps = ["4px"]
Button { ... },
Button { ... },
Button { ... },
},
}再添加一个右对齐按钮:
FlexBox
{
it.flex = ["1 1 auto", "0 0 auto"]
FlowBox { ... },
Button { ... },
}flex 指定父尺寸如何分给各子项,每个子项一条。"0 0 auto" 表示用首选宽度且不随父变化伸缩;"1 1 auto" 表示先按首选尺寸布局再吃掉剩余空间,从而把第二个子项推到右侧。
Modals(模态框)
概述
DomKit 提供两类模态内容:Dialog 与 Popup。
Dialog
Dialog 在页面内容之上打开模态窗口。
dialog := Dialog { it.title = "Hello" }
dialog.add(SashBox
{
it.dir = Dir.down
it.sizes = ["auto", "auto"]
it.style->padding = "12px"
Box
{
it.text= "Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Etiam accumsan, felis vestibulum elementum
efficitur, ligula sem porta magna, sit amet semper
lacus lorem vitae lacus."
},
FlowBox
{
it.style->paddingTop = "12px"
it.halign = Align.right
it.gaps = ["4px"]
Button { it.text="OK"; it.onAction { dialog.close; echo("# OK") }},
Button { it.text="Cancel"; it.onAction { dialog.close; echo("# Cancel") }},
},
})
dialog.open![]()
要改默认尺寸,覆盖 width / height 样式:
Dialog
{
it.style->width = "600px"
it.style->height = "400px"
...
}Dialog 挂载/卸载 DOM 时用 onOpen / onClose 回调:
dialog.onOpen { echo("Opened!") }
dialog.onClose { echo("Closed!") }完整 API 见 Dialog。
Popup
Popup 在页面上打开弹出层;点击外部或按 Esc 关闭:
popup := Popup
{
it.style->width = "400px"
it.style->padding = "12px"
Box
{
it.text= "Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Etiam accumsan, felis vestibulum elementum
efficitur, ligula sem porta magna, sit amet semper
lacus lorem vitae lacus."
},
}
popup.open(100, 100)![]()
完整 API 见 Popup。
Theming(主题)
概述
DomKit 设计为纯 CSS 覆盖样式类来定制。控件主要继承下列「基类」样式:
.domkit-control
.domkit-control-button
.domkit-control-text每种控件还有约定名 domkit-<TypeName> 的专用类,例如 Button 用 domkit-Button。页末有 示例主题 骨架可供参考。
字体
字体继承自父节点或 <body>;设好页面字体通常即可。也可用基类或具体类型类单独定制,例如:
body { font: 14px 'Helvetica Neue', Arial, sans-serif; }.domkit-control-text { font: 14px Monaco, Courier, monospace; }
div.domkit-TextArea { font: 12px Monaco, Courier, monospace; }垂直对齐
控件并排放置时会自动垂直对齐。因与字体无关(见上文「字体」),做法是统一 line-height,并用 padding 补偿边框。
.domkit-control {
padding: 5px 0;
}
.domkit-control-button {
border: 1px solid #ccc;
padding: 4px 10px; // -1px for border to keep valign with .domkit-control
}
.domkit-control-text {
border: 1px solid #ccc;
padding: 4px 10px; // -1px for border to keep valign with .domkit-control
}边框
DomKit 定义了若干标准边框色与样式类:
.domkit-border
.domkit-border-top
.domkit-border-left
.domkit-border-right
.domkit-border-bottom复杂控件如 Table、Tree 会用到。用法示例:
// adding a border
Box { it.style.addClass("domkit-border") }
// adding a bottom-border
Box { it.style.addClass("domkit-border-bottom") }
// removing a left/right borders
Table {
it.style.removeClass("domkit-border")
it.style.addClass("domkit-border-top").addClass("domkit-border-bottom")
}选中态
选中项加 domkit-sel 类。尽量考虑焦点,因此需分别定义有焦点/无焦点选中样式:
.domkit-sel {
background-color: #dcdcdc !important;
}
:focus .domkit-sel, .domkit-sel.pin {
background-color: #3498db !important;
color: #fff !important;
}焦点
控件焦点环可用 :focus 伪类定制:
.domkit-control-button:focus
.domkit-control-text:focusModals(模态框)
基础 Dialog 样式用 .domkit-Dialog-frame 与 .domkit-Dialog-title;标题栏还继承 .domkit-control。
div.domkit-Dialog-frame {
background: #f2f2f2;
border: 1px solid #aaa;
border-radius: 4px 4px 0 0;
box-shadow: 0px 12px 32px rgba(0, 0, 0, 0.4);
}
div.domkit-Dialog-title {
text-align: center;
font-weight: 500;
}Popup 样式:
div.domkit-Popup {
background: rgba(248, 248, 248, 0.95);
border: 1px solid #ddd;
box-shadow: 0px 9px 18px rgba(0, 0, 0, 0.25);
}模态下有遮罩阻止点击,样式为 .domkit-Dialog-mask:
div.domkit-Dialog-mask {
background: rgba(0, 0, 0, 0.25);
}
div.domkit-Popup-mask {
background: none;
}Table
主要 Table 样式:
div.domkit-Table
div.domkit-Table-header
div.domkit-Table-cell
div.domkit-Table-cell.odd
div.domkit-Table-cell.evenTable 较特殊:为大数据集性能,单元格尺寸需写死,故 -header / -cell 不会 继承父级字号(只继承样式)。若加大字号,可能还需配置 rowHeight:
div.domkit-Table-header { font-size: 13px; }
div.domkit-Table-cell { font-size: 14px; }
@Js class MyModel : TableModel
{
override Int rowHeight() { 30 }
}全屏
若要让文档 body 铺满视口(类似桌面应用),加如下样式:
html { height: 100%; }
body { height: 100%; overflow: hidden; }示例主题
自定义样式表起点示例:
.domkit-sel {
background-color: #dcdcdc !important;
}
:focus .domkit-sel, .domkit-sel.pin {
background-color: #8e44ad !important;
color: #fff !important;
}
:focus .domkit-sel a {
color: #fff !important;
}
.domkit-control {
font: 16px 'Helvetica Neue', Arial, sans-serif;
}
.domkit-control-button {
background: #f8f8f8;
border: 1px solid #444;
border-radius: 5px;
}
.domkit-control-button:hover { background: #f0f0f0; }
.domkit-control-button.down { background: #ccc; }
.domkit-control-button.selected,
.domkit-control-button.selected:hover {
color: #fff;
background-color: #8e44ad;
border-color: #6d2f87;
}
.domkit-control-text {
background: #fff;
border: 1px solid #444;
border-radius: 5px;
}
.domkit-control-button:focus, .domkit-control-text:focus {
border-color: #8e44ad;
}
div.domkit-Table-header {
font: bold 13px 'Helvetica Neue', Arial, sans-serif;
}
div.domkit-Table-cell {
font: 14px 'Helvetica Neue', Arial, sans-serif;
}
div.domkit-Table-cell.odd { background: #fff; }
div.domkit-Table-cell.even { background: #f8f8f8; }
div.domkit-Tree {
font: 16px 'Helvetica Neue', Arial, sans-serif;
}
div.domkit-Dialog-frame {
background: #fff;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0px 12px 32px rgba(0, 0, 0, 0.4);
}
div.domkit-Dialog-title {
text-align: center;
font-weight: bold;
background: #f8f8f8;
border-bottom: 1px solid #ccc;
}
div.domkit-Dialog-mask {
background: rgba(0, 0, 0, 0.25);
}
div.domkit-Popup {
background: #fff;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0px 9px 18px rgba(0, 0, 0, 0.25);
}
div.domkit-Popup-mask {
background: none;
}