Compare commits

...

31 Commits

Author SHA1 Message Date
Last
531d4fde46 减少多余 2025-03-25 01:59:38 +08:00
409dc6d032 1.1.8 2024-12-08 13:07:45 +08:00
62f8bfda2d 更新字体 2024-12-08 13:07:23 +08:00
3ad79bc529 简写合并代码 2024-09-06 01:35:45 +08:00
4b82c1afde 模态窗 2024-09-06 01:08:14 +08:00
a229600fc4 精简 2024-09-05 22:46:21 +08:00
9eb9ddb953 修正一位class 2024-09-05 22:30:01 +08:00
b60339796f 清理日志 2024-09-05 22:05:20 +08:00
96e2f9b9f2 修正m 2024-09-05 21:59:31 +08:00
8a87885921 消除 log 2024-09-05 12:49:28 +08:00
c204f8a3d6 修正tagName 2024-09-05 12:33:32 +08:00
cc6099fb7a DEBUG 2024-09-05 10:26:23 +08:00
7d1a12c5b0 修正路径命名 2024-09-05 10:23:29 +08:00
b094b3f914 快捷定位 2024-08-26 01:00:32 +08:00
573e40f4e2 组件拆分 2024-08-24 15:41:55 +08:00
6428d81818 宽高与内外边距的快捷方法 2024-06-27 14:44:29 +08:00
ddfee6403f 增加font 2024-05-17 00:31:40 +08:00
efa23f8d6a 增加font 2024-05-17 00:30:42 +08:00
2e41b3d18c DEBUG radius 2024-05-16 20:52:00 +08:00
b6df98b35d 快捷方法 mx 2024-05-16 19:24:21 +08:00
c12ca95c42 支持多类型参数 2024-05-16 06:46:41 +08:00
8b12e63fbb grid 布局 2024-05-16 05:24:43 +08:00
5cbc817840 DEBUG 2024-05-16 05:02:34 +08:00
3f6ddb0674 html5语义标签 2024-05-16 05:00:08 +08:00
26ee741741 说明 2024-05-15 23:25:25 +08:00
fcd9b76ace 说明 2024-05-15 23:16:30 +08:00
c5ee9687c9 使用说明 2024-05-15 22:50:58 +08:00
6967078a43 说明文档 2024-05-15 22:40:53 +08:00
4c8aeb57cd 基本结构 2024-05-15 22:38:07 +08:00
d575735bb4 无法动态导出, 恢复默认 2024-05-15 20:56:42 +08:00
3fb9f57e28 DEBUG 2024-05-14 16:37:46 +08:00
8 changed files with 913 additions and 440 deletions

134
.gitignore vendored
View File

@@ -1,134 +1,2 @@
node_modules
package-lock.json package-lock.json
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

104
README.md
View File

