feat(playground): add ETAF example package
Provide a runnable ETAF-only example and an optional UI catalog entry point with lifecycle-safe open/close commands, tests, and bilingual README documentation.
This commit is contained in:
commit
bce23f6ef0
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.elc
|
||||
tests/*.elc
|
||||
28
Makefile
Normal file
28
Makefile
Normal file
@ -0,0 +1,28 @@
|
||||
EMACS ?= emacs
|
||||
LOAD_PATH = -L . -L ../etaf -L ../etaf-ui -L ../emacs-box
|
||||
|
||||
.PHONY: all compile test check checkdoc load clean
|
||||
|
||||
all: check
|
||||
|
||||
compile:
|
||||
rm -f *.elc tests/*.elc
|
||||
$(EMACS) -Q --batch $(LOAD_PATH) --eval '(setq load-prefer-newer t)' \
|
||||
--eval '(load-file "etaf-playground.el")' \
|
||||
--eval '(byte-compile-file "etaf-playground.el")'
|
||||
|
||||
test: compile
|
||||
$(EMACS) -Q --batch $(LOAD_PATH) --eval '(setq load-prefer-newer t)' \
|
||||
-l tests/etaf-playground-tests.el -f ert-run-tests-batch-and-exit
|
||||
|
||||
load: compile
|
||||
$(EMACS) -Q --batch $(LOAD_PATH) --eval '(require (quote etaf-playground))' \
|
||||
--eval '(princ "etaf-playground load OK\n")'
|
||||
|
||||
checkdoc:
|
||||
$(EMACS) -Q --batch --eval '(progn (require (quote checkdoc)) (dolist (file (directory-files "." t)) (when (string-suffix-p ".el" file) (checkdoc-file file))))'
|
||||
|
||||
check: checkdoc compile test
|
||||
|
||||
clean:
|
||||
rm -f *.elc tests/*.elc
|
||||
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# etaf-playground
|
||||
|
||||
`etaf-playground` is the runnable example application for ETAF. The core example depends only on `etaf`; the optional UI example loads `etaf-ui` when requested. It never imports Ebox internals and it is independent from `ebox-playground`.
|
||||
|
||||
```elisp
|
||||
(require 'etaf-playground)
|
||||
(etaf-playground-open)
|
||||
```
|
||||
|
||||
Run `make check EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs` from this directory.
|
||||
10
README.zh-CN.md
Normal file
10
README.zh-CN.md
Normal file
@ -0,0 +1,10 @@
|
||||
# etaf-playground
|
||||
|
||||
`etaf-playground` 是 ETAF 的可运行示例应用。核心示例只依赖 `etaf`;需要时才通过可选入口加载 `etaf-ui`。它不调用 Ebox 私有实现,也不依赖 `ebox-playground`。
|
||||
|
||||
```elisp
|
||||
(require 'etaf-playground)
|
||||
(etaf-playground-open)
|
||||
```
|
||||
|
||||
在该目录运行 `make check EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs`。
|
||||
97
etaf-playground.el
Normal file
97
etaf-playground.el
Normal file
@ -0,0 +1,97 @@
|
||||
;;; etaf-playground.el --- ETAF public examples -*- lexical-binding: t; -*-
|
||||
|
||||
;; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
;; Author: ETAF contributors
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "29.1") (etaf "0.1.0"))
|
||||
;; URL: https://github.com/ginqi7/etaf-playground
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package is a runnable example application, not a second framework
|
||||
;; layer. The core example depends only on ETAF. The optional catalog demo
|
||||
;; loads `etaf-ui' only when its public command is called.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'etaf)
|
||||
|
||||
(defconst etaf-playground-buffer-name "*ETAF Playground*"
|
||||
"Default buffer name used by `etaf-playground-open'.")
|
||||
|
||||
;;;###autoload
|
||||
(etaf-define-component etaf-playground-counter (&key title)
|
||||
"Render a retained counter titled TITLE."
|
||||
:setup
|
||||
(let ((count (etaf-ref 0 :name 'playground-count)))
|
||||
(lambda ()
|
||||
(etaf-view
|
||||
(column
|
||||
:class "etaf-playground-counter"
|
||||
(text :face 'bold (expr :value title))
|
||||
(text (expr :value (format "Count: %d" (etaf-value count))))
|
||||
(row
|
||||
(text :ref 'decrement :role 'button :tab-index 0
|
||||
:on-press (lambda () (cl-decf (etaf-value count)))
|
||||
" - ")
|
||||
(text :ref 'increment :role 'button :tab-index 0
|
||||
:on-press (lambda () (cl-incf (etaf-value count)))
|
||||
" + ")))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun etaf-playground-view ()
|
||||
"Return the core ETAF playground View without side effects."
|
||||
(etaf-view
|
||||
(column
|
||||
:class "etaf-playground-root"
|
||||
(text :face 'bold "ETAF Playground")
|
||||
(text :color "#687386"
|
||||
"One grammar: Hosts, Components, props, children, and expr.")
|
||||
(etaf-playground-counter :title "Reactive counter")
|
||||
(text :color "#687386"
|
||||
"Press + or - through the public event dispatch path."))))
|
||||
|
||||
;;;###autoload
|
||||
(defun etaf-playground-ui-view ()
|
||||
"Return a View demonstrating the optional official `etaf-ui' catalog."
|
||||
(require 'etaf-ui)
|
||||
(etaf-view
|
||||
(column
|
||||
(etaf-panel :title "Official Components"
|
||||
(etaf-button :label "A public Component"
|
||||
:on-press (lambda () (message "ETAF UI button pressed")))
|
||||
(etaf-checkbox :label "Controlled checkbox"
|
||||
:on-change (lambda (value)
|
||||
(message "Checkbox value: %s" value))))
|
||||
(etaf-playground-counter :title "Core counter"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun etaf-playground-open (&optional buffer-name)
|
||||
"Mount the core playground in BUFFER-NAME and return its buffer."
|
||||
(interactive)
|
||||
(etaf-mount (or buffer-name etaf-playground-buffer-name)
|
||||
(etaf-playground-view)))
|
||||
|
||||
;;;###autoload
|
||||
(defun etaf-playground-open-ui (&optional buffer-name)
|
||||
"Mount the playground with the catalog in BUFFER-NAME."
|
||||
(interactive)
|
||||
(etaf-mount (or buffer-name etaf-playground-buffer-name)
|
||||
(etaf-playground-ui-view)))
|
||||
|
||||
;;;###autoload
|
||||
(defun etaf-playground-close (&optional buffer-name)
|
||||
"Unmount and kill the playground BUFFER-NAME, returning its buffer."
|
||||
(interactive)
|
||||
(let* ((name (or buffer-name etaf-playground-buffer-name))
|
||||
(buffer (get-buffer name)))
|
||||
(when-let ((runtime (and buffer (etaf-runtime-for-buffer buffer))))
|
||||
(etaf-unmount runtime))
|
||||
(when buffer
|
||||
(kill-buffer buffer))
|
||||
buffer))
|
||||
|
||||
(provide 'etaf-playground)
|
||||
|
||||
;;; etaf-playground.el ends here
|
||||
41
tests/etaf-playground-tests.el
Normal file
41
tests/etaf-playground-tests.el
Normal file
@ -0,0 +1,41 @@
|
||||
;;; etaf-playground-tests.el --- ETAF example tests -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'ert)
|
||||
(require 'etaf-playground)
|
||||
|
||||
(ert-deftest etaf-playground-mounts-and-dispatches-core-example ()
|
||||
"Mount the public playground and update its retained counter."
|
||||
(let ((buffer-name " *etaf-playground-test*"))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(etaf-mount buffer-name (etaf-playground-view))
|
||||
(with-current-buffer buffer-name
|
||||
(should (string-match-p "ETAF Playground" (buffer-string)))
|
||||
(should (string-match-p "Count: 0" (buffer-string))))
|
||||
(etaf-dispatch-event (etaf-runtime-for-buffer buffer-name)
|
||||
'increment 'press)
|
||||
(with-current-buffer buffer-name
|
||||
(should (string-match-p "Count: 1" (buffer-string)))))
|
||||
(when-let ((runtime (etaf-runtime-for-buffer buffer-name)))
|
||||
(etaf-unmount runtime))
|
||||
(when-let ((buffer (get-buffer buffer-name)))
|
||||
(kill-buffer buffer)))))
|
||||
|
||||
(ert-deftest etaf-playground-ui-entry-keeps-catalog-optional ()
|
||||
"Load the catalog only through the optional UI entry point."
|
||||
(require 'etaf-ui)
|
||||
(let ((buffer-name " *etaf-playground-ui-test*"))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(etaf-mount buffer-name (etaf-playground-ui-view))
|
||||
(with-current-buffer buffer-name
|
||||
(should (string-match-p "Official Components" (buffer-string)))
|
||||
(should (string-match-p "Controlled checkbox" (buffer-string)))))
|
||||
(when-let ((runtime (etaf-runtime-for-buffer buffer-name)))
|
||||
(etaf-unmount runtime))
|
||||
(when-let ((buffer (get-buffer buffer-name)))
|
||||
(kill-buffer buffer)))))
|
||||
|
||||
(provide 'etaf-playground-tests)
|
||||
|
||||
;;; etaf-playground-tests.el ends here
|
||||
Loading…
Reference in New Issue
Block a user