webc.site 0.1.16 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # 人工智能驱动的界面开发流程笔记
2
+
3
+ ## 纯 web 组件,支持无样式
4
+
5
+ 纯 web 组件,可适配全部前端框架。
6
+
7
+ 组件样式和组件逻辑完全拆分。
8
+
9
+ 可只引用组件的逻辑,然后自己写样式。
10
+
11
+ 同时支持无打包直接引用的使用方式,及配合 vite 等构建器打包的构建。
12
+
13
+ ## 初衷
14
+
15
+ 人工智能开发前端,比较大的问题是调试。
16
+
17
+ 虽然[谷歌反重力](https://antigravity.google) 之类的开发工具,有打开浏览器调试能力,但因为交互流程比较深、需要登录才能访问、需要后台准备数据等等,人工智能全自动浏览器调试流程经常被阻塞,无法继续。
18
+
19
+ 为了加速开发,我建议的方案是:
20
+
21
+ 不在组件中直接调用后端接口读取数据,而是用异步回调的函数的方式,暴露给上层。
22
+
23
+ 这样,就可以在 `Demo.svelte` 用假数据来展示组件的不同状态.
24
+
25
+ 不再对后端数据状态有依赖。不需要登录,不需要在调试的之前,去后端准备数据。
26
+
27
+ 也方便调整样式之后,查看在不同状态下,组件是否呈现有问题。
28
+
29
+ 具体方案,可以参考我的演示前端库 [webc-zh](https://github.com/webc-zh/webc-zh.github.io)。
30
+
31
+ 组件库在线浏览 [GitHub Page](https://webc-zh.github.io)
32
+
33
+ 这里我把前端组件,都拆分为独立的组件文件夹,每个文件夹包含了所有需要的资源(比如 svg 等)。
34
+
35
+ 如此做的好处是,可以类似[shadcn](https://ui.shadcn.com) 一样,实现按需添加。
36
+
37
+ 比如, `./comDev.sh com/scroll`,就可以对滚动条进行单独的开发,调试。
38
+
39
+ 调试默认会打开文件下的 `Demo.svelte` 作为调试入口。
40
+
41
+ 完整的开发提示词,可以参考 [.agents/skills/com/SKILL.md](.agents/skills/com/SKILL.md)
42
+
43
+ 谷歌反重力中 `/com ` 即可使用。
44
+
45
+ ![](https://i-01.eu.org/1779351273.avif)
package/Scroll.js CHANGED
@@ -1,3 +1,3 @@
1
- import { On as A, D as I, newEl as H } from "./x.js";(()=>{let{round:h,max:b,min:D}=Math,a=(r,l,...p)=>{let c=H(r);if(l)c.setAttribute("part",l);return c.append(...p),c},k=(r,l,p)=>{let c=r.toLowerCase(),f=l.toLowerCase(),_="client"+r,y="scroll"+r,d="scroll"+l,w="client"+p;return(i)=>{let C,m,g,v=-1,P=i.firstElementChild,u=a("i","si"),o=a("b","bar",u),T=(e=u[_])=>{let t=i[_],s=P[y];return[s-t,b(1,t-e-6),e,t,s]},E=(e)=>{if(!o.parentNode)return;let[t,s]=T(e),n=b(0,D(i[d],t));if(v!==-1&&v!==n)o.style.opacity=1,clearTimeout(C),C=setTimeout(()=>o.style.opacity=0,1000);v=n,u.style[f]=3+h(s*n/t)+"px"},G=(e)=>{if(m)return;let t=I.body;t.setPointerCapture(e.pointerId),t.classList.add("drag"),o.part.add("drag");let s=e[w],n=()=>{t.classList.remove("drag"),o.part.remove("drag"),x(),m=null},x=A(t,{pointermove:(R)=>{let[z,B]=T();i[d]+=h(z*(R[w]-s)/B),s=R[w]},pointerup:n,lostpointercapture:n});m=n},S=(e)=>{let t=o.getBoundingClientRect(),s=t[f],[n,x,R]=T();i[d]=h(n*b(D((e[w]-s-3-R/2)/x,1),0)),G(e)},N=[A(o,{pointerdown:S}),A(u,{pointerdown:(e)=>{e.stopPropagation(),G(e)}}),A(i,{scroll:()=>E()})],L=new ResizeObserver(()=>{clearTimeout(g),g=setTimeout(()=>{let[,,,e,t]=T();if(e<t){if(o.parentNode!==i)i.appendChild(o);let n=b(16,h(e*e/t));u.style[c]=n+"px",E(n)}else if(o.parentNode)o.remove()},200)}),X=()=>{if(clearTimeout(C),clearTimeout(g),N.forEach((e)=>e()),m)m();if(L.disconnect(),o.parentNode)o.remove()};return o.style.opacity=0,[i,P].forEach((e)=>L.observe(e)),X}};[["v","Height","Top","Y","flex-direction:column;width:100%;min-height:100%"],["h","Width","Left","X","min-width:100%;width:max-content;height:100%"]].map(([r,l,p,c,f])=>{let _=k(l,p,c);customElements.define(r+"-scroll",class extends HTMLElement{connectedCallback(){let y=a("b","",a("slot")),d=a("b","scroll",y);y.style.cssText="display:flex;"+f,this.attachShadow({mode:"open"}).appendChild(d),this._unbind=_(d)}disconnectedCallback(){this._unbind?.()}})})})();
1
+ import { On as A, D as I, newEl as H } from "./x.js";(()=>{let{round:h,max:b,min:D}=Math,a=(r,l,...p)=>{let c=H(r);if(l)c.setAttribute("part",l);return c.append(...p),c},k=(r,l,p)=>{let c=r.toLowerCase(),f=l.toLowerCase(),_="client"+r,y="scroll"+r,d="scroll"+l,w="client"+p;return(s)=>{let C,m,g,v=-1,P=s.firstElementChild,u=a("i","si"),o=a("b","bar",u),T=(e=u[_])=>{let t=s[_],i=P[y];return[i-t,b(1,t-e-6),e,t,i]},E=(e)=>{if(!o.parentNode)return;let[t,i]=T(e),n=b(0,D(s[d],t));if(v!=-1&&v!=n)o.style.opacity=1,clearTimeout(C),C=setTimeout(()=>o.style.opacity=0,1000);v=n,u.style[f]=3+h(i*n/t)+"px"},G=(e)=>{if(m)return;let t=I.body;t.setPointerCapture(e.pointerId),t.classList.add("drag"),o.part.add("drag");let i=e[w],n=()=>{t.classList.remove("drag"),o.part.remove("drag"),x(),m=null},x=A(t,{pointermove:(R)=>{let[z,B]=T();s[d]+=h(z*(R[w]-i)/B),i=R[w]},pointerup:n,lostpointercapture:n});m=n},S=(e)=>{let t=o.getBoundingClientRect(),i=t[f],[n,x,R]=T();s[d]=h(n*b(D((e[w]-i-3-R/2)/x,1),0)),G(e)},N=[A(o,{pointerdown:S}),A(u,{pointerdown:(e)=>{e.stopPropagation(),G(e)}}),A(s,{scroll:E.bind(null,void 0)})],L=new ResizeObserver(()=>{clearTimeout(g),g=setTimeout(()=>{let[,,,e,t]=T();if(e<t){if(o.parentNode!=s)s.appendChild(o);let n=b(16,h(e*e/t));u.style[c]=n+"px",E(n)}else if(o.parentNode)o.remove()},200)}),X=()=>{if(clearTimeout(C),clearTimeout(g),N.forEach((e)=>e()),m)m();if(L.disconnect(),o.parentNode)o.remove()};return o.style.opacity=0,[s,P].forEach((e)=>L.observe(e)),X}};[["v","Height","Top","Y","flex-direction:column;width:100%;min-height:100%"],["h","Width","Left","X","min-width:100%;width:max-content;height:100%"]].map(([r,l,p,c,f])=>{let _=k(l,p,c);customElements.define(r+"-scroll",class extends HTMLElement{connectedCallback(){let y=a("b","",a("slot")),d=a("b","scroll",y);y.style.cssText="display:flex;"+f,this.attachShadow({mode:"open"}).appendChild(d),this._unbind=_(d)}disconnectedCallback(){this._unbind?.()}})})})();
2
2
 
3
3
 
package/Scroll.js.map CHANGED
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../com/Scroll/Scroll.js"],
4
4
  "sourcesContent": [
5
- "import { On } from \"x/On.js\";\nimport { D, newEl } from \"x/dom.js\";\n\n(() => {\n const { round, max, min } = Math,\n PART = \"part\",\n BAR = \"bar\",\n SI = \"si\",\n DRAG = \"drag\",\n PX = \"px\",\n mk = (tag, part, ...kids) => {\n const e = newEl(tag);\n if (part) e.setAttribute(PART, part);\n e.append(...kids);\n return e;\n },\n mkScroll = (size, pos, axis) => {\n const style_size = size.toLowerCase(),\n style_pos = pos.toLowerCase(),\n client_size = \"client\" + size,\n scroll_size = \"scroll\" + size,\n scroll_pos = \"scroll\" + pos,\n client_pos = \"client\" + axis;\n return (ct) => {\n let timer_bar,\n ptr_unbind,\n timer_resize,\n pre_st = -1;\n const m = ct.firstElementChild,\n si = mk(\"i\", SI),\n bar = mk(\"b\", BAR, si),\n getGeo = (sih = si[client_size]) => {\n const ch = ct[client_size],\n sh = m[scroll_size];\n return [sh - ch, max(1, ch - sih - 6), sih, ch, sh];\n },\n updateTop = (h) => {\n if (!bar.parentNode) return;\n const [ds, db] = getGeo(h),\n st = max(0, min(ct[scroll_pos], ds));\n if (pre_st !== -1 && pre_st !== st) {\n bar.style.opacity = 1;\n clearTimeout(timer_bar);\n timer_bar = setTimeout(() => (bar.style.opacity = 0), 1e3);\n }\n pre_st = st;\n si.style[style_pos] = 3 + round((db * st) / ds) + PX;\n },\n onDown = (e) => {\n if (ptr_unbind) return;\n const bd = D.body;\n bd.setPointerCapture(e.pointerId);\n bd.classList.add(DRAG);\n bar.part.add(DRAG);\n let pre = e[client_pos];\n const detach = () => {\n bd.classList.remove(DRAG);\n bar.part.remove(DRAG);\n un_ptr();\n ptr_unbind = null;\n },\n un_ptr = On(bd, {\n pointermove: (e) => {\n const [ds, db] = getGeo();\n ct[scroll_pos] += round((ds * (e[client_pos] - pre)) / db);\n pre = e[client_pos];\n },\n pointerup: detach,\n lostpointercapture: detach,\n });\n ptr_unbind = detach;\n },\n onClick = (e) => {\n const rect = bar.getBoundingClientRect(),\n top = rect[style_pos],\n [ds, db, sih] = getGeo();\n ct[scroll_pos] = round(ds * max(min((e[client_pos] - top - 3 - sih / 2) / db, 1), 0));\n onDown(e);\n },\n unbind = [\n On(bar, { pointerdown: onClick }),\n On(si, {\n pointerdown: (e) => {\n e.stopPropagation();\n onDown(e);\n },\n }),\n On(ct, { scroll: () => updateTop() }),\n ],\n ob = new ResizeObserver(() => {\n clearTimeout(timer_resize);\n timer_resize = setTimeout(() => {\n const [, , , ch, sh] = getGeo(),\n is_turn = ch < sh;\n if (is_turn) {\n if (bar.parentNode !== ct) ct.appendChild(bar);\n const h = max(16, round((ch * ch) / sh));\n si.style[style_size] = h + PX;\n updateTop(h);\n } else if (bar.parentNode) {\n bar.remove();\n }\n }, 200);\n }),\n destroy = () => {\n clearTimeout(timer_bar);\n clearTimeout(timer_resize);\n unbind.forEach((f) => f());\n if (ptr_unbind) ptr_unbind();\n ob.disconnect();\n if (bar.parentNode) bar.remove();\n };\n bar.style.opacity = 0;\n [ct, m].forEach((i) => ob.observe(i));\n return destroy;\n };\n };\n\n [\n [\"v\", \"Height\", \"Top\", \"Y\", \"flex-direction:column;width:100%;min-height:100%\"],\n [\"h\", \"Width\", \"Left\", \"X\", \"min-width:100%;width:max-content;height:100%\"],\n ].map(([prefix, size, pos, axis, css]) => {\n const initScroll = mkScroll(size, pos, axis);\n customElements.define(\n prefix + \"-scroll\",\n class extends HTMLElement {\n connectedCallback() {\n const content = mk(\"b\", \"\", mk(\"slot\")),\n wrapper = mk(\"b\", \"scroll\", content);\n content.style.cssText = \"display:flex;\" + css;\n this.attachShadow({ mode: \"open\" }).appendChild(wrapper);\n this._unbind = initScroll(wrapper);\n }\n disconnectedCallback() {\n this._unbind?.();\n }\n },\n );\n });\n})();\n"
5
+ "import { On } from \"x/On.js\";\nimport { D, newEl } from \"x/dom.js\";\n\n(() => {\n const { round, max, min } = Math,\n PART = \"part\",\n BAR = \"bar\",\n SI = \"si\",\n DRAG = \"drag\",\n PX = \"px\",\n mk = (tag, part, ...kids) => {\n const e = newEl(tag);\n if (part) e.setAttribute(PART, part);\n e.append(...kids);\n return e;\n },\n mkScroll = (size, pos, axis) => {\n const style_size = size.toLowerCase(),\n style_pos = pos.toLowerCase(),\n client_size = \"client\" + size,\n scroll_size = \"scroll\" + size,\n scroll_pos = \"scroll\" + pos,\n client_pos = \"client\" + axis;\n return (ct) => {\n let timer_bar,\n ptr_unbind,\n timer_resize,\n pre_st = -1;\n const m = ct.firstElementChild,\n si = mk(\"i\", SI),\n bar = mk(\"b\", BAR, si),\n getGeo = (sih = si[client_size]) => {\n const ch = ct[client_size],\n sh = m[scroll_size];\n return [sh - ch, max(1, ch - sih - 6), sih, ch, sh];\n },\n updateTop = (h) => {\n if (!bar.parentNode) return;\n const [ds, db] = getGeo(h),\n st = max(0, min(ct[scroll_pos], ds));\n if (pre_st != -1 && pre_st != st) {\n bar.style.opacity = 1;\n clearTimeout(timer_bar);\n timer_bar = setTimeout(() => (bar.style.opacity = 0), 1e3);\n }\n pre_st = st;\n si.style[style_pos] = 3 + round((db * st) / ds) + PX;\n },\n onDown = (e) => {\n if (ptr_unbind) return;\n const bd = D.body;\n bd.setPointerCapture(e.pointerId);\n bd.classList.add(DRAG);\n bar.part.add(DRAG);\n let pre = e[client_pos];\n const detach = () => {\n bd.classList.remove(DRAG);\n bar.part.remove(DRAG);\n un_ptr();\n ptr_unbind = null;\n },\n un_ptr = On(bd, {\n pointermove: (e) => {\n const [ds, db] = getGeo();\n ct[scroll_pos] += round((ds * (e[client_pos] - pre)) / db);\n pre = e[client_pos];\n },\n pointerup: detach,\n lostpointercapture: detach,\n });\n ptr_unbind = detach;\n },\n onClick = (e) => {\n const rect = bar.getBoundingClientRect(),\n top = rect[style_pos],\n [ds, db, sih] = getGeo();\n ct[scroll_pos] = round(ds * max(min((e[client_pos] - top - 3 - sih / 2) / db, 1), 0));\n onDown(e);\n },\n unbind = [\n On(bar, { pointerdown: onClick }),\n On(si, {\n pointerdown: (e) => {\n e.stopPropagation();\n onDown(e);\n },\n }),\n On(ct, { scroll: updateTop.bind(null, undefined) }),\n ],\n ob = new ResizeObserver(() => {\n clearTimeout(timer_resize);\n timer_resize = setTimeout(() => {\n const [, , , ch, sh] = getGeo(),\n is_turn = ch < sh;\n if (is_turn) {\n if (bar.parentNode != ct) ct.appendChild(bar);\n const h = max(16, round((ch * ch) / sh));\n si.style[style_size] = h + PX;\n updateTop(h);\n } else if (bar.parentNode) {\n bar.remove();\n }\n }, 200);\n }),\n destroy = () => {\n clearTimeout(timer_bar);\n clearTimeout(timer_resize);\n unbind.forEach((f) => f());\n if (ptr_unbind) ptr_unbind();\n ob.disconnect();\n if (bar.parentNode) bar.remove();\n };\n bar.style.opacity = 0;\n [ct, m].forEach((i) => ob.observe(i));\n return destroy;\n };\n };\n\n [\n [\"v\", \"Height\", \"Top\", \"Y\", \"flex-direction:column;width:100%;min-height:100%\"],\n [\"h\", \"Width\", \"Left\", \"X\", \"min-width:100%;width:max-content;height:100%\"],\n ].map(([prefix, size, pos, axis, css]) => {\n const initScroll = mkScroll(size, pos, axis);\n customElements.define(\n prefix + \"-scroll\",\n class extends HTMLElement {\n connectedCallback() {\n const content = mk(\"b\", \"\", mk(\"slot\")),\n wrapper = mk(\"b\", \"scroll\", content);\n content.style.cssText = \"display:flex;\" + css;\n this.attachShadow({ mode: \"open\" }).appendChild(wrapper);\n this._unbind = initScroll(wrapper);\n }\n disconnectedCallback() {\n this._unbind?.();\n }\n },\n );\n });\n})();\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "1DBAB42F8A67745664756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "5DD6B8D33029FF4A64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,9 @@
1
+ 给x目录下每个 js 在同级目录,写同名的 .md 介绍用途
2
+
3
+ 第一行是标题,以#号开头,标题不要包含 js 的名字,主要写起用途
4
+
5
+ 内容依次介绍每个导出的函数、参数.
6
+
7
+ 函数参数如果是回调函数,请写明回调函数的参数,返回值
8
+
9
+ 文风要简洁,不写形容词
@@ -0,0 +1,13 @@
1
+ export const On = (elem, dict) => {
2
+ let event, func;
3
+ for (event in dict) {
4
+ func = dict[event];
5
+ elem.addEventListener(event, func);
6
+ }
7
+ return () => {
8
+ for (event in dict) {
9
+ func = dict[event];
10
+ elem.removeEventListener(event, func);
11
+ }
12
+ };
13
+ };
@@ -0,0 +1,8 @@
1
+ # 绑定与解绑事件监听
2
+
3
+ - `On(elem, dict)`
4
+ - `elem`: DOM 元素。
5
+ - `dict`: 事件与回调对象。键为事件名,值为回调函数。回调函数格式:
6
+ - 参数:`e`(事件对象)。
7
+ - 返回值:无。
8
+ - 返回值:解绑函数。格式为 `() => void`。
@@ -0,0 +1,23 @@
1
+ import { goto, selfA, B } from "../../x.js";
2
+ B.addEventListener("click", (e) => {
3
+ var href, name, p;
4
+ p = e.target;
5
+ while (p) {
6
+ ({ nodeName: name } = p);
7
+ if (name === "A") {
8
+ ({ href } = p);
9
+ if (href) {
10
+ href = selfA(p, e);
11
+ if (href !== void 0) {
12
+ goto(href);
13
+ } else if (!p.target) {
14
+ p.target = "_blank";
15
+ }
16
+ }
17
+ break;
18
+ } else if (name === "BODY") {
19
+ break;
20
+ }
21
+ p = p.parentNode;
22
+ }
23
+ });
@@ -0,0 +1,3 @@
1
+ # 拦截链接点击实现无刷新跳转
2
+
3
+ 无导出函数。导入时自动在 `document.body` 监听点击事件。点击同站链接时阻止默认行为并调用 `goto` 跳转,点击外链且无 `target` 属性时将其设为 `_blank`。
@@ -0,0 +1,17 @@
1
+ import { route } from "./route.js";
2
+
3
+ // 避免 onMount 之前,route 被触发,导致重复加载数据
4
+ export const delayRoute = (loadUrl) => {
5
+ let t;
6
+ const unbind = route((url, preUrl) => {
7
+ t = setTimeout(() => {
8
+ loadUrl(url, preUrl);
9
+ });
10
+ });
11
+ return () => {
12
+ unbind();
13
+ clearTimeout(t);
14
+ };
15
+ };
16
+
17
+ export default delayRoute;
@@ -0,0 +1,11 @@
1
+ # 延迟路由触发避免重复加载数据
2
+
3
+ 用于 Svelte,避免组件被卸载前触发路由导致重复加载数据。
4
+
5
+ - `delayRoute(loadUrl)`
6
+ - `loadUrl`: 路由变化时的回调函数。格式:
7
+ - 参数:
8
+ - `url`: 字符串,新 URL 路径。
9
+ - `preUrl`: 字符串(可选),旧 URL 路径。
10
+ - 返回值:无。
11
+ - 返回值:取消路由订阅并清除定时器的函数,格式为 `() => void`。
@@ -0,0 +1,3 @@
1
+ export const D = document,
2
+ B = D.body,
3
+ newEl = D.createElement.bind(D);
@@ -0,0 +1,7 @@
1
+ # DOM 属性与方法简写
2
+
3
+ - `D`: `document` 实例。
4
+ - `B`: `document.body` 元素。
5
+ - `newEl(tagName)`
6
+ - `tagName`: 字符串,HTML 标签名。
7
+ - 返回值:新创建的 DOM 元素。
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@3-/x",
3
+ "version": "0.2.0",
4
+ "description": "",
5
+ "keywords": [],
6
+ "homepage": "https://webc-zh.github.io",
7
+ "license": "MulanPSL-2.0",
8
+ "author": "i18n.site@gmail.com",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/webc-zh/webc-zh.github.io.git"
12
+ },
13
+ "files": [
14
+ "./*"
15
+ ],
16
+ "type": "module",
17
+ "exports": {
18
+ "./*": "./*"
19
+ },
20
+ "scripts": {},
21
+ "devDependencies": {}
22
+ }
@@ -0,0 +1,2 @@
1
+ import { D } from "./dom.js";
2
+ D.getElementById("W").remove();
@@ -0,0 +1,3 @@
1
+ # 移除加载等待元素
2
+
3
+ 无导出函数。导入时自动从 DOM 中移除 ID 为 `W` 的元素。
@@ -0,0 +1,59 @@
1
+ import { On } from "../../x.js";
2
+ export const nowUrl = () => location.pathname.slice(1),
3
+ [route, setPre, preUrl, refresh, removeSlash, split, setUrl, goto] = (() => {
4
+ let PRE = nowUrl(),
5
+ HOOK = [];
6
+ if (location.hash) PRE += location.hash;
7
+
8
+ const HASH = "#",
9
+ route = (hook) => {
10
+ HOOK.push(hook);
11
+ hook(nowUrl());
12
+ return () => {
13
+ HOOK = HOOK.filter((f) => f !== hook);
14
+ };
15
+ },
16
+ setPre = (url) => {
17
+ PRE = url;
18
+ },
19
+ preUrl = () => PRE,
20
+ refresh = (url) => {
21
+ url = url || nowUrl();
22
+ for (const f of HOOK) f(url, PRE);
23
+ setPre(url);
24
+ },
25
+ removeSlash = (url) => (url[0] === "/" ? url.slice(1) : url),
26
+ split = (str, s) => {
27
+ const p = str.indexOf(s);
28
+ return p >= 0 ? [str.slice(0, p), str.slice(p + 1)] : [str, ""];
29
+ },
30
+ setUrl = (url) => {
31
+ url = removeSlash(url);
32
+ if (url !== PRE) {
33
+ const [path, hash] = split(url, HASH),
34
+ [p] = split(PRE, HASH);
35
+ setPre(url);
36
+ if (path !== p) {
37
+ history.pushState(null, "", "/" + url);
38
+ return 1;
39
+ }
40
+ if (location.hash.slice(1) !== hash) {
41
+ location.hash = hash;
42
+ return;
43
+ }
44
+ }
45
+ window.dispatchEvent(new HashChangeEvent("hashchange"));
46
+ },
47
+ goto = (url) => {
48
+ if (setUrl(url)) refresh(url);
49
+ };
50
+
51
+ On(window, {
52
+ popstate: () => {
53
+ const url = nowUrl();
54
+ if (url !== split(PRE, HASH)[0]) refresh(url);
55
+ },
56
+ });
57
+
58
+ return [route, setPre, preUrl, refresh, removeSlash, split, setUrl, goto];
59
+ })();
@@ -0,0 +1,37 @@
1
+ # 路由管理与跳转
2
+
3
+ - `nowUrl()`
4
+ - 返回值:当前 URL 路径(不含首部斜杠)。
5
+
6
+ - `route(hook)`
7
+ - `hook`: 路由变化时执行的回调函数。格式:
8
+ - 参数:
9
+ - `url`: 字符串,新 URL 路径。
10
+ - `preUrl`: 字符串(可选),旧 URL 路径。
11
+ - 返回值:无。
12
+ - 返回值:取消订阅的函数,格式为 `() => void`。
13
+
14
+ - `setPre(url)`
15
+ - `url`: 字符串,新的前一次 URL 路径。
16
+
17
+ - `preUrl()`
18
+ - 返回值:前一次的 URL 路径。
19
+
20
+ - `refresh(url)`
21
+ - `url`: 字符串(可选),要触发回调的 URL 路径,默认是当前 URL。
22
+
23
+ - `removeSlash(url)`
24
+ - `url`: 字符串。
25
+ - 返回值:移除首部斜杠后的字符串。
26
+
27
+ - `split(str, s)`
28
+ - `str`: 待拆分字符串。
29
+ - `s`: 分隔符。
30
+ - 返回值:包含两个元素的数组 `[前部, 后部]`。
31
+
32
+ - `setUrl(url)`
33
+ - `url`: 目标 URL 路径。
34
+ - 返回值:若路径改变返回 `1`,若仅 hash 改变不返回值。
35
+
36
+ - `goto(url)`
37
+ - `url`: 目标 URL 路径。
@@ -0,0 +1,13 @@
1
+ // 判断A标签的href是否为当前网站的, 如果是, 返回url, 以实现不刷新跳转
2
+ export const selfA = (p, e) => {
3
+ var hash, url;
4
+ if (p.host === location.host) {
5
+ ({ hash } = p);
6
+ url = p.pathname.slice(1) + p.search;
7
+ if (hash) {
8
+ url += hash;
9
+ }
10
+ e.preventDefault();
11
+ return url;
12
+ }
13
+ };
@@ -0,0 +1,6 @@
1
+ # 同站链接点击校验与处理
2
+
3
+ - `selfA(p, e)`
4
+ - `p`: `<a>` 元素。
5
+ - `e`: 点击事件对象。
6
+ - 返回值:若是同站链接,阻止默认行为并返回包含 search/hash 的相对路径,否则返回 `undefined`。
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"webc.site","version":"0.1.16","description":"","keywords":[],"homepage":"https://webc-zh.github.io","license":"MulanPSL-2.0","author":"i18n.site@gmail.com","repository":{"type":"git","url":"git+https://github.com/webc-zh/webc-zh.github.io.git"},"files":["./*"],"type":"module","exports":{"./*":"./*"},"scripts":{},"devDependencies":{}}
1
+ {"name":"webc.site","version":"0.1.19","description":"","keywords":[],"homepage":"https://webc-zh.github.io","license":"MulanPSL-2.0","author":"i18n.site@gmail.com","repository":{"type":"git","url":"git+https://github.com/webc-zh/webc-zh.github.io.git"},"files":["./*"],"type":"module","exports":{"./*":"./*"},"scripts":{},"devDependencies":{}}
package/x.js CHANGED
@@ -1 +1 @@
1
- const e=(e,t)=>{let n,r;for(n in t)r=t[n],e.addEventListener(n,r);return()=>{for(n in t)r=t[n],e.removeEventListener(n,r)}},t=document,n=t.body,r=t.createElement.bind(t);export{n as B,t as D,e as On,r as newEl};
1
+ const e=(e,t)=>{let n,r;for(n in t)r=t[n],e.addEventListener(n,r);return()=>{for(n in t)r=t[n],e.removeEventListener(n,r)}},t=()=>location.pathname.slice(1),[n,r,i,a,o,s,c,l]=(()=>{let n=t(),r=[];location.hash&&(n+=location.hash);let i=e=>(r.push(e),e(t()),()=>{r=r.filter(t=>t!==e)}),a=e=>{n=e},o=()=>n,s=e=>{e||=t();for(let t of r)t(e,n);a(e)},c=e=>e[0]===`/`?e.slice(1):e,l=(e,t)=>{let n=e.indexOf(t);return n>=0?[e.slice(0,n),e.slice(n+1)]:[e,``]},u=e=>{if(e=c(e),e!==n){let[t,r]=l(e,`#`),[i]=l(n,`#`);if(a(e),t!==i)return history.pushState(null,``,`/`+e),1;if(location.hash.slice(1)!==r){location.hash=r;return}}window.dispatchEvent(new HashChangeEvent(`hashchange`))};return e(window,{popstate:()=>{let e=t();e!==l(n,`#`)[0]&&s(e)}}),[i,a,o,s,c,l,u,e=>{u(e)&&s(e)}]})(),u=(e,t)=>{var n,r;if(e.host===location.host)return{hash:n}=e,r=e.pathname.slice(1)+e.search,n&&(r+=n),t.preventDefault(),r},d=document,f=d.body,p=d.createElement.bind(d);export{f as B,d as D,e as On,l as goto,p as newEl,t as nowUrl,i as preUrl,a as refresh,o as removeSlash,n as route,u as selfA,r as setPre,c as setUrl,s as split};
package/x.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"x.js","names":[],"sources":["../../importmap/x/On.js","../../importmap/x/dom.js"],"sourcesContent":["export const On = (elem, dict) => {\n let event, func;\n for (event in dict) {\n func = dict[event];\n elem.addEventListener(event, func);\n }\n return () => {\n for (event in dict) {\n func = dict[event];\n elem.removeEventListener(event, func);\n }\n };\n};\n","export const D = document,\n B = D.body,\n newEl = D.createElement.bind(D);\n"],"mappings":"AAAA,MAAa,GAAM,EAAM,IAAS,CAChC,IAAI,EAAO,EACX,IAAK,KAAS,EACZ,EAAO,EAAK,GACZ,EAAK,iBAAiB,EAAO,CAAI,EAEnC,UAAa,CACX,IAAK,KAAS,EACZ,EAAO,EAAK,GACZ,EAAK,oBAAoB,EAAO,CAAI,CAExC,CACF,ECZa,EAAI,SACf,EAAI,EAAE,KACN,EAAQ,EAAE,cAAc,KAAK,CAAC"}
1
+ {"version":3,"file":"x.js","names":[],"sources":["../../importmap/x/On.js","../../importmap/x/route.js","../../importmap/x/selfA.js","../../importmap/x/dom.js"],"sourcesContent":["export const On = (elem, dict) => {\n let event, func;\n for (event in dict) {\n func = dict[event];\n elem.addEventListener(event, func);\n }\n return () => {\n for (event in dict) {\n func = dict[event];\n elem.removeEventListener(event, func);\n }\n };\n};\n","import { On } from \"x/On.js\";\n\nexport const nowUrl = () => location.pathname.slice(1),\n [route, setPre, preUrl, refresh, removeSlash, split, setUrl, goto] = (() => {\n let PRE = nowUrl(),\n HOOK = [];\n if (location.hash) PRE += location.hash;\n\n const HASH = \"#\",\n route = (hook) => {\n HOOK.push(hook);\n hook(nowUrl());\n return () => {\n HOOK = HOOK.filter((f) => f !== hook);\n };\n },\n setPre = (url) => {\n PRE = url;\n },\n preUrl = () => PRE,\n refresh = (url) => {\n url = url || nowUrl();\n for (const f of HOOK) f(url, PRE);\n setPre(url);\n },\n removeSlash = (url) => (url[0] === \"/\" ? url.slice(1) : url),\n split = (str, s) => {\n const p = str.indexOf(s);\n return p >= 0 ? [str.slice(0, p), str.slice(p + 1)] : [str, \"\"];\n },\n setUrl = (url) => {\n url = removeSlash(url);\n if (url !== PRE) {\n const [path, hash] = split(url, HASH),\n [p] = split(PRE, HASH);\n setPre(url);\n if (path !== p) {\n history.pushState(null, \"\", \"/\" + url);\n return 1;\n }\n if (location.hash.slice(1) !== hash) {\n location.hash = hash;\n return;\n }\n }\n window.dispatchEvent(new HashChangeEvent(\"hashchange\"));\n },\n goto = (url) => {\n if (setUrl(url)) refresh(url);\n };\n\n On(window, {\n popstate: () => {\n const url = nowUrl();\n if (url !== split(PRE, HASH)[0]) refresh(url);\n },\n });\n\n return [route, setPre, preUrl, refresh, removeSlash, split, setUrl, goto];\n })();\n","// 判断A标签的href是否为当前网站的, 如果是, 返回url, 以实现不刷新跳转\nexport const selfA = (p, e) => {\n var hash, url;\n if (p.host === location.host) {\n ({ hash } = p);\n url = p.pathname.slice(1) + p.search;\n if (hash) {\n url += hash;\n }\n e.preventDefault();\n return url;\n }\n};\n","export const D = document,\n B = D.body,\n newEl = D.createElement.bind(D);\n"],"mappings":"AAAA,MAAa,GAAM,EAAM,IAAS,CAChC,IAAI,EAAO,EACX,IAAK,KAAS,EACZ,EAAO,EAAK,GACZ,EAAK,iBAAiB,EAAO,CAAI,EAEnC,UAAa,CACX,IAAK,KAAS,EACZ,EAAO,EAAK,GACZ,EAAK,oBAAoB,EAAO,CAAI,CAExC,CACF,ECVa,MAAe,SAAS,SAAS,MAAM,CAAC,EACnD,CAAC,EAAO,EAAQ,EAAQ,EAAS,EAAa,EAAO,EAAQ,QAAe,CAC1E,IAAI,EAAM,EAAO,EACf,EAAO,CAAC,EACN,SAAS,OAAM,GAAO,SAAS,MAEnC,IACE,EAAS,IACP,EAAK,KAAK,CAAI,EACd,EAAK,EAAO,CAAC,MACA,CACX,EAAO,EAAK,OAAQ,GAAM,IAAM,CAAI,CACtC,GAEF,EAAU,GAAQ,CAChB,EAAM,CACR,EACA,MAAe,EACf,EAAW,GAAQ,CACjB,IAAa,EAAO,EACpB,IAAK,IAAM,KAAK,EAAM,EAAE,EAAK,CAAG,EAChC,EAAO,CAAG,CACZ,EACA,EAAe,GAAS,EAAI,KAAO,IAAM,EAAI,MAAM,CAAC,EAAI,EACxD,GAAS,EAAK,IAAM,CAClB,IAAM,EAAI,EAAI,QAAQ,CAAC,EACvB,OAAO,GAAK,EAAI,CAAC,EAAI,MAAM,EAAG,CAAC,EAAG,EAAI,MAAM,EAAI,CAAC,CAAC,EAAI,CAAC,EAAK,EAAE,CAChE,EACA,EAAU,GAAQ,CAEhB,GADA,EAAM,EAAY,CAAG,EACjB,IAAQ,EAAK,CACf,GAAM,CAAC,EAAM,GAAQ,EAAM,EAAK,GAAI,EAClC,CAAC,GAAK,EAAM,EAAK,GAAI,EAEvB,GADA,EAAO,CAAG,EACN,IAAS,EAEX,OADA,QAAQ,UAAU,KAAM,GAAI,IAAM,CAAG,EAC9B,EAET,GAAI,SAAS,KAAK,MAAM,CAAC,IAAM,EAAM,CACnC,SAAS,KAAO,EAChB,MACF,CACF,CACA,OAAO,cAAc,IAAI,gBAAgB,YAAY,CAAC,CACxD,EAYF,OAPA,EAAG,OAAQ,CACT,aAAgB,CACd,IAAM,EAAM,EAAO,EACf,IAAQ,EAAM,EAAK,GAAI,EAAE,IAAI,EAAQ,CAAG,CAC9C,CACF,CAAC,EAEM,CAAC,EAAO,EAAQ,EAAQ,EAAS,EAAa,EAAO,EAXlD,GAAQ,CACV,EAAO,CAAG,GAAG,EAAQ,CAAG,CAC9B,CASsE,CAC1E,GAAG,EC1DQ,GAAS,EAAG,IAAM,CAC7B,IAAI,EAAM,EACV,GAAI,EAAE,OAAS,SAAS,KAOtB,MANC,SAAW,EACZ,EAAM,EAAE,SAAS,MAAM,CAAC,EAAI,EAAE,OAC1B,IACF,GAAO,GAET,EAAE,eAAe,EACV,CAEX,ECZa,EAAI,SACf,EAAI,EAAE,KACN,EAAQ,EAAE,cAAc,KAAK,CAAC"}