@@ -1,8 +1,22 @@
# widgets # widgets
1. 减少多余操作
2. 提高信息密度
3. 补充需求
像 flutter 风格用纯粹 js 构建页面 像 flutter 风格用纯粹 js 构建页面
{ createElement List ListItem Span Button Img Input TextArea Avatar Dialog } { createElement List ListItem Span Button Img Input TextArea Avatar Dialog }
(本质上只是给创建 html 的js方法套了一层使其能够链式调用)
```bash
npm version patch # 修复问题
npm version minor # 添加功能
npm version major # 破坏性更改
npm publish
```
```bash ```bash
npm i @laniakeasupercluster/widgets npm i @laniakeasupercluster/widgets
@@ -33,3 +47,93 @@ git clone git@git.satori.love:LaniakeaSupercluster/widgets.git
npm i npm i
npm run dev npm run dev
``` ```
### HTML 基本概念
对于UI书写划分为两类, 一是外观样式, 一是结构关系
一个元素的基本构成就是其外观样式, 因此它书写在声明它的同一列
```javascript
div.w(128).h(64)
```
嵌套关系则应该换行并缩进, 保持与 HTML 一致的风格
```javascript
document.body.appendChild(div.w(128).h(64).children([
div.text('Hello world!'),
div.text('Hello world!'),
div.text('Hello world!')
]))
```
有时期望复用样式
```javascript
const option = { style: { width: '128px', height: '64px' } }
div(option).children([
div.text('Hello world!'),
div.text('Hello world!'),
div.text('Hello world!')
])
```
由于返回值是真实的 DOM 对象, 它具有原生 DOM 对象的所有方法, 因而可以这样使用它
```javascript
const demo = div.w(128).h(64).children([
div.text('Hello world!'),
div.text('Hello world!'),
div.text('Hello world!')
])
demo.textContent = 'hello world!'
document.body.appendChild(demo)
```
然而 dom 元素的 textContent 并不是一个函数方法而是一个变量值, 自然无法使用链式调用 `demo.textContent('hello world!')`
为了减少代码量也为了避免破坏dom基本结构, 所有额外提供的方法都使用缩写,
像是这样 `demo.text('hello world!')`
这样做的期望自然是将样式便捷地与数据混合处理, 像这样
```javascript
import { div, span } from '@laniakeasupercluster/widgets'
const data = ['hello', 'world', 'and', 'you']
document.body.appendChild(div.w(20).h(20).children([
...data.filter(item => item.length >= 3).map(item => span.text(item))
]))
// 也许想要这样做
const data = ['hello', 'world', 'and', 'you'].filter(item => item.length >= 3)
const items = data.map(item => span.text(item)).map((item, index) => {
if (index < 1) item.style.color = '#cc1414'
return item
})
document.body.appendChild(div.w(20).h(20).children(items))
```
也许想要这样做
```javascript
import { div, span } from '@laniakeasupercluster/widgets'
const data = ['hello', 'world', 'and', 'you'].filter(item => item.length >= 3)
const items = data.map(item => span.text(item)).map((item, index) => {
if (index < 1) item.style.color = '#cc1414'
return item
})
document.body.appendChild(div.w(20).h(20).children(items))
```
虽然这与 pug 中 `div.w-8.h-12` 赋予元素 class 名的概念基本相同, 但实际并不会给元素赋予 class 名, 它只是一个方法去设置元素样式, 因为在 js 的实现中过多引入 css 概念是没有意义的(至少目前我这么认为)
CSS 的便捷性还有两点, 一是伪元素和伪类(:hover ::after), 一是选择器(div.text#ctx.mix > .cc { width: 12px }), 在js中目前没有很好的实现方法, 此事只能再议
三种情境:
div({})
div.text('')
new div({})
符合直觉的标准
1. 元素在初始化前是 class 而不是 function, 这在编辑器中渲染的样式也将不同
2. 直接导出 html5 语义标签
3. 额外添加的快捷方法都是缩写, 将避免与默认属性或方法冲突

View File

@@ -1,12 +1,59 @@
import { div, pre } from './main.js' import { div, span, pre, h1, h2, p, ul, li, button } from './main.js'
import { Dialog } from './widgets/dialog.js'
const demo = div.w(128).h(64) //.onclick.stop('click') document.body.style.fontFamily = 'Arial, sans-serif'
demo.textContent = 'Hello world' document.body.style.fontSize = '14px'
console.log('demo', demo) //document.body.appendChild(div('px-2 w-12 bg- ').childs([
document.body.appendChild(demo) // div.text('会话/账户')
document.body.appendChild(div.w(128).h(64).text('Hello world')) //]))
fetch('./index.js').then(res => res.text()).then(text => { document.body.appendChild(div.absolute.t_2.r_2.bg_red_500.childs([
document.body.appendChild(pre.text(text)) div.text('会话/账户')
]))
document.body.appendChild(div.cursor_pointer.bg_red_500.overflow_clip.w_1000.childs([
h1.m_2rem.pt_2rem.text('Widgets!'),
p.m('1rem').p({ top: '1rem' }).text('是什么, 为什么, 怎么做?'),
ul.m('1rem').p({ top: '1rem' }).childs([
li.text('让事情变简单直观, 仅使用js代码, 而不必在 js/html/css 文件之间来回翻找'),
li.text('大幅提高代码信息密度, 灵感来自 pug/winicss/flutter, 减少布局与样式冗余'),
li.text('为dom对象添加链式操作方法, 使其返回值都是dom自身')
]),
new button({
textContent: '模态窗口 dialog',
onclick: event => {
document.body.appendChild(Dialog({
style: { width: '32rem', padding: '24px' },
children: [
div.childs([
h2.m_0.font_semibold.text('Are you absolutely sure?'),
p.mt_8.color('#71717a').text('This action cannot be undone. This will permanently delete your account and remove your data from our servers.')
]),
div.flex.justify_end.gap_8.childs([
button.text('Cancel'),
button.text('Delete'),
]),
]
}))
}
}),
// 三类参数
p.text(`
span 是一个类(class), 直接实例化 (new span()) 将创建一个 span 元素(DOM对象). 实例化时可以传递参数, 它对应DOM对象的属性.
span.m('2rem').p({ top: '2rem' }).text('Hello world!') 是一个链式操作, 每个链式操作都是DOM对象的方法缩写. 不同的是它返回的是DOM对象自身.
span.cursor_pointer.color_ppp.w(128).h(64).childs([span.text('Hello world!')]) 是预定义的链式操作, 在执行函数方法前的静态属性调用并没有实例化对象, 只是缓存了属性值. 当对象被实例化时, 静态属性会被应用到实例对象上. 而静态属性的语法对应 tailwindcss 的类名.
`)
]))
// 使用 js 文件作为组件管理代码
document.body.appendChild(div.cursor_pointer.color_ppp.w(128).h(64).childs([
span.m('2rem').p({ top: '2rem' }).text('Hello world!'),
new span({ textContent: 'Hello world!' }),
]))
fetch('/index.js').then(res => res.text()).then(text => {
text = text.replace(/from "\/main.js"/g, "from '@laniakeasupercluster/widgets'")
document.body.appendChild(pre.overflow_auto.p('2rem').bg('#ececec').text(text.substring(0, text.indexOf('fetch'))))
}) })

988
main.js

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "@laniakeasupercluster/widgets", "name": "@laniakeasupercluster/widgets",
"description": "A simple widgets tracker", "description": "A simple widgets tracker",
"version": "1.0.2", "version": "1.1.8",
"type": "module", "type": "module",
"main": "main.js", "main": "main.js",
"author": "Laniakea Supercluster <huan0016@gmail.com>", "author": "Laniakea Supercluster <huan0016@gmail.com>",

7
widgets/avatar.js Normal file
View File

@@ -0,0 +1,7 @@
import { createElement } from '../main.js'
export function Avatar(options) {
const element = createElement(options, 'img')
element.onerror = () => element.src = '/favicon.ico'
return element
}

56
widgets/dialog.js Normal file
View File

@@ -0,0 +1,56 @@
import { createElement } from '../main.js'
export function Dialog(options) {
const element = createElement({
tabIndex: 0,
style: {
position: 'fixed',
top: 0,
left: 0,
zIndex: 1000,
width: '100%',
height: '100%',
backdropFilter: 'blur(5px)',
duration: '0.2s',
transition: 'all 0.2s'
},
onclick: async event => {
if (event.target !== event.currentTarget) return
await event.target.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 100 }).finished
await event.target.remove()
},
onkeydown: async event => {
if (event.key !== 'Escape') return
await event.target.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 100 }).finished
await event.target.remove()
},
children: [
createElement({
...options,
style: {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: '#fff',
borderRadius: '0.5rem',
boxShadow: '0 0 1em #ccc',
overflow: 'hidden',
justifyContent: 'center',
...options.style,
}
})
]
})
const observer = new MutationObserver(mutationsList => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
element.focus()
element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 100 }).finished
return observer.disconnect()
}
}
})
observer.observe(document.body, { childList: true, subtree: true })
return element
}

7
widgets/navbar.js Normal file
View File

@@ -0,0 +1,7 @@
import { nav } from '../main.js'
export default function navbar(options) {
return nav.bg_gray_500.w_full.h_12.childs([
new span({ textContent: 'Hello world!' }),
])
}