add document, fix tooltip position issue
This commit is contained in:
parent
f5bc42fa20
commit
5406eea20e
@ -8,7 +8,6 @@ $boxDisabledColor: #d9d9d9;
|
|||||||
.check-box-inner {
|
.check-box-inner {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-top: 2px;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
|
2
css/ui.min.css
vendored
2
css/ui.min.css
vendored
@ -1 +1 @@
|
|||||||
.checkbox-image>input[type=checkbox]{display:none}.checkbox-image>input[type=checkbox]:checked~.checked{display:inline}.checkbox-image>input[type=checkbox]:checked~.unchecked{display:none}.checkbox-image>.checked{display:none}.checkbox-image>.unchecked{display:inline}.checkbox-wrapper{display:flex;align-items:center;padding:0 8px;height:36px}.checkbox-wrapper .check-box-inner{position:relative;display:inline-block;margin-top:2px;padding:0;width:14px;height:14px;background-color:#fff;border:1px solid #999898;-moz-user-select:none;user-select:none;-webkit-user-select:none;border-radius:2px;transition:all .2s;cursor:pointer}.checkbox-wrapper .check-box-inner>svg{position:absolute;top:0;left:0;width:100%;height:100%;fill:#fff;transform:scale(0);opacity:0;transition:all .08s cubic-bezier(0.78, 0.14, 0.15, 0.86)}.checkbox-wrapper>input[type=checkbox]{display:none}.checkbox-wrapper>input[type=checkbox]:checked+.check-box-inner{border-color:#1890ff;background-color:#1890ff}.checkbox-wrapper>input[type=checkbox]:checked+.check-box-inner>svg{transform:scale(1);opacity:1}.checkbox-wrapper>input[type=checkbox]:disabled+.check-box-inner{border-color:#d9d9d9;cursor:default}.checkbox-wrapper>input[type=checkbox]:disabled:checked+.check-box-inner{border-color:#d9d9d9;background-color:#d9d9d9}.checkbox-wrapper>input[type=checkbox]:disabled~span{color:#d9d9d9;cursor:default}.checkbox-wrapper .check-box-inner{flex:0 0 auto}.checkbox-wrapper>span{flex:1 1 auto;font-weight:400;font-size:.875rem;padding-left:8px;padding-right:6px;align-self:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;color:#201f1e}.tooltip-color{background-color:#fff;color:#323130;border-color:rgba(204,204,204,.8);outline:none}.tooltip-wrapper{position:fixed;word-wrap:break-word;height:auto;text-align:left;z-index:250;min-width:75px;max-width:480px;min-height:32px;border-radius:2px;box-shadow:0 3.2px 7.2px 0 rgba(0,0,0,.13),0 .6px 1.8px 0 rgba(0,0,0,.11);transition:visibility 0s linear 120ms,opacity 120ms ease}.tooltip-wrapper>.tooltip-pointer{box-sizing:border-box;box-shadow:0 5px 15px 2px rgba(0,0,0,.3);border:1px solid #fff;z-index:-1;width:16px;height:16px;position:absolute;left:calc(50% - 8px);bottom:-8px;transform:rotate(-45deg);transform-origin:center}.tooltip-wrapper>.tooltip-curtain{position:absolute;width:100%;height:100%;z-index:-1}.tooltip-wrapper>.tooltip-content{font-size:.8125rem;line-height:1rem;white-space:normal;overflow:auto;margin:8px;height:calc(100% - 16px);-moz-user-select:none;user-select:none;-webkit-user-select:none}
|
.checkbox-image>input[type=checkbox]{display:none}.checkbox-image>input[type=checkbox]:checked~.checked{display:inline}.checkbox-image>input[type=checkbox]:checked~.unchecked{display:none}.checkbox-image>.checked{display:none}.checkbox-image>.unchecked{display:inline}.checkbox-wrapper{display:flex;align-items:center;padding:0 8px;height:36px}.checkbox-wrapper .check-box-inner{position:relative;display:inline-block;padding:0;width:14px;height:14px;background-color:#fff;border:1px solid #999898;-moz-user-select:none;user-select:none;-webkit-user-select:none;border-radius:2px;transition:all .2s;cursor:pointer}.checkbox-wrapper .check-box-inner>svg{position:absolute;top:0;left:0;width:100%;height:100%;fill:#fff;transform:scale(0);opacity:0;transition:all .08s cubic-bezier(0.78, 0.14, 0.15, 0.86)}.checkbox-wrapper>input[type=checkbox]{display:none}.checkbox-wrapper>input[type=checkbox]:checked+.check-box-inner{border-color:#1890ff;background-color:#1890ff}.checkbox-wrapper>input[type=checkbox]:checked+.check-box-inner>svg{transform:scale(1);opacity:1}.checkbox-wrapper>input[type=checkbox]:disabled+.check-box-inner{border-color:#d9d9d9;cursor:default}.checkbox-wrapper>input[type=checkbox]:disabled:checked+.check-box-inner{border-color:#d9d9d9;background-color:#d9d9d9}.checkbox-wrapper>input[type=checkbox]:disabled~span{color:#d9d9d9;cursor:default}.checkbox-wrapper .check-box-inner{flex:0 0 auto}.checkbox-wrapper>span{flex:1 1 auto;font-weight:400;font-size:.875rem;padding-left:8px;padding-right:6px;align-self:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;color:#201f1e}.tooltip-color{background-color:#fff;color:#323130;border-color:rgba(204,204,204,.8);outline:none}.tooltip-wrapper{position:fixed;word-wrap:break-word;height:auto;text-align:left;z-index:250;min-width:75px;max-width:480px;min-height:32px;border-radius:2px;box-shadow:0 3.2px 7.2px 0 rgba(0,0,0,.13),0 .6px 1.8px 0 rgba(0,0,0,.11);transition:visibility 0s linear 120ms,opacity 120ms ease}.tooltip-wrapper>.tooltip-pointer{box-sizing:border-box;box-shadow:0 5px 15px 2px rgba(0,0,0,.3);border:1px solid #fff;z-index:-1;width:16px;height:16px;position:absolute;left:calc(50% - 8px);bottom:-8px;transform:rotate(-45deg);transform-origin:center}.tooltip-wrapper>.tooltip-curtain{position:absolute;width:100%;height:100%;z-index:-1}.tooltip-wrapper>.tooltip-content{font-size:.8125rem;line-height:1rem;white-space:normal;overflow:auto;margin:8px;height:calc(100% - 16px);-moz-user-select:none;user-select:none;-webkit-user-select:none}
|
62
index.html
62
index.html
@ -1,42 +1,38 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
|
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>UI Lib</title>
|
<title>UI Lib</title>
|
||||||
<script type="module" src="/main.js"></script>
|
<script type="module" src="/main.js"></script>
|
||||||
</head>
|
<script src="/dist/ui.min.js"></script>
|
||||||
<body>
|
</head>
|
||||||
<div id="app">
|
|
||||||
<div>
|
<body>
|
||||||
<a
|
<div id="directory">
|
||||||
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript"
|
<ul>
|
||||||
target="_blank"
|
<li class="title">lib-ui</li>
|
||||||
>
|
<li>
|
||||||
<img id="js-logo" class="logo vanilla" alt="JavaScript logo" />
|
<ol>
|
||||||
</a>
|
<li data-page="lib/ui/icon.html">icon</li>
|
||||||
<div class="card">
|
<li data-page="lib/ui/checkbox.html">checkbox</li>
|
||||||
<div id="create-icon" class="app-module">
|
<li data-page="lib/ui/tooltip.html">tooltip</li>
|
||||||
<div>
|
</ol>
|
||||||
<svg data-type="fa-solid" data-id="user-edit"></svg>
|
</li>
|
||||||
|
<li class="title">lib-utility</li>
|
||||||
|
<li>
|
||||||
|
<ol>
|
||||||
|
<li data-page="lib/utility/cookie.html">cookie</li>
|
||||||
|
<li data-page="lib/utility/lgres.html">lgres</li>
|
||||||
|
<li data-page="lib/utility/request.html">request</li>
|
||||||
|
<li data-page="lib/utility/strings.html">strings</li>
|
||||||
|
</ol>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="container"></div>
|
||||||
<div id="create-checkbox" class="app-module">
|
</body>
|
||||||
<label data-checkbox data-label="Case 1"></label>
|
|
||||||
</div>
|
|
||||||
<div id="buttons" class="app-module">
|
|
||||||
<button
|
|
||||||
data-title-lgid="editContacts"
|
|
||||||
data-lgid="editContacts"
|
|
||||||
title="Edit Contact"
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
<button id="button-fetch" title="Fetch">Fetch</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
@ -3,10 +3,13 @@ import { createCheckbox, resolveCheckbox } from "./ui/checkbox";
|
|||||||
import { setTooltip, resolveTooltip } from "./ui/tooltip";
|
import { setTooltip, resolveTooltip } from "./ui/tooltip";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
// icon
|
||||||
createIcon,
|
createIcon,
|
||||||
resolveIcon,
|
resolveIcon,
|
||||||
|
// checkbox
|
||||||
createCheckbox,
|
createCheckbox,
|
||||||
resolveCheckbox,
|
resolveCheckbox,
|
||||||
|
// tooltip
|
||||||
setTooltip,
|
setTooltip,
|
||||||
resolveTooltip
|
resolveTooltip
|
||||||
}
|
}
|
||||||
|
13
lib/ui/checkbox.d.ts
vendored
13
lib/ui/checkbox.d.ts
vendored
@ -1,2 +1,13 @@
|
|||||||
export function createCheckbox(opts: any): HTMLElement
|
interface CheckboxOptions {
|
||||||
|
type?: string;
|
||||||
|
label?: string;
|
||||||
|
checked?: boolean;
|
||||||
|
isImage?: boolean;
|
||||||
|
imageHeight?: Number;
|
||||||
|
checkedNode?: HTMLElement;
|
||||||
|
uncheckedNode?: HTMLElement;
|
||||||
|
onchange?: (this: HTMLInputElement, ev: Event) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCheckbox(opts?: CheckboxOptions): HTMLElement
|
||||||
export function resolveCheckbox(container: HTMLElement): HTMLElement
|
export function resolveCheckbox(container: HTMLElement): HTMLElement
|
93
lib/ui/checkbox.html
Normal file
93
lib/ui/checkbox.html
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<div>
|
||||||
|
<h1>checkbox</h1>
|
||||||
|
<hr />
|
||||||
|
<p>
|
||||||
|
创建一个统一样式的复选框元素,或者解析转换页面上特定类型的 label
|
||||||
|
标签为复选框元素。
|
||||||
|
</p>
|
||||||
|
<h2>createCheckbox</h2>
|
||||||
|
<code>function createCheckbox(opts?: CheckboxOptions): HTMLElement</code>
|
||||||
|
<h3>opts?: CheckboxOptions</h3>
|
||||||
|
<p>
|
||||||
|
复选框初始参数,结构为
|
||||||
|
<pre>interface CheckboxOptions {
|
||||||
|
type?: string;
|
||||||
|
label?: string;
|
||||||
|
checked?: boolean;
|
||||||
|
isImage?: boolean;
|
||||||
|
imageHeight?: Number;
|
||||||
|
checkedNode?: HTMLElement;
|
||||||
|
uncheckedNode?: HTMLElement;
|
||||||
|
onchange?: (this: HTMLInputElement, ev: Event) => any;
|
||||||
|
}</pre>
|
||||||
|
</p>
|
||||||
|
<h3>type?: string</h3>
|
||||||
|
<p>
|
||||||
|
复选框图标的样式,可选值目前有 <code>fa-regular</code>、<code>fa-light</code>、<code>fa-solid</code>
|
||||||
|
</p>
|
||||||
|
<h3>label?: string</h3>
|
||||||
|
<p>
|
||||||
|
复选框的标签文本
|
||||||
|
</p>
|
||||||
|
<h3>checked?: boolean</h3>
|
||||||
|
<p>
|
||||||
|
初始是否选中
|
||||||
|
</p>
|
||||||
|
<h3>isImage?: boolean</h3>
|
||||||
|
<p>
|
||||||
|
是否为图片复选框
|
||||||
|
</p>
|
||||||
|
<h3>imageHeight?: Number</h3>
|
||||||
|
<p>
|
||||||
|
为图片复选框时的图片限制高度
|
||||||
|
</p>
|
||||||
|
<h3>checkedNode?: HTMLElement</h3>
|
||||||
|
<p>
|
||||||
|
为图片复选框时的选中时显示的元素
|
||||||
|
</p>
|
||||||
|
<h3>uncheckedNode?: HTMLElement</h3>
|
||||||
|
<p>
|
||||||
|
为图片复选框时的未选中时显示的元素
|
||||||
|
</p>
|
||||||
|
<h3>onchange?: (this: HTMLInputElement, ev: Event) => any</h3>
|
||||||
|
<p>
|
||||||
|
复选框改变时触发的事件
|
||||||
|
</p>
|
||||||
|
<h2>resolveCheckbox</h2>
|
||||||
|
<code>function resolveCheckbox(container: HTMLElement): HTMLElement</code>
|
||||||
|
<h3>container: HTMLElement</h3>
|
||||||
|
<p>
|
||||||
|
将把此 HTML 元素下的所有 <code>label[data-checkbox]</code> 元素解析为复选框,<code>[data-id]</code> 为复选框元素的 id,包含
|
||||||
|
<code>[data-checked]</code> 时复选框默认选中。</p>
|
||||||
|
<p>当该元素无子元素时,<code>[data-type]</code> 同上述参数中的 <code>type?: string</code>,<code>[data-label]</code> 同上述参数中的
|
||||||
|
<code>label?: string</code>。
|
||||||
|
</p>
|
||||||
|
<p>当该元素有子元素时,解析为图片复选框,class 为 <code>checked</code>、<code>unchecked</code> 的子元素将分别在选中与未选中时显示。</p>
|
||||||
|
<h2>示例</h2>
|
||||||
|
<pre>
|
||||||
|
<div id="checkbox-sample">
|
||||||
|
<label data-checkbox data-type="fa-light" data-label="Checkbox Light"></label>
|
||||||
|
<label data-checkbox data-checked data-label="Checkbox Regular"></label>
|
||||||
|
<label data-checkbox data-type="fa-solid" data-label="Checkbox Solid"></label>
|
||||||
|
<label data-checkbox>
|
||||||
|
<code class="checked">Checked</code>
|
||||||
|
<code class="unchecked">Unchecked</code>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
window["lib-ui"].resolveCheckbox(document.querySelector("#checkbox-sample"));
|
||||||
|
</script></pre>
|
||||||
|
<div id="checkbox-sample">
|
||||||
|
<label data-checkbox data-type="fa-light" data-label="Checkbox Light"></label>
|
||||||
|
<label data-checkbox data-checked data-label="Checkbox Regular"></label>
|
||||||
|
<label data-checkbox data-type="fa-solid" data-label="Checkbox Solid"></label>
|
||||||
|
<label data-checkbox>
|
||||||
|
<code class="checked">Checked</code>
|
||||||
|
<code class="unchecked">Unchecked</code>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window["lib-ui"].resolveCheckbox(document.querySelector("#checkbox-sample"));
|
||||||
|
</script>
|
||||||
|
</div>
|
@ -18,6 +18,9 @@ function createCheckbox(opts) {
|
|||||||
container.className = 'checkbox-wrapper';
|
container.className = 'checkbox-wrapper';
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.setAttribute('type', 'checkbox');
|
input.setAttribute('type', 'checkbox');
|
||||||
|
if (opts.checked === true) {
|
||||||
|
input.checked = true;
|
||||||
|
}
|
||||||
if (typeof opts.onchange === 'function') {
|
if (typeof opts.onchange === 'function') {
|
||||||
input.addEventListener('change', opts.onchange);
|
input.addEventListener('change', opts.onchange);
|
||||||
}
|
}
|
||||||
@ -64,6 +67,13 @@ function resolveCheckbox(container) {
|
|||||||
box.removeAttribute('data-label');
|
box.removeAttribute('data-label');
|
||||||
}
|
}
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
|
const id = box.getAttribute('data-id');
|
||||||
|
if (id != null && id.length > 0) {
|
||||||
|
input.id = id;
|
||||||
|
}
|
||||||
|
if (box.getAttribute('data-checked') != null) {
|
||||||
|
input.checked = true;
|
||||||
|
}
|
||||||
input.setAttribute('type', 'checkbox');
|
input.setAttribute('type', 'checkbox');
|
||||||
box.insertBefore(input, box.firstChild);
|
box.insertBefore(input, box.firstChild);
|
||||||
}
|
}
|
||||||
|
46
lib/ui/icon.html
Normal file
46
lib/ui/icon.html
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<div>
|
||||||
|
<h1>icon</h1>
|
||||||
|
<hr />
|
||||||
|
<p>
|
||||||
|
创建一个 svg 矢量图标元素,或者解析转换页面上特定类型的 svg
|
||||||
|
标签到指定的图标元素。
|
||||||
|
</p>
|
||||||
|
<h2>createIcon</h2>
|
||||||
|
<code>function createIcon(type: string, id: string): SVGElement</code>
|
||||||
|
<h3>type: string</h3>
|
||||||
|
<p>
|
||||||
|
图标类型,可选值目前有 <code>fa-regular</code>、<code>fa-light</code>、<code>fa-solid</code>
|
||||||
|
</p>
|
||||||
|
<h3>id: string</h3>
|
||||||
|
<p>
|
||||||
|
图形 id,例如
|
||||||
|
<code>user-edit</code>、<code>address-card</code>、<code>frog</code>……
|
||||||
|
</p>
|
||||||
|
<h2>resolveIcon</h2>
|
||||||
|
<code>function resolveIcon(container: HTMLElement): HTMLElement</code>
|
||||||
|
<h3>container: HTMLElement</h3>
|
||||||
|
<p>
|
||||||
|
将把此 HTML 元素下的所有 <code>svg[data-id]</code> 元素解析为图标,<code>[data-id]</code>
|
||||||
|
同上述 <code>id: string</code>,<code>[data-type]</code> 同上述
|
||||||
|
<code>type: string</code>
|
||||||
|
</p>
|
||||||
|
<h2>示例</h2>
|
||||||
|
<pre>
|
||||||
|
<div id="icon-sample">
|
||||||
|
<svg data-id="address-card" data-type="fa-regular"></svg>
|
||||||
|
<svg data-id="user-edit" data-type="fa-light"></svg>
|
||||||
|
<svg data-id="frog" data-type="fa-solid"></svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
window["lib-ui"].resolveIcon(document.querySelector("#icon-sample"));
|
||||||
|
</script></pre>
|
||||||
|
<div id="icon-sample">
|
||||||
|
<svg data-id="address-card" data-type="fa-regular"></svg>
|
||||||
|
<svg data-id="user-edit" data-type="fa-light"></svg>
|
||||||
|
<svg data-id="frog" data-type="fa-solid"></svg>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window["lib-ui"].resolveIcon(document.querySelector("#icon-sample"));
|
||||||
|
</script>
|
||||||
|
</div>
|
44
lib/ui/tooltip.html
Normal file
44
lib/ui/tooltip.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<div>
|
||||||
|
<h1>tooltip</h1>
|
||||||
|
<hr />
|
||||||
|
<p>
|
||||||
|
给某个元素或者页面上含有 title 属性的元素设置一个统一样式的 tooltip。
|
||||||
|
</p>
|
||||||
|
<h2>setTooltip</h2>
|
||||||
|
<code>function setTooltip(container: HTMLElement, content: string | HTMLElement): void</code>
|
||||||
|
<h3>container: HTMLElement</h3>
|
||||||
|
<p>
|
||||||
|
要设置 tooltip 的元素
|
||||||
|
</p>
|
||||||
|
<h3>content: string | HTMLElement</h3>
|
||||||
|
<p>
|
||||||
|
要设置的 tooltip 内容,允许为字符串或者 HTML 元素
|
||||||
|
</p>
|
||||||
|
<h2>resolveTooltip</h2>
|
||||||
|
<code>function resolveTooltip(container: HTMLElement): HTMLElement</code>
|
||||||
|
<h3>container: HTMLElement</h3>
|
||||||
|
<p>
|
||||||
|
给此元素下的所有含有 title 属性的子元素设置成统一样式的 tooltip
|
||||||
|
</p>
|
||||||
|
<h2>示例</h2>
|
||||||
|
<pre>
|
||||||
|
<div id="tooltip-sample">
|
||||||
|
<blockquote title="From MDN Website">To send an HTTP request, create an XMLHttpRequest object, open a URL, and
|
||||||
|
send the request. After the transaction completes, the object will contain useful information such as the
|
||||||
|
response body and the HTTP status of the result.</blockquote>
|
||||||
|
<button title="Test to send request through XMLHttpRequest.">Test</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
window["lib-ui"].resolveCheckbox(document.querySelector("#checkbox-sample"));
|
||||||
|
</script></pre>
|
||||||
|
<div id="tooltip-sample">
|
||||||
|
<blockquote title="From MDN Website">To send an HTTP request, create an XMLHttpRequest object, open a URL, and
|
||||||
|
send the request. After the transaction completes, the object will contain useful information such as the
|
||||||
|
response body and the HTTP status of the result.</blockquote>
|
||||||
|
<button title="Test to send request through XMLHttpRequest.">Test</button>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window["lib-ui"].resolveTooltip(document.querySelector("#tooltip-sample"));
|
||||||
|
</script>
|
||||||
|
</div>
|
@ -23,8 +23,16 @@ function setTooltip(container, content) {
|
|||||||
container.addEventListener('mouseenter', () => {
|
container.addEventListener('mouseenter', () => {
|
||||||
tid && clearTimeout(tid);
|
tid && clearTimeout(tid);
|
||||||
tid = setTimeout(() => {
|
tid = setTimeout(() => {
|
||||||
const left = container.offsetLeft + container.offsetWidth / 2 - wrapper.offsetWidth / 2;
|
let left = container.offsetLeft;
|
||||||
const top = container.offsetTop - wrapper.offsetHeight - 14;
|
let top = container.offsetTop;
|
||||||
|
let parent = container.parentElement;
|
||||||
|
while (parent != null) {
|
||||||
|
left -= parent.scrollLeft;
|
||||||
|
top -= parent.scrollTop;
|
||||||
|
parent = parent.parentElement;
|
||||||
|
}
|
||||||
|
left -= wrapper.offsetWidth / 2 - container.offsetWidth / 2;
|
||||||
|
top -= wrapper.offsetHeight + 14;
|
||||||
wrapper.style.left = `${left}px`;
|
wrapper.style.left = `${left}px`;
|
||||||
wrapper.style.top = `${top}px`;
|
wrapper.style.top = `${top}px`;
|
||||||
wrapper.style.visibility = 'visible';
|
wrapper.style.visibility = 'visible';
|
||||||
|
@ -4,15 +4,19 @@ import { get, post, upload } from "./utility/request";
|
|||||||
import { nullOrEmpty, contains, endsWith, padStart } from "./utility/strings";
|
import { nullOrEmpty, contains, endsWith, padStart } from "./utility/strings";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
// cookie
|
||||||
getCookie,
|
getCookie,
|
||||||
setCookie,
|
setCookie,
|
||||||
deleteCookie,
|
deleteCookie,
|
||||||
|
// lgres
|
||||||
init,
|
init,
|
||||||
r,
|
r,
|
||||||
lang,
|
lang,
|
||||||
|
// request
|
||||||
get,
|
get,
|
||||||
post,
|
post,
|
||||||
upload,
|
upload,
|
||||||
|
// strings
|
||||||
nullOrEmpty,
|
nullOrEmpty,
|
||||||
contains,
|
contains,
|
||||||
endsWith,
|
endsWith,
|
||||||
|
7
lib/utility/lgres.d.ts
vendored
7
lib/utility/lgres.d.ts
vendored
@ -1,4 +1,9 @@
|
|||||||
export function init(dom?: HTMLElement, ahead?: | { callback?: (result: any) => void }): Promise<any>
|
interface LgresOptions {
|
||||||
|
template?: string,
|
||||||
|
callback?: (result: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function init(dom?: HTMLElement, options?: LgresOptions): Promise<any>
|
||||||
export function r(key: string, defaultValue?: any): any
|
export function r(key: string, defaultValue?: any): any
|
||||||
export const lang: {
|
export const lang: {
|
||||||
get current(): string,
|
get current(): string,
|
||||||
|
@ -45,23 +45,24 @@ function getStorageKey(lgid) {
|
|||||||
return `res_${lgid}`;
|
return `res_${lgid}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doRefreshLgres() {
|
async function doRefreshLgres(template) {
|
||||||
|
template ??= '';
|
||||||
const lgid = getCurrentLgId();
|
const lgid = getCurrentLgId();
|
||||||
const r = await get(`language/${lgid}/res.json`);
|
const r = await get(`language/${lgid}${template}`);
|
||||||
const dict = await r.json();
|
const dict = await r.json();
|
||||||
localStorage.setItem(getStorageKey(lgid), JSON.stringify(dict));
|
localStorage.setItem(getStorageKey(lgid), JSON.stringify(dict));
|
||||||
cache = dict;
|
cache = dict;
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshLgres(lgres) {
|
async function refreshLgres(template, lgres) {
|
||||||
if (lgres == null || typeof consts === 'undefined') {
|
if (lgres == null || typeof consts === 'undefined') {
|
||||||
return await doRefreshLgres();
|
return await doRefreshLgres(template);
|
||||||
}
|
}
|
||||||
const ver = Number(consts.resver);
|
const ver = Number(consts.resver);
|
||||||
if (isNaN(lgres.ver) || isNaN(ver) || ver > lgres.ver) {
|
if (isNaN(lgres.ver) || isNaN(ver) || ver > lgres.ver) {
|
||||||
console.log(`found new language res version: ${lgres.ver} => ${ver}`);
|
console.log(`found new language res version: ${lgres.ver} => ${ver}`);
|
||||||
return await doRefreshLgres();
|
return await doRefreshLgres(template);
|
||||||
}
|
}
|
||||||
cache = lgres;
|
cache = lgres;
|
||||||
return lgres;
|
return lgres;
|
||||||
@ -97,42 +98,40 @@ function applyLanguage(dom, result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function init(dom, ahead) {
|
async function init(dom, options) {
|
||||||
|
options ??= {};
|
||||||
const lgid = getCurrentLgId();
|
const lgid = getCurrentLgId();
|
||||||
let lgres = localStorage.getItem(getStorageKey(lgid));
|
let lgres = localStorage.getItem(getStorageKey(lgid));
|
||||||
let result;
|
let result;
|
||||||
if (lgres != null) {
|
if (lgres != null) {
|
||||||
try {
|
try {
|
||||||
lgres = JSON.parse(lgres);
|
lgres = JSON.parse(lgres);
|
||||||
result = await refreshLgres(lgres);
|
result = await refreshLgres(options.template, lgres);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('error while parsing lgres, try refresh ...', e);
|
console.error('error while parsing lgres, try refresh ...', e);
|
||||||
result = await refreshLgres();
|
result = await refreshLgres(options.template);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result = await refreshLgres();
|
result = await refreshLgres(options.template);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (ahead != null) {
|
|
||||||
// not in defer mode
|
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
let tid = setTimeout(() => reject('timeout'), 30000);
|
let tid = setTimeout(() => reject('timeout'), 30000);
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
clearTimeout(tid);
|
clearTimeout(tid);
|
||||||
tid = void 0;
|
tid = void 0;
|
||||||
if (typeof ahead.callback === 'function') {
|
if (typeof options.callback === 'function') {
|
||||||
ahead.callback(result);
|
options.callback(result);
|
||||||
}
|
}
|
||||||
applyLanguage(dom, result);
|
applyLanguage(dom, result);
|
||||||
resolve(result);
|
resolve(result);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (typeof ahead.callback === 'function') {
|
if (typeof options.callback === 'function') {
|
||||||
ahead.callback(result);
|
options.callback(result);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
applyLanguage(dom, result);
|
applyLanguage(dom, result);
|
||||||
return result;
|
return result;
|
||||||
|
33
main.js
33
main.js
@ -1,8 +1,7 @@
|
|||||||
import './css/ui.min.css'
|
import './css/ui.min.css'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
// import javascriptLogo from './javascript.svg'
|
// import javascriptLogo from './javascript.svg'
|
||||||
import { resolveCheckbox, resolveIcon, resolveTooltip } from './lib/ui'
|
import { get } from './lib/utility'
|
||||||
import { init, r, lang, get, post, upload } from './lib/utility'
|
|
||||||
|
|
||||||
// document.querySelector('#js-logo').src = javascriptLogo
|
// document.querySelector('#js-logo').src = javascriptLogo
|
||||||
|
|
||||||
@ -11,7 +10,36 @@ window.consts = {
|
|||||||
resver: 20230329
|
resver: 20230329
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function navigate(page) {
|
||||||
|
get(page, {
|
||||||
|
accept: 'text/html'
|
||||||
|
})
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
const range = document.createRange();
|
||||||
|
range.selectNode(document.body);
|
||||||
|
const doc = range.createContextualFragment(html);
|
||||||
|
document.querySelector('#container').replaceChildren(doc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('#directory').addEventListener('click', (ev) => {
|
||||||
|
const page = ev.target.getAttribute('data-page');
|
||||||
|
if (typeof page === 'string') {
|
||||||
|
location.hash = page;
|
||||||
|
navigate(page);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let page = location.hash;
|
||||||
|
if (page.length > 1) {
|
||||||
|
page = page.substring(1);
|
||||||
|
navigate(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
init(null, {
|
init(null, {
|
||||||
|
template: '/res.json',
|
||||||
callback: (result) => console.log(result)
|
callback: (result) => console.log(result)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// document.querySelector('#create-icon').appendChild(createIcon('fa-solid', 'user-edit'))
|
// document.querySelector('#create-icon').appendChild(createIcon('fa-solid', 'user-edit'))
|
||||||
@ -35,3 +63,4 @@ init(null, {
|
|||||||
.then(blob => document.querySelector('#js-logo').src = URL.createObjectURL(blob));
|
.then(blob => document.querySelector('#js-logo').src = URL.createObjectURL(blob));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
150
style.css
150
style.css
@ -13,6 +13,46 @@
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
|
|
||||||
|
--border-color: #ccc;
|
||||||
|
--hover-color: #666;
|
||||||
|
--mono-font-family: 'FantasqueSansMono NFM', 'Cascadia Code', 'PT Mono', Consolas, 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
code, kbd, pre, samp {
|
||||||
|
font-family: var(--mono-font-family);
|
||||||
|
background-color: var(--hover-color);
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-size: .875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 + code {
|
||||||
|
margin-left: 70px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 + code::before {
|
||||||
|
content: '签名:';
|
||||||
|
position: absolute;
|
||||||
|
margin-left: -70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-family: var(--mono-font-family);
|
||||||
|
font-size: 1em;
|
||||||
|
margin-left: 10px;
|
||||||
|
/* font-weight: bold; */
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 ~ p {
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@ -20,50 +60,11 @@ a {
|
|||||||
color: #646cff;
|
color: #646cff;
|
||||||
text-decoration: inherit;
|
text-decoration: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: #535bf2;
|
color: #535bf2;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
place-items: center;
|
|
||||||
min-width: 320px;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 3.2em;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app {
|
|
||||||
max-width: 1280px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
height: 6em;
|
|
||||||
padding: 1.5em;
|
|
||||||
will-change: filter;
|
|
||||||
}
|
|
||||||
.logo:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #646cffaa);
|
|
||||||
}
|
|
||||||
.logo.vanilla:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #f7df1eaa);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.read-the-docs {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
button {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
@ -75,14 +76,74 @@ button {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: border-color 0.25s;
|
transition: border-color 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
border-color: #646cff;
|
border-color: #646cff;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:focus,
|
button:focus,
|
||||||
button:focus-visible {
|
button:focus-visible {
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.2em;
|
||||||
|
line-height: 1.1;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#directory {
|
||||||
|
width: 200px;
|
||||||
|
padding: 2rem;
|
||||||
|
border-right: 1px solid var(--border-color);
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#directory>ul {
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#directory>ul>li {
|
||||||
|
list-style: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#directory>ul>li.title {
|
||||||
|
margin: 20px 0 6px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#directory ol {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#directory ol>li {
|
||||||
|
padding: 0 6px;
|
||||||
|
list-style-position: inside;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#directory ol>li:hover {
|
||||||
|
background-color: var(--hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container>div {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.app-module {
|
.app-module {
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
}
|
}
|
||||||
@ -91,6 +152,7 @@ button:focus-visible {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#create-icon svg {
|
#create-icon svg {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -101,11 +163,13 @@ button:focus-visible {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-wrapper .check-box-inner {
|
.checkbox-wrapper .check-box-inner {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
}
|
}
|
||||||
.checkbox-wrapper > span {
|
|
||||||
|
.checkbox-wrapper>span {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,10 +177,14 @@ button:focus-visible {
|
|||||||
:root {
|
:root {
|
||||||
color: #213547;
|
color: #213547;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
|
--border-color: #666;
|
||||||
|
--hover-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: #747bff;
|
color: #747bff;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user