Compare commits
10 commits
6e13ed40da
...
4627f4eb29
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4627f4eb29 | ||
|
|
2dbdb7a761 | ||
|
|
ea92ab2bc2 | ||
|
|
210d169289 | ||
|
|
6f7ec55091 | ||
|
|
415542b8c3 | ||
|
|
e53e84b31f | ||
|
|
1c67bc8cbf | ||
|
|
78ae418dc0 | ||
|
|
dafac086ef |
15 changed files with 931 additions and 145 deletions
|
|
@ -22,6 +22,7 @@
|
||||||
"<all_urls>"
|
"<all_urls>"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
|
"src/js/main.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,47 @@
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
--base_bg_color: 10;
|
||||||
|
--base_bg_color_range: 10;
|
||||||
|
--base_bg_saturation: 10%;
|
||||||
|
|
||||||
|
--color_text_white: #fff;
|
||||||
|
--color_text_gray: #999;
|
||||||
|
--surface-000: var(--s000);
|
||||||
|
--surface-100: var(--s100);
|
||||||
|
--surface-200: color-mix(in srgb, var(--s200), transparent 95%);
|
||||||
|
--surface-300: color-mix(in srgb, var(--s300), transparent 85%);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
overflow-y: 0;
|
overflow-y: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: #000;
|
background: var(--surface-000) !important;
|
||||||
}
|
}
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
/* background: #f002 !important; */
|
||||||
}
|
}
|
||||||
|
|
||||||
#background {
|
#background {
|
||||||
|
transition: opacity .15s;
|
||||||
|
--base_bg_color_tint_p: calc(var(--base_bg_color) + var(--base_bg_color_range));
|
||||||
|
--base_bg_color_tint_n: calc(var(--base_bg_color) - var(--base_bg_color_range));
|
||||||
|
|
||||||
|
--c-0-base: hsla(var(--base_bg_color), calc(var(--base_bg_saturation) - 9%), calc(var(--base_bg_brightness) + 2%), 1);
|
||||||
|
--c-1-base: hsla(var(--base_bg_color_tint_p), calc(var(--base_bg_saturation) - 8%), calc(var(--base_bg_brightness) + 2%), 1);
|
||||||
|
--c-3-base: hsla(var(--base_bg_color_tint_n), calc(var(--base_bg_saturation) - 6%), calc(var(--base_bg_brightness) + 26%), 1);
|
||||||
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
#background.changing {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* https://www.mshr.app/mesh/1727910717365 */
|
/* https://www.mshr.app/mesh/1727910717365 */
|
||||||
@keyframes hero-gradient-animation {
|
@keyframes hero-gradient-animation {
|
||||||
|
|
@ -26,8 +50,10 @@ body {
|
||||||
--s-start-0: 9%;
|
--s-start-0: 9%;
|
||||||
--s-end-0: 55%;
|
--s-end-0: 55%;
|
||||||
--y-0: 80%;
|
--y-0: 80%;
|
||||||
--c-0: hsla(266.99999999999983, 1%, 12%, 1);
|
--c-0: var(--c-0-base);
|
||||||
--c-1: hsla(335.9999999999997, 2%, 22%, 1);
|
--c-1: var(--c-1-base);
|
||||||
|
--c-2: hsla(0,0%, 0%, 0.49);
|
||||||
|
--c-3: var(--c-3-base);
|
||||||
--s-start-1: 5%;
|
--s-start-1: 5%;
|
||||||
--s-end-1: 72%;
|
--s-end-1: 72%;
|
||||||
--y-1: 24%;
|
--y-1: 24%;
|
||||||
|
|
@ -35,11 +61,9 @@ body {
|
||||||
--s-start-2: 5%;
|
--s-start-2: 5%;
|
||||||
--s-end-2: 52%;
|
--s-end-2: 52%;
|
||||||
--y-2: 82%;
|
--y-2: 82%;
|
||||||
--c-2: hsla(53.999999999999886, 0%, 0%, 0.49);
|
|
||||||
--x-2: 13%;
|
--x-2: 13%;
|
||||||
--s-start-3: 13%;
|
--s-start-3: 13%;
|
||||||
--s-end-3: 68%;
|
--s-end-3: 68%;
|
||||||
--c-3: hsla(299, 4%, 36%, 1);
|
|
||||||
--x-3: 24%;
|
--x-3: 24%;
|
||||||
--y-3: 7%;
|
--y-3: 7%;
|
||||||
}
|
}
|
||||||
|
|
@ -49,8 +73,10 @@ body {
|
||||||
--s-start-0: 9%;
|
--s-start-0: 9%;
|
||||||
--s-end-0: 55%;
|
--s-end-0: 55%;
|
||||||
--y-0: 94%;
|
--y-0: 94%;
|
||||||
--c-0: hsla(266.99999999999943, 0%, 12%, 1);
|
--c-0: var(--c-0-base);
|
||||||
--c-1: hsla(0, 4%, 19%, 1);
|
--c-1: var(--c-1-base);
|
||||||
|
--c-2: hsla(0,0%, 0%, 0.49);
|
||||||
|
--c-3: var(--c-3-base);
|
||||||
--s-start-1: 5%;
|
--s-start-1: 5%;
|
||||||
--s-end-1: 72%;
|
--s-end-1: 72%;
|
||||||
--y-1: 25%;
|
--y-1: 25%;
|
||||||
|
|
@ -58,11 +84,9 @@ body {
|
||||||
--s-start-2: 5%;
|
--s-start-2: 5%;
|
||||||
--s-end-2: 52%;
|
--s-end-2: 52%;
|
||||||
--y-2: 20%;
|
--y-2: 20%;
|
||||||
--c-2: hsla(54.000000000000036, 0%, 0%, 0.49);
|
|
||||||
--x-2: 98%;
|
--x-2: 98%;
|
||||||
--s-start-3: 13%;
|
--s-start-3: 13%;
|
||||||
--s-end-3: 68%;
|
--s-end-3: 68%;
|
||||||
--c-3: hsla(298.99999999999994, 3%, 41%, 1);
|
|
||||||
--x-3: 95%;
|
--x-3: 95%;
|
||||||
--y-3: 92%;
|
--y-3: 92%;
|
||||||
}
|
}
|
||||||
|
|
@ -95,13 +119,13 @@ body {
|
||||||
@property --c-0 {
|
@property --c-0 {
|
||||||
syntax: '<color>';
|
syntax: '<color>';
|
||||||
inherits: false;
|
inherits: false;
|
||||||
initial-value: hsla(266.99999999999983, 1%, 12%, 1)
|
initial-value: hsla(0,0,0,0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@property --c-1 {
|
@property --c-1 {
|
||||||
syntax: '<color>';
|
syntax: '<color>';
|
||||||
inherits: false;
|
inherits: false;
|
||||||
initial-value: hsla(335.9999999999997, 2%, 22%, 1)
|
initial-value: hsla(0,0,0,0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@property --s-start-1 {
|
@property --s-start-1 {
|
||||||
|
|
@ -149,7 +173,7 @@ body {
|
||||||
@property --c-2 {
|
@property --c-2 {
|
||||||
syntax: '<color>';
|
syntax: '<color>';
|
||||||
inherits: false;
|
inherits: false;
|
||||||
initial-value: hsla(53.999999999999886, 0%, 0%, 0.49)
|
initial-value: hsla(0,0,0,0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@property --x-2 {
|
@property --x-2 {
|
||||||
|
|
@ -173,7 +197,7 @@ body {
|
||||||
@property --c-3 {
|
@property --c-3 {
|
||||||
syntax: '<color>';
|
syntax: '<color>';
|
||||||
inherits: false;
|
inherits: false;
|
||||||
initial-value: hsla(299, 4%, 36%, 1)
|
initial-value: hsla(0,0,0,0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@property --x-3 {
|
@property --x-3 {
|
||||||
|
|
@ -189,21 +213,40 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
#background {
|
#background {
|
||||||
|
background-color: var(--surface-000);
|
||||||
|
}
|
||||||
|
#background.default {
|
||||||
--x-0: 85%;
|
--x-0: 85%;
|
||||||
--y-0: 80%;
|
--y-0: 80%;
|
||||||
--c-0: hsla(266.99999999999983, 1%, 12%, 1);
|
--c-0: hsla(var(--base_bg_color), calc(var(--base_bg_saturation) - 9%), 12%, 1);
|
||||||
--c-1: hsla(335.9999999999997, 2%, 22%, 1);
|
--c-1: hsla(var(--base_bg_color_tint_p), calc(var(--base_bg_saturation) - 8%), 22%, 1);
|
||||||
--y-1: 24%;
|
--y-1: 24%;
|
||||||
--x-1: 60%;
|
--x-1: 60%;
|
||||||
--y-2: 82%;
|
--y-2: 82%;
|
||||||
--c-2: hsla(53.999999999999886, 0%, 0%, 0.49);
|
--c-2: hsla(0,0%, 0%, 0.49);
|
||||||
--x-2: 13%;
|
--x-2: 13%;
|
||||||
--c-3: hsla(299, 4%, 36%, 1);
|
--c-3: hsla(var(--base_bg_color_tint_n), calc(var(--base_bg_saturation) - 6%), 36%, 1);
|
||||||
--x-3: 24%;
|
--x-3: 24%;
|
||||||
--y-3: 7%;
|
--y-3: 7%;
|
||||||
|
background-color: var(--surface-000);
|
||||||
|
background-image:
|
||||||
|
radial-gradient(
|
||||||
|
circle at var(--x-0) var(--y-0), var(--c-0) var(--s-start-0),
|
||||||
|
transparent var(--s-end-0)
|
||||||
|
),
|
||||||
|
radial-gradient(
|
||||||
|
circle at var(--x-1) var(--y-1), var(--c-1) var(--s-start-1),
|
||||||
|
transparent var(--s-end-1)
|
||||||
|
),
|
||||||
|
radial-gradient(
|
||||||
|
circle at var(--x-2) var(--y-2), var(--c-2) var(--s-start-2),
|
||||||
|
transparent var(--s-end-2)
|
||||||
|
),
|
||||||
|
radial-gradient(
|
||||||
|
circle at var(--x-3) var(--y-3), var(--c-3) var(--s-start-3),
|
||||||
|
transparent var(--s-end-3)
|
||||||
|
)
|
||||||
;
|
;
|
||||||
background-color: hsla(0, 0%, 0%, 1);
|
|
||||||
background-image: radial-gradient(circle at var(--x-0) var(--y-0), var(--c-0) var(--s-start-0), transparent var(--s-end-0)), radial-gradient(circle at var(--x-1) var(--y-1), var(--c-1) var(--s-start-1), transparent var(--s-end-1)), radial-gradient(circle at var(--x-2) var(--y-2), var(--c-2) var(--s-start-2), transparent var(--s-end-2)), radial-gradient(circle at var(--x-3) var(--y-3), var(--c-3) var(--s-start-3), transparent var(--s-end-3));
|
|
||||||
animation: hero-gradient-animation 10s linear infinite alternate;
|
animation: hero-gradient-animation 10s linear infinite alternate;
|
||||||
background-blend-mode: normal, normal, normal, normal;
|
background-blend-mode: normal, normal, normal, normal;
|
||||||
}
|
}
|
||||||
23
src/css/icons.css
Normal file
23
src/css/icons.css
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Material Symbols Rounded';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100 700;
|
||||||
|
src: url(./icons.woff2) format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-symbols-rounded {
|
||||||
|
font-family: 'Material Symbols Rounded';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-wrap: normal;
|
||||||
|
direction: ltr;
|
||||||
|
-webkit-font-feature-settings: 'liga';
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
BIN
src/css/icons.woff2
Normal file
BIN
src/css/icons.woff2
Normal file
Binary file not shown.
|
|
@ -26,11 +26,11 @@
|
||||||
#searcher > h1 {
|
#searcher > h1 {
|
||||||
font-family: 'Playfair Display';
|
font-family: 'Playfair Display';
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 70px;
|
font-size: 60px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
} #searcher > h1 > span {
|
} #searcher > h1 > span {
|
||||||
transition: background .2s;
|
transition: background .2s;
|
||||||
font-size: 33px;
|
font-size: 23px;
|
||||||
font-family: 'Fira Code';
|
font-family: 'Fira Code';
|
||||||
background: rgba(255,255,255,.1);
|
background: rgba(255,255,255,.1);
|
||||||
padding: 0 5px 0;
|
padding: 0 5px 0;
|
||||||
|
|
|
||||||
199
src/css/settings.css
Normal file
199
src/css/settings.css
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
.settings_visual_help {
|
||||||
|
z-index: 11;
|
||||||
|
position: fixed;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 100dvh;
|
||||||
|
width: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: end;
|
||||||
|
padding-left: 50px;
|
||||||
|
cursor: default;
|
||||||
|
>div {
|
||||||
|
cursor: pointer;
|
||||||
|
rotate: -90deg;
|
||||||
|
transform: translateY(98px) scale(.9);
|
||||||
|
transition: transform .2s;
|
||||||
|
gap: 7px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--surface-200);
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
border: solid 1px var(--surface-300);
|
||||||
|
border-bottom: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
>div:hover {
|
||||||
|
background: var(--surface-300);
|
||||||
|
}
|
||||||
|
>div:active {
|
||||||
|
background: var(--surface-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
span:not(.icon) {
|
||||||
|
text-wrap: nowrap;
|
||||||
|
font-family: var(--sans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.settings_visual_help:hover {
|
||||||
|
>div {
|
||||||
|
transform: translateY(58px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings.ov {
|
||||||
|
transform: translateX(34vw);
|
||||||
|
}
|
||||||
|
#settings {
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
z-index: 1001;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 20px;
|
||||||
|
gap: 10px;
|
||||||
|
height: calc(100dvh - 40px);
|
||||||
|
width: 30vw;
|
||||||
|
/* background: var(--surface-100); */
|
||||||
|
/* border-left: solid 1px var(--surface-300); */
|
||||||
|
border: none;
|
||||||
|
scrollbar-color: var(--surface-300) transparent;
|
||||||
|
|
||||||
|
>* {
|
||||||
|
transition: transform .3s, opacity .3s;
|
||||||
|
}
|
||||||
|
>.ov {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(34vw);
|
||||||
|
}
|
||||||
|
>.back {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
h2 {
|
||||||
|
font-family: var(--serif);
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
border-radius: 100px;
|
||||||
|
font-size: 25px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>.back:hover {
|
||||||
|
span {
|
||||||
|
background: var(--surface-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>h2, >h3 {
|
||||||
|
font-family: var(--serif);
|
||||||
|
font-weight: normal;
|
||||||
|
/* margin-bottom: 5px; */
|
||||||
|
}
|
||||||
|
>p {
|
||||||
|
font-family: var(--sans);
|
||||||
|
/* margin-bottom: 10px; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.action {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: fit-content;
|
||||||
|
background: var(--surface-200);
|
||||||
|
border: solid 1px var(--surface-300);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-radius: 100px;
|
||||||
|
padding: 5px 5px;
|
||||||
|
gap: 3px;
|
||||||
|
.icon {
|
||||||
|
--size: 17px;
|
||||||
|
font-size: var(--size) !important;
|
||||||
|
height: var(--size) !important;
|
||||||
|
width: var(--size) !important;
|
||||||
|
}
|
||||||
|
:not(.icon) {
|
||||||
|
font-family: var(--sans);
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
padding: 2px 3px 3px;
|
||||||
|
background: var(--surface-200);
|
||||||
|
border: solid 1px var(--surface-300);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
input, textarea {
|
||||||
|
border-radius: 20px;
|
||||||
|
background: var(--surface-200);
|
||||||
|
padding: 10px;
|
||||||
|
border: solid 1px var(--surface-300);
|
||||||
|
outline: none;
|
||||||
|
resize: vertical;
|
||||||
|
font-family: var(--monospace);
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
border-radius: 15px !important;
|
||||||
|
height: 100dvh;
|
||||||
|
min-height: 180px;
|
||||||
|
scrollbar-color: var(--color_text_white) transparent;
|
||||||
|
text-wrap-mode: wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
input::placeholder, textarea::placeholder {
|
||||||
|
color: var(--color_text_gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--surface-300)
|
||||||
|
}
|
||||||
|
.surface {
|
||||||
|
background: var(--surface-200);
|
||||||
|
border: solid 1px var(--surface-300);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
/* padding: 10px; */
|
||||||
|
border-radius: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.setting_entry:nth-child(n+2) {
|
||||||
|
border-top: solid 1px var(--surface-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setting_entry {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 6px 11px;
|
||||||
|
padding-right: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
.icon {
|
||||||
|
color: var(--color_text_gray);
|
||||||
|
}
|
||||||
|
:not(.icon) {
|
||||||
|
font-family: var(--sans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setting_entry:hover {
|
||||||
|
background: var(--surface-200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
#update {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
font-size: 13px;
|
|
||||||
font-family: 'Nunito';
|
|
||||||
background: rgba(255,255,255,.1);
|
|
||||||
border-radius: 100px;
|
|
||||||
margin: 10px;
|
|
||||||
display: flex;
|
|
||||||
z-index: 20;
|
|
||||||
transition: transform .5s;
|
|
||||||
} #update > * {
|
|
||||||
padding: 5px 10px;
|
|
||||||
} #update > a {
|
|
||||||
text-decoration: none;
|
|
||||||
background: rgba(255,255,255,.1);
|
|
||||||
border-radius: 100px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#update.hidden {
|
|
||||||
transform: translateY(40px);
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,9 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Home</title>
|
<title>Home</title>
|
||||||
<link rel="shortcut icon" href="icon.svg" type="image/x-icon">
|
<link rel="shortcut icon" href="icon.svg" type="image/x-icon">
|
||||||
|
<link rel="stylesheet" href="css/icons.css">
|
||||||
<link rel="stylesheet" href="css/global.css">
|
<link rel="stylesheet" href="css/global.css">
|
||||||
|
<link rel="stylesheet" href="css/settings.css">
|
||||||
<link rel="stylesheet" href="css/clock.css">
|
<link rel="stylesheet" href="css/clock.css">
|
||||||
<link rel="stylesheet" href="css/search.css">
|
<link rel="stylesheet" href="css/search.css">
|
||||||
<link rel="stylesheet" href="css/update.css">
|
<link rel="stylesheet" href="css/update.css">
|
||||||
|
|
@ -17,12 +19,15 @@
|
||||||
<p>Search via Google</p>
|
<p>Search via Google</p>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" id="search_ddd" autocorrect="off" autocomplete="off" autofocus>
|
<input type="text" id="search_ddd" autocorrect="off" autocomplete="off" autofocus>
|
||||||
<div id="update" class="hidden"><span>update available</span> <a href="https://github.com/ktnk-dev/LittleHome">download</a></div>
|
<div class="settings_visual_help">
|
||||||
<script src="js/abstract.js"></script>
|
</div>
|
||||||
|
<script src="lib/sinya.lib.js"></script>
|
||||||
|
<script src="lib/gicons.lib.js"></script>
|
||||||
<script src="js/storage.js"></script>
|
<script src="js/storage.js"></script>
|
||||||
<script src="js/bang.js"></script>
|
<script src="js/bang.js"></script>
|
||||||
<script src="js/builtin.js"></script>
|
<script src="js/builtin.js"></script>
|
||||||
<script src="js/update.js"></script>
|
<script src="js/update.js"></script>
|
||||||
|
<script src="js/settings.js"></script>
|
||||||
<script src="js/main.js"></script>
|
<script src="js/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
/**
|
|
||||||
* @typedef {Object} $
|
|
||||||
* @property {function(object=, ...HTMLElement): HTMLElement} DOCS - creates element with this tagname
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ### Create element
|
|
||||||
*
|
|
||||||
* @type {$}
|
|
||||||
*/
|
|
||||||
const $ = new Proxy(
|
|
||||||
class {
|
|
||||||
/**
|
|
||||||
* ### Creates `$` element
|
|
||||||
* @param {string} type element type
|
|
||||||
* @param {object} props element properties (onclick, class, ...)
|
|
||||||
* @param {...HTMLElement} children
|
|
||||||
* @returns {HTMLElement}
|
|
||||||
*/
|
|
||||||
static createElement(type, props, ...children) {
|
|
||||||
const element = document.createElement(type)
|
|
||||||
|
|
||||||
if (props) {
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
props instanceof Node
|
|
||||||
|| typeof props === 'string'
|
|
||||||
|| typeof props === 'number'
|
|
||||||
) children = [props, ...children]
|
|
||||||
else {throw Error()}
|
|
||||||
} catch {
|
|
||||||
for (const [key, value] of Object.entries(props)) {
|
|
||||||
if (key.startsWith('on') && typeof value === 'function') {
|
|
||||||
element.addEventListener(key.slice(2).toLowerCase(), value)
|
|
||||||
} else if (key === 'className') {
|
|
||||||
element.className = value
|
|
||||||
} else {
|
|
||||||
element.setAttribute(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
children.flat().forEach(child => {
|
|
||||||
if (typeof child === 'string' || typeof child === 'number') {
|
|
||||||
element.innerHTML += child
|
|
||||||
} else if (child instanceof Node) {
|
|
||||||
element.appendChild(child)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
element[Symbol.toPrimitive] = function(hint) {
|
|
||||||
if (hint === 'string') return this.outerHTML
|
|
||||||
return this
|
|
||||||
};
|
|
||||||
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
get(target, element) {
|
|
||||||
return function(props, ...children) {
|
|
||||||
return target.createElement(element, props, ...children)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
@ -64,12 +64,8 @@ searcher = () => {
|
||||||
const query = element.target.value.trim()
|
const query = element.target.value.trim()
|
||||||
const fb = findbang(query)
|
const fb = findbang(query)
|
||||||
const url = fb.valid ? fb.data.u : 'https://www.google.com/search?q={{{s}}}'
|
const url = fb.valid ? fb.data.u : 'https://www.google.com/search?q={{{s}}}'
|
||||||
window.location.href = url.replace('{{{s}}}', encodeURI(fb.found ? query.replace('!'+fb.inputed, '').trim() : query.trim()))
|
window.location.href = url.replace('{{{s}}}', encodeURIComponent(fb.found ? query.replace('!'+fb.inputed, '').trim() : query.trim()))
|
||||||
}
|
}
|
||||||
document.querySelector('#search_ddd').addEventListener('change', find)
|
document.querySelector('#search_ddd').addEventListener('change', find)
|
||||||
document.querySelector('#search_ddd').addEventListener('input', (e) => {proceed(e.target.value.trim())})
|
document.querySelector('#search_ddd').addEventListener('input', (e) => {proceed(e.target.value.trim())})
|
||||||
setInterval(() => {
|
|
||||||
document.querySelector('#search_ddd').focus()
|
|
||||||
proceed(document.querySelector('#search_ddd').value.trim())
|
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
window.addEventListener('DOMContentLoaded', async () => {
|
window.addEventListener('DOMContentLoaded', async () => {
|
||||||
clock()
|
search_focus.enable()
|
||||||
|
select('.settings_visual_help').overwrite(
|
||||||
|
$.div(
|
||||||
|
{onclick: () => settings.start()},
|
||||||
|
$.span('Open settings'),
|
||||||
|
Icons.settings({fill: true, size: 15}),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (storage.get('active')?.hide_clock == true) clock()
|
||||||
|
preset.load(storage.get('_active') || 'Default')
|
||||||
searcher()
|
searcher()
|
||||||
check_update()
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
167
src/js/settings.js
Normal file
167
src/js/settings.js
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
const search_focus = {
|
||||||
|
id: null,
|
||||||
|
paused: false,
|
||||||
|
enable: () => {
|
||||||
|
search_focus.paused = false
|
||||||
|
if (search_focus.id) return
|
||||||
|
search_focus.id = setInterval(() => {
|
||||||
|
if (search_focus.paused) return
|
||||||
|
if (!search_focus.id) return
|
||||||
|
document.querySelector('#search_ddd').focus()
|
||||||
|
console.log(search_focus.id)
|
||||||
|
// proceed(document.querySelector('#search_ddd').value.trim())
|
||||||
|
}, 100)
|
||||||
|
},
|
||||||
|
disable: () => {
|
||||||
|
search_focus.paused = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = {
|
||||||
|
views: [],
|
||||||
|
current: null,
|
||||||
|
|
||||||
|
back: async () => {
|
||||||
|
settings.current = settings.views.pop()
|
||||||
|
if (!settings.current) {
|
||||||
|
selectAll('#settings > *').map(e => e.element.classList.add('ov'))
|
||||||
|
await sleep(.2)
|
||||||
|
settings.stop()
|
||||||
|
} else {
|
||||||
|
settings._open(settings.current())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_open: async elements => {
|
||||||
|
selectAll('#settings > *').map(e => e.element.classList.add('ov'))
|
||||||
|
await sleep(.2)
|
||||||
|
select('#settings').overwrite(...elements)
|
||||||
|
selectAll('#settings > *').map(e => e.element.classList.add('ov'))
|
||||||
|
requestAnimationFrame(() => selectAll('#settings > *').map(e => e.element.classList.remove('ov')))
|
||||||
|
},
|
||||||
|
|
||||||
|
next: async fn => {
|
||||||
|
settings.views.push(settings.current)
|
||||||
|
settings.current = fn
|
||||||
|
settings._open(fn())
|
||||||
|
},
|
||||||
|
|
||||||
|
back_button: (title, icon = Icons.arrow_back) => $.div(
|
||||||
|
{onclick: settings.back, class: 'back'},
|
||||||
|
icon(),
|
||||||
|
$.h2(title),
|
||||||
|
),
|
||||||
|
start: () => {
|
||||||
|
settings.views = []
|
||||||
|
settings.current = null
|
||||||
|
select('body').append(
|
||||||
|
$.div({id: 'settings'})
|
||||||
|
)
|
||||||
|
search_focus.disable()
|
||||||
|
settings.next(settings.main)
|
||||||
|
},
|
||||||
|
stop: () => {
|
||||||
|
select('#settings').remove()
|
||||||
|
search_focus.enable()
|
||||||
|
},
|
||||||
|
main: () => [
|
||||||
|
settings.back_button('Settings', Icons.close),
|
||||||
|
$.div(
|
||||||
|
{class: 'surface'},
|
||||||
|
$.div(
|
||||||
|
{class: 'setting_entry', onclick: () => settings.next(settings.basic)},
|
||||||
|
$.span('Basic settings'),
|
||||||
|
Icons.arrow_right()
|
||||||
|
),
|
||||||
|
$.div(
|
||||||
|
{class: 'setting_entry', onclick: () => settings.next(settings.bangs)},
|
||||||
|
$.span('Bangs editor'),
|
||||||
|
Icons.arrow_right()
|
||||||
|
),
|
||||||
|
$.div(
|
||||||
|
{class: 'setting_entry', onclick: () => settings.next(settings.customization)},
|
||||||
|
$.span('Customization'),
|
||||||
|
Icons.arrow_right()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
basic: () => [
|
||||||
|
settings.back_button('Basic Settings'),
|
||||||
|
],
|
||||||
|
bangs: () => [
|
||||||
|
settings.back_button('Bangs editor'),
|
||||||
|
$.p('Here are your custom bangs, that will override default one from DuckDuckGo'),
|
||||||
|
$.div(
|
||||||
|
{class: 'list'},
|
||||||
|
$.div(
|
||||||
|
{class: 'action', onclick: () => settings.next(() => settings.edit_bang(false))},
|
||||||
|
Icons.add_2(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
$.div(
|
||||||
|
{class: 'surface'},
|
||||||
|
...bangs_override.toReversed().map(b => $.div(
|
||||||
|
{class: 'setting_entry', onclick: () => settings.next(() => settings.edit_bang(b))},
|
||||||
|
$.span(b.name),
|
||||||
|
Icons.arrow_right()
|
||||||
|
))
|
||||||
|
|
||||||
|
)
|
||||||
|
],
|
||||||
|
edit_bang: bang_data => {
|
||||||
|
const index = bangs_override.findIndex(b => b === bang_data)
|
||||||
|
/** @type {Bang} */
|
||||||
|
state.bang = bang_data || {
|
||||||
|
name: '',
|
||||||
|
keyword: '',
|
||||||
|
url: ''
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
settings.back_button('Edit Bang'),
|
||||||
|
$.h3('Name'),
|
||||||
|
$.input({value: state.bang.name, placeholder: 'Bang name', onchange: (e) => state.bang.name = e.target.value.trim()}),
|
||||||
|
$.div({style: 'height: 10px'}),
|
||||||
|
$.h3('Keyword'),
|
||||||
|
$.p('Add a short keyword for your bang, e.g. ', $.code('google')),
|
||||||
|
$.input({value: state.bang.keyword, placeholder: 'Bang keyword', onchange: (e) => state.bang.keyword = e.target.value.trim()}),
|
||||||
|
$.div({style: 'height: 10px'}),
|
||||||
|
$.h3('URL'),
|
||||||
|
$.p('Add a url for your bang with ', $.code('{{{s}}}'), ' string, that will be replaced with the search query'),
|
||||||
|
$.textarea({value: state.bang.url, placeholder: 'Bang URL', onchange: (e) => state.bang.url = e.target.value.trim()}, state.bang.url),
|
||||||
|
$.div(
|
||||||
|
{class: 'list'},
|
||||||
|
$.div(
|
||||||
|
{class: 'action', onclick: () => {
|
||||||
|
if (state.bang.name.length && state.bang.keyword.length && state.bang.url.length) {
|
||||||
|
index != -1 ? bangs_override[index] = state.bang : bangs_override.push(state.bang)
|
||||||
|
storage.set('_bangs', bangs_override)
|
||||||
|
settings.back()
|
||||||
|
} else {
|
||||||
|
alert('Please fill in all fields')
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
$.span('Save'),
|
||||||
|
),
|
||||||
|
bang_data ? $.div(
|
||||||
|
{class: 'action', onclick: () => {
|
||||||
|
if (confirm('Are you sure you want to delete this bang?')) {
|
||||||
|
bangs_override.splice(index, 1)
|
||||||
|
storage.set('_bangs', bangs_override)
|
||||||
|
settings.back()
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
$.span('Delete'),
|
||||||
|
) : null
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
/** @type {Bang} */
|
||||||
|
bang: null,
|
||||||
|
/** @type {string} */
|
||||||
|
search_url: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug
|
||||||
|
sleep(.3).then(() => settings.start())
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
const CookieManager = {
|
const storage = {
|
||||||
get: (cname) => {
|
get: (variable_name) => {
|
||||||
|
const worker = (cname) => {
|
||||||
let name = cname + "=";
|
let name = cname + "=";
|
||||||
let decodedCookie = decodeURIComponent(document.cookie);
|
let decodedCookie = document.cookie;
|
||||||
let ca = decodedCookie.split(';');
|
let ca = decodedCookie.split(';');
|
||||||
for(let i = 0; i <ca.length; i++) {
|
for(let i = 0; i <ca.length; i++) {
|
||||||
let c = ca[i];
|
let c = ca[i];
|
||||||
|
|
@ -13,21 +14,173 @@ const CookieManager = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
|
}
|
||||||
|
const c = worker(variable_name)
|
||||||
|
return c === '' ? null : JSON.parse(c).data
|
||||||
},
|
},
|
||||||
set: (cname, cvalue) => {
|
set: (variable_name, value) => {
|
||||||
|
const worker = (cname, cvalue) => {
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
d.setTime(d.getTime() + (999*24*60*60*1000));
|
d.setTime(d.getTime() + (999*24*60*60*1000));
|
||||||
let expires = "expires="+ d.toUTCString();
|
let expires = "expires="+ d.toUTCString();
|
||||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
|
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
|
||||||
}
|
}
|
||||||
|
worker(variable_name, JSON.stringify({data: value}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getFromStorage(variable_name) {
|
/**
|
||||||
const c = CookieManager.get(variable_name)
|
* @typedef {Object} Preset
|
||||||
return c === '' ? null : JSON.parse(c).data
|
* @prop {number} version
|
||||||
|
* @prop {string} name
|
||||||
|
* @prop {"default" | "url" | "color" | "gradient"} background_type // TODO: gradient
|
||||||
|
* @prop {number} base_bg_color
|
||||||
|
* @prop {number} base_bg_saturation
|
||||||
|
* @prop {number} base_bg_color_range
|
||||||
|
* @prop {string} font_sans
|
||||||
|
// * @prop {string} font_sans_size
|
||||||
|
* @prop {string} font_serif
|
||||||
|
// * @prop {string} font_serif_size
|
||||||
|
* @prop {string} font_monospace
|
||||||
|
// * @prop {string} font_monospace_size
|
||||||
|
* @prop {string} color_text_white
|
||||||
|
* @prop {string} color_text_gray
|
||||||
|
* @prop {string} s000
|
||||||
|
* @prop {string} s100
|
||||||
|
* @prop {string} s200
|
||||||
|
* @prop {string} s300
|
||||||
|
* @prop {bool} m
|
||||||
|
*/
|
||||||
|
|
||||||
|
const preset = {
|
||||||
|
/** @type {Preset} */
|
||||||
|
active: {},
|
||||||
|
current_version: 1,
|
||||||
|
migration: () => ([
|
||||||
|
['version', preset.current_version, 1],
|
||||||
|
['m', true, 1],
|
||||||
|
]).forEach(q => preset.active[q[0]] = q.length>2 ? q[1] : (preset.active[q[0]] || q[1])),
|
||||||
|
|
||||||
|
save: () => {
|
||||||
|
preset.migration()
|
||||||
|
storage.set(preset.active.name, preset.active)
|
||||||
|
storage.set('_active', preset.active.name)
|
||||||
|
var list = storage.get('_presets') ? storage.get('_presets') : []
|
||||||
|
if (!list.includes(preset.active.name)) {
|
||||||
|
list.push(preset.active.name)
|
||||||
|
storage.set('_presets', list)
|
||||||
|
}
|
||||||
|
preset.render()
|
||||||
|
},
|
||||||
|
delete: () => {
|
||||||
|
/**@type {string[]} */
|
||||||
|
var list = storage.get('_presets') ? storage.get('_presets') : []
|
||||||
|
|
||||||
|
list[list.indexOf(preset.active.name)] = false
|
||||||
|
list = list.filter(q => q != null)
|
||||||
|
storage.set('_presets', list)
|
||||||
|
preset.load(list.length > 0 ? list[0] : 'Default')
|
||||||
|
},
|
||||||
|
load: preset_name => {
|
||||||
|
if (!preset_name) preset_name = storage.get('_active') ? storage.get('_active') : 'Default'
|
||||||
|
|
||||||
|
var list = storage.get('_presets') ? storage.get('_presets') : []
|
||||||
|
if (!list.includes(preset_name)) {
|
||||||
|
preset.active = preset.default()
|
||||||
|
preset.active.name = preset_name
|
||||||
|
preset.save()
|
||||||
|
} else {
|
||||||
|
preset.active = storage.get(preset_name)
|
||||||
|
preset.save()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: () => {
|
||||||
|
const force_redraw = (element) => {
|
||||||
|
if (!element) return
|
||||||
|
element.classList.toggle('twt')
|
||||||
|
var n = document.createTextNode(' ')
|
||||||
|
var disp = element.style.display || 'block'
|
||||||
|
element.appendChild(n)
|
||||||
|
element.style.display = 'none'
|
||||||
|
requestAnimationFrame(
|
||||||
|
() => {
|
||||||
|
element.style.display = disp
|
||||||
|
n.parentNode.removeChild(n)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const style_override = {
|
||||||
|
// s000: 'surface-000',
|
||||||
|
// s100: 'surface-100',
|
||||||
|
// s200: 'surface-200',
|
||||||
|
// s300: 'surface-300',
|
||||||
|
font_sans: 'sans',
|
||||||
|
font_serif: 'serif',
|
||||||
|
font_monospace: 'monospace',
|
||||||
|
}
|
||||||
|
|
||||||
|
const set_style = (element, s) => {
|
||||||
|
s = s.replaceAll('-', '_')
|
||||||
|
const prop_name = Object.keys(style_override).includes(s) ? style_override[s] : s
|
||||||
|
element.style.setProperty('--'+prop_name, ['base_bg_saturation','base_bg_brightness'].includes(s) ? (preset.active[s]+'%') : preset.active[s])
|
||||||
|
}
|
||||||
|
|
||||||
|
select('#background').element.setAttribute('class', preset.active.background_type);
|
||||||
|
|
||||||
|
(['s000', 's100', 's200', 's300', 'font_sans', 'font_serif', 'font_monospace', 'color_text_white', 'color_text_gray',])
|
||||||
|
.forEach(s => set_style(select('body').element, s));
|
||||||
|
|
||||||
|
(['base_bg_color', 'base_bg_saturation', 'base_bg_color_range', 'base_bg_brightness'])
|
||||||
|
.forEach(s => set_style(select('#background').element, s));
|
||||||
|
force_redraw(select('#background').element)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Preset}
|
||||||
|
*/
|
||||||
|
default: () => { return {
|
||||||
|
name: 'Defualt',
|
||||||
|
version: preset.current_version,
|
||||||
|
// disable_clock: false,
|
||||||
|
// disable_animations: false,
|
||||||
|
background_type: 'default',
|
||||||
|
base_bg_color: 10,
|
||||||
|
base_bg_saturation: 10,
|
||||||
|
base_bg_brightness: 10,
|
||||||
|
base_bg_color_range: 10,
|
||||||
|
// search_url: 'https://www.google.com/search?q={{{s}}}',
|
||||||
|
// display_suggestions: false,
|
||||||
|
// display_history: false,
|
||||||
|
font_sans: 'Nunito',
|
||||||
|
font_serif: 'Playfair Display',
|
||||||
|
font_monospace: 'monospace',
|
||||||
|
color_text_white: '#fff',
|
||||||
|
color_text_gray: '#999',
|
||||||
|
s000: '#000',
|
||||||
|
s100: '#fff',
|
||||||
|
s200: '#fff',
|
||||||
|
s300: '#fff',
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setToStorage(variable_name, value) {
|
const defaults = {
|
||||||
CookieManager.set(variable_name, JSON.stringify({data: value}))
|
bangs: [
|
||||||
|
{name: 'Test bang', keyword: 'test_bang', url: 'https://www.google.com/search?q={{{s}}}'},
|
||||||
|
{name: 'Google Search AI', keyword: 'ai', url: 'https://www.google.com/search?q={{{s}}}&sourceid=chrome&ie=UTF-8&amc=1&oq={{{s}}}&udm=50&aep=109&cud='}
|
||||||
|
],
|
||||||
|
search_engine: 'https://www.google.com/search?q={{{s}}}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Bang
|
||||||
|
* @prop {string} keyword
|
||||||
|
* @prop {string} name
|
||||||
|
* @prop {string} url
|
||||||
|
*/
|
||||||
|
/** @type {Bang[]} */
|
||||||
|
var bangs_override = storage.get('_bangs') || defaults.bangs
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
var search_engine = storage.get('_search') || defaults.search_engine
|
||||||
56
src/lib/gicons.lib.js
Normal file
56
src/lib/gicons.lib.js
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* @typedef {Object} IconOptions
|
||||||
|
* @prop {100|200|300|400|500|600|700} weight Symbol weight
|
||||||
|
* @prop {bool} fill
|
||||||
|
* @prop {'low' | 'default' | 'high'} grade Symbol thickness
|
||||||
|
* @prop {20|48} optical_size (from 20 to 48) For the image to look the same at different sizes in dp
|
||||||
|
* @prop {number} size Icon size in px
|
||||||
|
* @prop {string} color Any css-supported color variant (names, rgb, hex)
|
||||||
|
*/
|
||||||
|
|
||||||
|
var DEFAULT_ICON_COLOR = '#000'
|
||||||
|
var DEFAULT_ICON_SIZE = 24
|
||||||
|
/** @type {IconOptions} */
|
||||||
|
var DEFAULT_ICON_OPTIONS = {}
|
||||||
|
|
||||||
|
/** @type {Record<string, (options: IconOptions) => HTMLSpanElement} */
|
||||||
|
const Icons = new Proxy(
|
||||||
|
class {
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {IconOptions} options
|
||||||
|
* @returns {HTMLSpanElement}
|
||||||
|
*/
|
||||||
|
static getIcon(name, {fill, weight, grade, optical_size, size, color}) {
|
||||||
|
const gr = ({
|
||||||
|
'low': -25,
|
||||||
|
'default': 0,
|
||||||
|
'high': 200
|
||||||
|
})[grade]
|
||||||
|
const data = `
|
||||||
|
'FILL' ${fill !== undefined ? fill?1:0 : 1},
|
||||||
|
'wdth' ${weight || 400},
|
||||||
|
'GRAD' ${gr || 0},
|
||||||
|
'opsz' ${optical_size || 20}`
|
||||||
|
const fz = size ? typeof size != Number ? `${size}px`:size : (DEFAULT_ICON_SIZE+'px')
|
||||||
|
return $.span(
|
||||||
|
{class: 'material-symbols-rounded icon', style: `font-size: ${fz}; width: ${fz}; height: ${fz}; ${color ? `color: ${color};` : ''}font-variation-settings: ${data}`},
|
||||||
|
name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
get(target, name) {
|
||||||
|
/**
|
||||||
|
* @argument {IconOptions} options
|
||||||
|
*/
|
||||||
|
return function(options) {
|
||||||
|
return target.getIcon(name, options || DEFAULT_ICON_OPTIONS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const gIconsInit = () => document.head.innerHTML += `
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200">
|
||||||
|
`
|
||||||
226
src/lib/sinya.lib.js
Normal file
226
src/lib/sinya.lib.js
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
/**
|
||||||
|
* @typedef {Object} $
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} div - creates div element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} p - creates p element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} span - creates span element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} button - creates button element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} input - creates input element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} a - creates a element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} img - creates img element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} h1 - creates h1 element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} h2 - creates h2 element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} h3 - creates h3 element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} h4 - creates h4 element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} h5 - creates h5 element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} h6 - creates h6 element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} ul - creates ul element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} li - creates li element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} form - creates form element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} table - creates table element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} tr - creates tr element
|
||||||
|
* @property {function(object=, ...HTMLElement): HTMLElement} td - creates td element
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ### Create element
|
||||||
|
*
|
||||||
|
* @type {$}
|
||||||
|
*/
|
||||||
|
const $ = new Proxy(
|
||||||
|
class {
|
||||||
|
/**
|
||||||
|
* ### Creates `$` element
|
||||||
|
* @param {string} type element type
|
||||||
|
* @param {object} props element properties (onclick, class, ...)
|
||||||
|
* @param {...HTMLElement} children
|
||||||
|
* @returns {HTMLElement}
|
||||||
|
*/
|
||||||
|
static createElement(type, props, ...children) {
|
||||||
|
const element = document.createElement(type)
|
||||||
|
|
||||||
|
if (props) {
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
props instanceof Node
|
||||||
|
|| typeof props === 'string'
|
||||||
|
|| typeof props === 'number'
|
||||||
|
) children = [props, ...children]
|
||||||
|
else {throw Error()}
|
||||||
|
} catch {
|
||||||
|
for (const [key, value] of Object.entries(props)) {
|
||||||
|
if (key.startsWith('on') && typeof value === 'function') {
|
||||||
|
element.addEventListener(key.slice(2).toLowerCase(), value)
|
||||||
|
} else if (key === 'className') {
|
||||||
|
element.className = value
|
||||||
|
} else {
|
||||||
|
element.setAttribute(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
children.flat().forEach(child => {
|
||||||
|
if (typeof child === 'string' || typeof child === 'number') {
|
||||||
|
element.innerHTML += child
|
||||||
|
} else if (child instanceof Node) {
|
||||||
|
element.appendChild(child)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
element[Symbol.toPrimitive] = function(hint) {
|
||||||
|
if (hint === 'string') return this.outerHTML
|
||||||
|
return this
|
||||||
|
};
|
||||||
|
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
get(target, element) {
|
||||||
|
return function(props, ...children) {
|
||||||
|
return target.createElement(element, props, ...children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select single DOM element and wrap them
|
||||||
|
* @param {string} selector
|
||||||
|
* @returns {WrappedElement}
|
||||||
|
*/
|
||||||
|
const select = (selector, _target = document) => wrap(_target.querySelector(selector))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select multiple DOM element and wrap them
|
||||||
|
* @param {string} selector
|
||||||
|
* @returns {Array.<WrappedElement>}
|
||||||
|
*/
|
||||||
|
const selectAll = (selector, _target = document) => [..._target.querySelectorAll(selector)].map(_ => wrap(_))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap existing DOM element
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
* @returns {WrappedElement}
|
||||||
|
*/
|
||||||
|
const wrap = element => new WrappedElement(element)
|
||||||
|
|
||||||
|
const sleep = s => new Promise(r => setTimeout(r, s*1000))
|
||||||
|
|
||||||
|
|
||||||
|
class WrappedElement {
|
||||||
|
/**
|
||||||
|
* @type {HTMLElement}
|
||||||
|
*/
|
||||||
|
element
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create element wrapper
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
*/
|
||||||
|
constructor(element) {
|
||||||
|
this.element = element
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override element classes
|
||||||
|
* @param {...string} list
|
||||||
|
* @returns {WrappedElement} this element for chaining
|
||||||
|
*/
|
||||||
|
classes(...list) {
|
||||||
|
this.element.classList = list.join(' ')
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get elements styles to edit them
|
||||||
|
* @returns {CSSStyleDeclaration}
|
||||||
|
*/
|
||||||
|
styles () {
|
||||||
|
return this.element.style
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get element nodes
|
||||||
|
* @returns {Array.<WrappedElement>}
|
||||||
|
*/
|
||||||
|
nodes () {
|
||||||
|
return [...this.element.childNodes].map(e => wrap(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply animation to element (async)
|
||||||
|
* @param {string} animation_name
|
||||||
|
* @param {number} animation_duration
|
||||||
|
* @param {string} animation_function_name
|
||||||
|
* @returns {WrappedElement} this element for chaining
|
||||||
|
*/
|
||||||
|
async animate (animation_name, animation_duration, animation_function_name = '') {
|
||||||
|
this.styles().animation = `${animation_name} ${animation_duration}s ${animation_function_name}`
|
||||||
|
await sleep(animation_duration - (animation_duration / 20))
|
||||||
|
this.styles().animation = ``
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear element content (async)
|
||||||
|
* @returns {WrappedElement} this element for chaining
|
||||||
|
*/
|
||||||
|
async clear () {
|
||||||
|
this.element.innerHTML = ''
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite element content (async)
|
||||||
|
* @param {...HTMLElement} children
|
||||||
|
* @returns {WrappedElement} this element for chaining
|
||||||
|
*/
|
||||||
|
async overwrite (...children) {
|
||||||
|
this.clear()
|
||||||
|
this.append(...children)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Add content to element (async)
|
||||||
|
* @param {...HTMLElement} children
|
||||||
|
* @returns {WrappedElement} this element for chaining
|
||||||
|
*/
|
||||||
|
async append (...children) {
|
||||||
|
children.forEach(child => {
|
||||||
|
if (typeof child === 'string' || typeof child === 'number') {
|
||||||
|
this.element.innerHTML += child
|
||||||
|
} else if (child instanceof Node) {
|
||||||
|
this.element.appendChild(child)
|
||||||
|
} else if (child instanceof WrappedElement) {
|
||||||
|
this.element.appendChild(child.element)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Removes element (async)
|
||||||
|
*/
|
||||||
|
async remove() {
|
||||||
|
this.element.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return element node by selector
|
||||||
|
* @param {string} selector
|
||||||
|
* @returns {WrappedElement}
|
||||||
|
*/
|
||||||
|
select (selector) {
|
||||||
|
return select(selector, this.element)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return element nodes by selector
|
||||||
|
* @param {string} selector
|
||||||
|
* @returns {Array.<WrappedElement>}
|
||||||
|
*/
|
||||||
|
selectAll (selector) {
|
||||||
|
return selectAll(selector, this.element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Body = select('body')
|
||||||
Loading…
Add table
Reference in a new issue