refactor: make Grid placement a direct Box role
This commit is contained in:
parent
4b409f16f4
commit
6d9f2a4d82
@ -136,9 +136,7 @@ of full browser CSS compatibility.
|
||||
:grid-template-columns '((200) 1fr 1fr)
|
||||
:grid-template-rows '(1 1)
|
||||
:gap '(1 (12))
|
||||
(ebox-grid-item
|
||||
(ebox-create :content "Header")
|
||||
:grid-column '(1 :span 3))
|
||||
(ebox-create :content "Header" :grid-column '(1 :span 3))
|
||||
(ebox-create :content "Main" :grid-column 1 :grid-row 2)
|
||||
(ebox-create :content "Aside" :grid-column 2 :grid-row 2))
|
||||
```
|
||||
@ -152,8 +150,8 @@ positions use implicit tracks. `:grid-auto-columns`, `:grid-auto-rows`, and
|
||||
Grid item placement is one-based. `:grid-column` and `:grid-row` accept a
|
||||
positive start integer, `(start :span positive-integer)`, or `(start end)` with
|
||||
`end` greater than `start`. `:grid-column-span` and `:grid-row-span` accept
|
||||
positive integers. `ebox-grid-item` is a convenience wrapper; placement
|
||||
properties may also be placed directly on a child node.
|
||||
positive integers. Placement properties belong directly to the child Box;
|
||||
Grid item is a parent-child role, not a wrapper or constructor.
|
||||
|
||||
## 4. Units, box properties, and text properties
|
||||
|
||||
@ -534,15 +532,14 @@ boolean to nil when a host application must avoid background preparation.
|
||||
## 10. Standalone `.ebox` DSL
|
||||
|
||||
`ebox-build` compiles a data-oriented list form. Supported tags are `box` and
|
||||
`ebox`, `row`, `column`, `flex`, `item`, `grid`, `grid-item`, and `spacer`:
|
||||
`ebox`, `row`, `column`, `flex`, `item`, `grid`, and `spacer`:
|
||||
|
||||
```elisp
|
||||
(ebox-build
|
||||
'(grid :width (640)
|
||||
:grid-template-columns ((200) 1fr 1fr)
|
||||
:gap (1 (12))
|
||||
(grid-item :grid-column (1 :span 3)
|
||||
(box :content "Header"))
|
||||
(box :grid-column (1 :span 3) :content "Header")
|
||||
(box :content "Main")))
|
||||
```
|
||||
|
||||
@ -550,7 +547,7 @@ Strings directly under `box` become newline-joined content. Child forms become
|
||||
a default vertical child layout. `row` and `column` can receive box
|
||||
properties around their child layout. `flex` accepts container properties;
|
||||
`item` accepts flex item properties and exactly one or more child forms;
|
||||
`grid-item` requires exactly one child; `spacer` accepts no children.
|
||||
`spacer` accepts no children.
|
||||
|
||||
Inside a `.ebox` file, keep structural forms unquoted. Quote list and symbol
|
||||
constants in property positions when they are data, such as
|
||||
@ -591,7 +588,7 @@ style-rule functions immediately below are public module-level style APIs.
|
||||
| `ebox-create`, `ebox-build` | Create a legacy node or compile the `.ebox` list DSL during migration. |
|
||||
| `ebox-concat`, `ebox-stack`, `ebox-row`, `ebox-column`, `ebox-spacer` | Compose simple horizontal/vertical layouts and blank boxes. |
|
||||
| `ebox-flex`, `ebox-flex-item` | Build flex containers and item metadata. |
|
||||
| `ebox-grid`, `ebox-grid-fr`, `ebox-grid-item` | Build Grid layouts, fractional tracks, and placement metadata. |
|
||||
| `ebox-grid`, `ebox-grid-fr` | Build Grid layouts and fractional tracks; placement metadata belongs to direct child Boxes. |
|
||||
| `ebox-render`, `ebox-render-to-buffer`, `ebox-display-buffer` | Pure materialization, retained mount, and display wrapper. |
|
||||
| `ebox-commit`, `ebox-buffer-update-report`, `ebox-rerender-buffer-with-context`, `ebox-viewport-window-width` | Atomic root commit, report lookup, viewport-context rerender, and the shared display-safe width sampler. |
|
||||
| `ebox-region-ids`, `ebox-region-resolve`, `ebox-region-update` | Region mapping, logical handle lookup, and direct update. |
|
||||
|
||||
@ -124,9 +124,7 @@ Flex preferred/min/max size 支持 Ebox 子集:`auto`、`min-content`、
|
||||
:grid-template-columns '((200) 1fr 1fr)
|
||||
:grid-template-rows '(1 1)
|
||||
:gap '(1 (12))
|
||||
(ebox-grid-item
|
||||
(ebox-create :content "Header")
|
||||
:grid-column '(1 :span 3))
|
||||
(ebox-create :content "Header" :grid-column '(1 :span 3))
|
||||
(ebox-create :content "Main" :grid-column 1 :grid-row 2)
|
||||
(ebox-create :content "Aside" :grid-column 2 :grid-row 2))
|
||||
```
|
||||
@ -138,8 +136,8 @@ Flex preferred/min/max size 支持 Ebox 子集:`auto`、`min-content`、
|
||||
|
||||
Grid placement 从 1 开始。`:grid-column` 和 `:grid-row` 接受正整数起点、
|
||||
`(start :span 正整数)`,或 `start` 小于 `end` 的 `(start end)`。
|
||||
`:grid-column-span` 与 `:grid-row-span` 接受正整数。`ebox-grid-item` 是便捷
|
||||
包装器;placement property 也可以直接放在 child node 上。
|
||||
`:grid-column-span` 与 `:grid-row-span` 接受正整数。placement property 直接属于
|
||||
child Box;Grid item 是父子参与角色,不是 wrapper 或 constructor。
|
||||
|
||||
## 4. 单位、box property 与文本属性
|
||||
|
||||
@ -484,22 +482,20 @@ Idle prewarm 不会发布 buffer text 或 runtime state。宿主应用不希望
|
||||
## 10. 独立 `.ebox` DSL
|
||||
|
||||
`ebox-build` 编译数据型 list form。支持的 tag 是 `box`/`ebox`、`row`、
|
||||
`column`、`flex`、`item`、`grid`、`grid-item` 和 `spacer`:
|
||||
`column`、`flex`、`item`、`grid` 和 `spacer`:
|
||||
|
||||
```elisp
|
||||
(ebox-build
|
||||
'(grid :width (640)
|
||||
:grid-template-columns ((200) 1fr 1fr)
|
||||
:gap (1 (12))
|
||||
(grid-item :grid-column (1 :span 3)
|
||||
(box :content "Header"))
|
||||
(box :grid-column (1 :span 3) :content "Header")
|
||||
(box :content "Main")))
|
||||
```
|
||||
|
||||
`box` 下的直接字符串会合并为换行内容;child form 会变成默认纵向 child
|
||||
layout。`row` 与 `column` 可以接收 box property 并包住 child layout;`flex`
|
||||
接收容器 property;`item` 接收 flex item property;`grid-item` 必须有一个
|
||||
child;`spacer` 不能有 child。
|
||||
接收容器 property;`item` 接收 flex item property;`spacer` 不能有 child。
|
||||
|
||||
在 `.ebox` 文件中,结构 form 保持不加 quote;property 位置的 list 和 symbol
|
||||
常量要作为 data quote,例如 `:gap '(1 (12))` 或 `:justify-content 'center`;
|
||||
@ -537,7 +533,7 @@ style API。
|
||||
| `ebox-create`、`ebox-build` | 迁移期间创建旧节点或编译 `.ebox` list DSL。 |
|
||||
| `ebox-concat`、`ebox-stack`、`ebox-row`、`ebox-column`、`ebox-spacer` | 组合简单横向/纵向布局与空白 box。 |
|
||||
| `ebox-flex`、`ebox-flex-item` | 构建 Flex 容器与 item metadata。 |
|
||||
| `ebox-grid`、`ebox-grid-fr`、`ebox-grid-item` | 构建 Grid、分数轨道和 placement metadata。 |
|
||||
| `ebox-grid`、`ebox-grid-fr` | 构建 Grid 与分数轨道;placement metadata 直接属于 child Box。 |
|
||||
| `ebox-render`、`ebox-render-to-buffer`、`ebox-display-buffer` | 纯 materialization、retained mount 与展示包装。 |
|
||||
| `ebox-commit`、`ebox-buffer-update-report`、`ebox-rerender-buffer-with-context`、`ebox-viewport-window-width` | 原子根提交、报告查询、viewport-context rerender 与统一的显示安全宽度采样。 |
|
||||
| `ebox-region-ids`、`ebox-region-resolve`、`ebox-region-update` | Region 映射、logical handle 查询与直接更新。 |
|
||||
|
||||
14
ebox-dsl.el
14
ebox-dsl.el
@ -160,17 +160,6 @@ The first value is string content. The second value is a lazy child node."
|
||||
(plist-put child key (plist-get item-props key))))
|
||||
(apply #'ebox-flex-item child item-props))))
|
||||
|
||||
(defun ebox--build-grid-item (items)
|
||||
"Build a grid-item node from DSL ITEMS."
|
||||
(let* ((split (ebox-grid--split-attrs items))
|
||||
(props (car split))
|
||||
(children (delq nil (cdr split))))
|
||||
(unless (= (length children) 1)
|
||||
(error "ebox-build: grid-item requires exactly one child node"))
|
||||
(apply #'ebox-grid-item
|
||||
(ebox-dsl-build (car children))
|
||||
props)))
|
||||
|
||||
(defun ebox--build-grid (items)
|
||||
"Build a grid node from DSL ITEMS."
|
||||
(let* ((split (ebox-grid--split-attrs items))
|
||||
@ -226,7 +215,7 @@ Supported forms:
|
||||
(row (box :content \"Left\") (spacer :width (16)) (box :content \"Right\"))
|
||||
(column (box :content \"Top\") (box :content \"Bottom\"))
|
||||
(grid :grid-template-columns ((80) (80))
|
||||
(grid-item :grid-column (1 :span 2) (box \"Header\")))
|
||||
(box :grid-column (1 :span 2) \"Header\"))
|
||||
(flex :width (320) :gap (1 (12)) (item :flex 1 (box \"A\")))
|
||||
|
||||
`box' and `ebox' create `ebox-create' nodes. `row', `column',
|
||||
@ -249,7 +238,6 @@ the child layout in a box using the rendered child layout as content."
|
||||
('column (ebox--build-layout tag #'ebox-column items))
|
||||
('flex (ebox--build-flex items))
|
||||
('item (ebox--build-flex-item items))
|
||||
('grid-item (ebox--build-grid-item items))
|
||||
('spacer (ebox--build-spacer items))
|
||||
('grid (ebox--build-grid items))
|
||||
(_ (error "ebox-build: unknown DSL tag %S" tag)))))))
|
||||
|
||||
26
ebox-grid.el
26
ebox-grid.el
@ -342,29 +342,6 @@ programmatic port and are therefore rejected here."
|
||||
(error "ebox-grid-fr: FACTOR must be positive: %S" factor))
|
||||
(list 'fr factor))
|
||||
|
||||
;;;###autoload
|
||||
(defun ebox-grid-item (node &rest props)
|
||||
"Return NODE with grid placement PROPS attached.
|
||||
Supported properties are `:grid-column', `:grid-row',
|
||||
`:grid-column-span', and `:grid-row-span'."
|
||||
(unless (and (listp node) (plist-member node :ebox-type))
|
||||
(error "ebox-grid-item: NODE is not an Ebox node: %S" node))
|
||||
(let ((copy (copy-sequence node))
|
||||
(declarations (ebox-style-compile-declarations props t)))
|
||||
(while props
|
||||
(let* ((key (pop props))
|
||||
(value (progn
|
||||
(unless props
|
||||
(error "ebox-grid-item: missing value for %S" key))
|
||||
(pop props))))
|
||||
(unless (memq key ebox--grid-item-prop-keys)
|
||||
(error "ebox-grid-item: unsupported property %S" key))
|
||||
(plist-put copy key value)))
|
||||
(plist-put copy :ebox-style-declarations
|
||||
(append (plist-get copy :ebox-style-declarations)
|
||||
declarations))
|
||||
copy))
|
||||
|
||||
(defun ebox-grid--placement-part (value default)
|
||||
"Return (START SPAN) parsed from placement VALUE and DEFAULT."
|
||||
(cond
|
||||
@ -895,7 +872,8 @@ Grid properties include `:grid-template-columns', `:grid-template-rows',
|
||||
`:grid-auto-flow', `:gap', `:grid-row-gap', `:grid-column-gap',
|
||||
`:justify-items', `:align-items', `:width', and `:height'. Track sizes use
|
||||
Ebox pixel/line units, `auto', `(fr FACTOR)', or symbols such as `1fr'.
|
||||
Children may carry `:grid-column' and `:grid-row' placement properties.
|
||||
Children may carry `:grid-column', `:grid-row', `:grid-column-span', and
|
||||
`:grid-row-span' placement properties directly.
|
||||
`ebox-grid-fr' is a convenient constructor for fractional tracks."
|
||||
(let* ((split (ebox-grid--split-attrs items))
|
||||
(raw-props (car split))
|
||||
|
||||
3
ebox.el
3
ebox.el
@ -4608,7 +4608,6 @@ through dirty-set and patch-set execution before falling back to root rerender."
|
||||
ebox-grid
|
||||
ebox-grid-fr
|
||||
ebox-grid-layout-create
|
||||
ebox-grid-item
|
||||
ebox-host-ref-bounds
|
||||
ebox-host-ref-position
|
||||
ebox-native-build
|
||||
@ -4693,7 +4692,7 @@ and are documented separately in the public API reference.")
|
||||
;; :padding :margin :border :border-color :bgcolor
|
||||
;; :text-align :wrap-mode :box-sizing :surface-properties :host-ref
|
||||
;; ebox-build dsl
|
||||
;; Compile ETML-style (box/row/column/flex/item/spacer ...) forms into
|
||||
;; Compile ETML-style (box/row/column/flex/spacer ...) forms into
|
||||
;; normal ebox layout nodes.
|
||||
;;
|
||||
;; ── Layout ────────────────────────────────────────────────────
|
||||
|
||||
@ -100,6 +100,16 @@
|
||||
(should-not (string-match-p "`etaf-[[:alnum:]-]+\\.el`" source))
|
||||
(should-not (string-match-p "`etaf\\.el`" source)))))
|
||||
|
||||
(ert-deftest ebox-docs-do-not-present-removed-grid-item-api ()
|
||||
"Public documentation must expose direct child Grid placement only."
|
||||
(dolist (file '("docs/user/ebox-api-reference.en.md"
|
||||
"docs/user/ebox-api-reference.zh.md"
|
||||
"docs/user/ebox-user-guide.en.md"
|
||||
"docs/user/ebox-user-guide.zh.md"))
|
||||
(let ((source (ebox-docs-test--read file)))
|
||||
(should-not (string-match-p "ebox-grid-item" source))
|
||||
(should-not (string-match-p "`grid-item`" source)))))
|
||||
|
||||
(provide 'ebox-docs-contract-tests)
|
||||
|
||||
;;; ebox-docs-contract-tests.el ends here
|
||||
|
||||
@ -161,12 +161,9 @@
|
||||
(ebox-grid
|
||||
:grid-template-columns '((20) (20))
|
||||
:grid-template-rows '(1 1)
|
||||
(ebox-grid-item (ebox-create :content "Header")
|
||||
:grid-column '(1 :span 2))
|
||||
(ebox-grid-item (ebox-create :content "Left")
|
||||
:grid-row 2 :grid-column 1)
|
||||
(ebox-grid-item (ebox-create :content "Right")
|
||||
:grid-row 2 :grid-column 2)))))
|
||||
(ebox-create :content "Header" :grid-column '(1 :span 2))
|
||||
(ebox-create :content "Left" :grid-row 2 :grid-column 1)
|
||||
(ebox-create :content "Right" :grid-row 2 :grid-column 2)))))
|
||||
(should (string-match-p "Header" plain))
|
||||
(should (string-match-p "Left.*Right" plain))))
|
||||
|
||||
@ -217,11 +214,11 @@
|
||||
(should-error
|
||||
(ebox-render
|
||||
(ebox-grid
|
||||
(ebox-grid-item (ebox-create :content "A") :grid-column-span 0))))
|
||||
(ebox-create :content "A" :grid-column-span 0))))
|
||||
(should-error
|
||||
(ebox-render
|
||||
(ebox-grid
|
||||
(ebox-grid-item (ebox-create :content "A") :grid-column 0)))))
|
||||
(ebox-create :content "A" :grid-column 0)))))
|
||||
|
||||
(ert-deftest ebox-grid-rejects-invalid-ecss-placement-before-layout ()
|
||||
"ECSS rules should reject invalid Grid placement before layout runs."
|
||||
@ -266,13 +263,12 @@
|
||||
(should (string-match-p "A" (ebox-grid-test--plain node)))
|
||||
(should (string-match-p "B" (ebox-grid-test--plain node)))))
|
||||
|
||||
(ert-deftest ebox-build-compiles-grid-item-placement ()
|
||||
"The Ebox DSL should preserve grid-item placement metadata."
|
||||
(ert-deftest ebox-build-compiles-direct-grid-placement ()
|
||||
"The Ebox DSL should preserve direct child Box placement metadata."
|
||||
(let ((node
|
||||
(ebox-build
|
||||
'(grid :grid-template-columns ((20) (20))
|
||||
(grid-item :grid-column (1 :span 2)
|
||||
(box :content "Header"))))))
|
||||
(box :grid-column (1 :span 2) :content "Header")))))
|
||||
(should (string-match-p "Header" (ebox-grid-test--plain node)))
|
||||
(should (equal (plist-get (car (plist-get node :children)) :grid-column)
|
||||
'(1 :span 2)))))
|
||||
|
||||
@ -170,7 +170,6 @@
|
||||
("ebox-flex-item" . "defun")
|
||||
("ebox-flex" . "defun")
|
||||
("ebox-grid-fr" . "defun")
|
||||
("ebox-grid-item" . "defun")
|
||||
("ebox-grid" . "defun")
|
||||
("ebox-grid-layout-create" . "defun")
|
||||
("ebox-normal-layout-create" . "defun")
|
||||
@ -230,7 +229,6 @@
|
||||
ebox-grid
|
||||
ebox-grid-fr
|
||||
ebox-grid-layout-create
|
||||
ebox-grid-item
|
||||
ebox-host-ref-bounds
|
||||
ebox-host-ref-position
|
||||
ebox-native-build
|
||||
|
||||
Loading…
Reference in New Issue
Block a user