This commit is contained in:
relaxed 2024-08-30 05:49:53 +05:00
commit 880f01daa9
29 changed files with 689 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# Fumas
> svelte + tailwindcss minimal sytle ui lib
add it with
```sh
bun add -D git+https://git.ogkod.ru/root/fumas.git
```
and set up tailwind
```js
export default {
content: [
"./node_modules/fumas/src/ui/*.{js,svelte}",
...
]
}
```

BIN
bun.lockb Executable file

Binary file not shown.

27
index.html Normal file
View File

@ -0,0 +1,27 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>fumas</title>
<script>
if (
localStorage.getItem("color-theme") === "dark" ||
(!("color-theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
</script>
</head>
<body class="bg-[var(--w-bg)] dark:bg-[var(--b-bg)]">
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

32
jsconfig.json Normal file
View File

@ -0,0 +1,32 @@
{
"compilerOptions": {
"moduleResolution": "bundler",
"target": "ESNext",
"module": "ESNext",
/**
* svelte-preprocess cannot figure out whether you have
* a value or a type, so tell TypeScript to enforce using
* `import type` instead of `import` for Types.
*/
"verbatimModuleSyntax": true,
"isolatedModules": true,
"resolveJsonModule": true,
/**
* To have warnings / errors of the Svelte compiler at the
* correct position, enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable this if you'd like to use dynamic types.
*/
"checkJs": true
},
/**
* Use global.d.ts instead of compilerOptions.types
* to avoid limiting type declarations.
*/
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
}

30
package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "fumas",
"main": "dist/index.js",
"svelte": "./dist/index.js",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.41",
"svelte": "^4.2.18",
"tailwindcss": "^3.4.10",
"vite": "^5.4.1"
},
"svelte": "./src/ui/index.js",
"exports": {
".": {
"svelte": "./src/ui/index.js",
"import": "./src/ui/index.js"
},
"./package.json": "./package.json"
},
"files": ["src"]
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

BIN
public/jbmv.ttf Normal file

Binary file not shown.

83
src/App.svelte Normal file
View File

@ -0,0 +1,83 @@
<script>
import ThemeSwitch from "./ui/ThemeSwitch.svelte";
import Button from "./ui/Button.svelte";
import Window from "./ui/Window.svelte";
import Dialog from "./ui/Dialog.svelte";
import SideMenu from "./ui/SideMenu.svelte";
import Input from "./ui/form/Input.svelte";
import TextArea from "./ui/form/TextArea.svelte";
import Box from "./ui/Box.svelte";
import x from "./ui/x.svg";
import burger from "./ui/burger.svg";
import arrow from "./ui/arrow.svg";
let is_dialog_open = false;
let is_side_menu_open = false;
</script>
<Window>
<SideMenu bind:is_open={is_side_menu_open}></SideMenu>
<div class="w-auto mx-2 flex justify-between items-center">
<button
on:click={() => {
is_side_menu_open = !is_side_menu_open;
}}
>
<img src={burger} class="size-[50px] dark:invert" alt="" />
</button>
<h1 class="text-2xl">Fumas</h1>
<ThemeSwitch></ThemeSwitch>
</div>
<div class="w-auto">
<Button>Just Button</Button>
<Button color={"red"}>Red Button</Button>
<Button color={"green"}>Green Button</Button>
<Button color={"blue"}>Blue Button</Button>
</div>
<Dialog
name={"This is DIaLog!!!!"}
is_name_editable={true}
bind:is_open={is_dialog_open}
>
<div class="w-auto">
<Button
class_name={"bg-[#f0f] dark:bg-[#f0f] hover:bg-cyan-500"}
on_click={async () => {
is_dialog_open = false;
}}
>
Close Dialog!
</Button>
</div>
</Dialog>
<Button
is_border={false}
on_click={async () => {
is_dialog_open = true;
}}
>
Open Dialog!
</Button>
<Input placeholder={"input hey hey"}></Input>
<TextArea placeholder={"auto resize textarea LES GOOOOOO"}></TextArea>
<Box is_padding={true}>this is just a box</Box>
<h1 class="w-full text-2xl text-center">Icons</h1>
<h1 class="text-2xl">fumas/x.svg</h1>
<img src={x} class="dark:invert size-[100px]" alt="" />
<h1 class="text-2xl">fumas/burger.svg</h1>
<img src={burger} class="dark:invert size-[100px]" alt="" />
<h1 class="text-2xl">fumas/arrow.svg</h1>
<img src={arrow} class="dark:invert size-[100px]" alt="" />
</Window>

42
src/global.css Normal file
View File

@ -0,0 +1,42 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: JetBrainsMono;
src: url(/jbmv.ttf);
}
* {
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
outline: none !important;
font-family: JetBrainsMono;
/* white theme */
--w-bg: #ddd;
--w-text: #000;
--w-border: #000;
--w-red: #fcc;
--w-green: #0f0;
--w-blue: #ccf;
--w-orange: #f80;
/* dark theme */
--b-bg: #000;
--b-text: #fff;
--b-border: #fff;
--b-red: #800;
--b-green: #080;
--b-blue: #000066;
--b-orange: #f80;
}

0
src/index.js Normal file
View File

8
src/main.js Normal file
View File

@ -0,0 +1,8 @@
import './global.css'
import App from './App.svelte'
const app = new App({
target: document.getElementById('app'),
})
export default app

16
src/ui/Box.svelte Normal file
View File

@ -0,0 +1,16 @@
<script>
export let class_name = "";
export let is_border = true;
export let is_padding = false;
</script>
<div
class="m-2 h-auto w-auto
{is_padding ? 'p-2' : ''}
bg-transparent
{is_border ? 'border-2' : 'border-0'}
border-[var(--w-border)] dark:border-[var(--b-border)] rounded-xl
{class_name}"
>
<slot></slot>
</div>

48
src/ui/Button.svelte Normal file
View File

@ -0,0 +1,48 @@
<script>
export let on_click = async () => {};
/** @type {'blue' | 'red' | 'green' | 'bg'} */
export let color = "bg";
export let class_name = "";
export let is_border = true;
let is_hover = false;
const colors = {
blue: "bg-[var(--w-blue)] dark:bg-[var(--b-blue)]",
red: "bg-[var(--w-red)] dark:bg-[var(--b-red)]",
green: "bg-[var(--w-green)] dark:bg-[var(--b-green)]",
bg: "bg-[var(--w-bg)] dark:bg-[var(--b-bg)]",
};
</script>
<div class="w-auto m-2">
<button
on:click={async () => {
is_hover = false;
setTimeout(async () => {
await on_click();
}, 100);
}}
on:mouseover={() => {
is_hover = true;
}}
on:focus={() => {
is_hover = true;
}}
on:mouseout={() => {
is_hover = false;
}}
on:blur={() => {
is_hover = false;
}}
class="w-full py-1 px-2
flex justify-center items-center
text-[var(--w-text)] dark:text-[var(--b-text)] text-xl
{is_border ? 'border-2' : 'border-0'}
border-[var(--w-border)] dark:border-[var(--b-border)] rounded-xl
text-[var(--w-text)] dark:text-[var(--b-text)]
{is_hover ? 'opacity-80' : 'opacity-100'}
{colors[color]} {class_name}
transition-all duration-100"
>
<slot></slot>
</button>
</div>

58
src/ui/Dialog.svelte Normal file
View File

@ -0,0 +1,58 @@
<script>
import x from "./x.svg";
export let is_open = false;
export let is_name_editable = false;
export let name = "";
export let class_name = "";
export let on_close = () => {};
</script>
<div
class="fixed top-0 left-0 w-full h-svh z-20
max-w-full max-h-full
flex justify-center items-center
backdrop-blur-sm
bg-[#00000022]
{is_open ? 'opacity-100 visible' : 'invisible opacity-0'}
transition-all duration-300"
>
<div
class="size-[700px] mx-4 flex flex-col
border-[var(--w-border)] dark:border-[var(--b-border)] border-2 rounded-xl
bg-[var(--w-bg)] dark:bg-[var(--b-bg)]
text-[var(--w-text)] dark:text-[var(--b-text)] text-2xl
overflow-auto
{class_name}
"
>
<div
class="ml-2 relative flex flex-initial justify-between items-center"
>
{#if is_name_editable}
<input
type="text"
bind:value={name}
class="bg-[var(--w-bg)] dark:bg-[var(--b-bg)] w-full focus-visible:outline-none"
/>
{:else}
<p class=" w-full focus-visible:outline-none">
{name}
</p>
{/if}
<button
on:click={() => {
is_open = false;
on_close();
}}
class="size-[60px] m-2 pb-3"
>
<img
src={x}
class="size-[40px] dark:invert"
alt="x close img"
/>
</button>
</div>
<slot></slot>
</div>
</div>

50
src/ui/SideMenu.svelte Normal file
View File

@ -0,0 +1,50 @@
<script>
import x from "./x.svg";
export let is_open = false;
export let name = "SideMenu";
export let minw = "";
</script>
<div
class="h-svh p-2
fixed top-0 left-0 z-20
{is_open ? 'w-svw' : 'w-0'}
flex
transition-all duration-300 ease-in-out
text-[var(--w-text)] dark:text-[var(--b-text)]
"
>
<div
class="h-full
flex-initial
bg-[var(--w-bg)] dark:bg-[var(--b-bg)]
border-[var(--w-border)] dark:border-[var(--b-border)] rounded-xl
{is_open ? 'border-2 max-w-full' : 'border-0 max-w-0'}
{is_open ? 'max-w-[312px] min-w-[300px]' + minw : 'max-w-0 min-w-0'}
overflow-y-auto overflow-x-hidden
transition-all duration-300 ease-in-out
"
>
<div class="m-2 w-auto flex justify-between items-center">
<h1 class="text-2xl">{name}</h1>
<button
on:click={() => {
is_open = !is_open;
}}
>
<img
src={x}
class="size-[48px] dark:invert transition-all hover:opacity-80"
alt="x close icon"
/>
</button>
</div>
<slot></slot>
</div>
<button
class="flex-auto w-full"
on:click={() => {
is_open = !is_open;
}}
></button>
</div>

88
src/ui/ThemeSwitch.svelte Normal file
View File

@ -0,0 +1,88 @@
<script>
import {
theme_switch_is_resize,
theme_switch_is_transparent,
theme_switch_is_dark,
theme_switch_is_update,
} from "./state.js";
import { onMount } from "svelte";
import { get } from "svelte/store";
let is_resize = false;
let is_transparent = false;
let is_dark = true;
let is_update = false;
theme_switch_is_resize.subscribe((val) => {
is_resize = val;
});
theme_switch_is_transparent.subscribe((val) => {
is_transparent = val;
});
theme_switch_is_dark.subscribe((val) => {
is_dark = val;
});
theme_switch_is_update.subscribe((val) => {
is_update = val;
});
function handleSwitchDarkMode() {
setTimeout(() => {
theme_switch_is_transparent.set(true);
theme_switch_is_dark.set(!is_dark);
setTimeout(() => {
theme_switch_is_resize.set(false);
theme_switch_is_transparent.set(false);
setTimeout(() => {
theme_switch_is_update.set(true);
setTimeout(() => {
theme_switch_is_update.set(false);
}, 0);
}, 0);
}, 250);
}, 500);
theme_switch_is_resize.set(true);
const isDark = window.document.documentElement.classList.toggle("dark");
if (isDark) {
localStorage.setItem("color-theme", "dark");
} else {
localStorage.setItem("color-theme", "light");
}
}
onMount(() => {
if (localStorage.getItem("color-theme") === "light") {
is_dark = false;
}
});
</script>
{#if !get(theme_switch_is_update)}
<div class=" min-w-[40px] min-h-[40px] flex justify-center items-center">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="group h-[50px] w-[50px] rounded-full flex justify-center items-center
{is_dark ? 'bg-[var(--w-bg)]' : 'bg-[var(--b-bg)]'}
"
on:click={handleSwitchDarkMode}
>
<div
class=" rounded-full
{!is_transparent
? is_dark
? 'group-hover:bg-[var(--b-bg)] dark:group-hover:bg-[var(--b-bg)]'
: 'group-hover:bg-[var(--w-bg)] dark:group-hover:bg-[var(--w-bg)]'
: ''}
{!is_transparent
? is_resize
? 'h-[50px] w-[50px]'
: 'h-[25px] w-[25px]'
: 'h-[25px] w-[25px]'}
transition-all duration-500 ease-in-out"
></div>
</div>
</div>
{/if}

9
src/ui/Window.svelte Normal file
View File

@ -0,0 +1,9 @@
<div class="flex justify-center items-center">
<div
class="w-[1400px] m-2
flex flex-col max-[1300px]:w-full
text-[var(--w-text)] dark:text-[var(--b-text)]"
>
<slot></slot>
</div>
</div>

17
src/ui/arrow.svg Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="800px" height="800px" viewBox="0 -4.5 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>arrow_up [#340]</title>
<desc>Created with Sketch.</desc>
<defs>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Dribbble-Light-Preview" transform="translate(-140.000000, -6683.000000)" fill="#000000">
<g id="icons" transform="translate(56.000000, 160.000000)">
<path d="M84,6532.61035 L85.4053672,6534 L94.0131154,6525.73862 L94.9311945,6526.61986 L94.9261501,6526.61502 L102.573446,6533.95545 L104,6532.58614 C101.8864,6530.55736 95.9854722,6524.89321 94.0131154,6523 C92.5472155,6524.40611 93.9757869,6523.03486 84,6532.61035" id="arrow_up-[#340]">
</path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 935 B

4
src/ui/burger.svg Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" fill="none">
<path fill="#000" fill-rule="evenodd" d="M18 5a1 1 0 100-2H2a1 1 0 000 2h16zm0 4a1 1 0 100-2H2a1 1 0 100 2h16zm1 3a1 1 0 01-1 1H2a1 1 0 110-2h16a1 1 0 011 1zm-1 5a1 1 0 100-2H2a1 1 0 100 2h16z"/>
</svg>

After

Width:  |  Height:  |  Size: 430 B

30
src/ui/form/Input.svelte Normal file
View File

@ -0,0 +1,30 @@
<script>
export let value = "";
export let placeholder = "";
export let is_border = true;
export let class_name = "";
export let d_class = `h-[40px] p-2 m-2
bg-[var(--w-bg)] dark:bg-[var(--b-bg)]
${is_border ? "border-2" : "border-0"}
text-[var(--w-text)] dark:text-[var(--b-text)]
border-[var(--w-border)] dark:border-[var(--b-border)] rounded-xl`;
/** @type {'text' | 'password'}*/
export let type = "text";
</script>
{#if type === "password"}
<input
type="password"
bind:value
{placeholder}
class="{d_class} {class_name}"
/>
{:else}
<input
type="text"
bind:value
{placeholder}
class="{d_class} {class_name}"
/>
{/if}

View File

@ -0,0 +1,44 @@
<script>
import { onMount } from "svelte";
export let value = "";
export let placeholder = "";
export let is_border = true;
export let is_resize = true;
export let class_name = "";
let tx;
function resize() {
setTimeout(() => {
tx.style.height = tx.scrollHeight + "px";
const sh = Number(tx.scrollHeight);
if (sh <= 60 || value === "") {
tx.style.height = "40px";
} else {
tx.style.height = "auto";
tx.style.height = tx.scrollHeight + "px";
}
}, 0);
}
onMount(() => {
tx.addEventListener("input", resize, false);
tx.style.height = "auto";
setTimeout(() => {
resize();
}, 100);
});
</script>
<textarea
bind:value
bind:this={tx}
{placeholder}
class="p-2 m-2 h-auto min-h-0
overflow-y-hidden
focus-visible:outline-none
bg-[var(--w-bg)] dark:bg-[var(--b-bg)]
{is_border ? 'border-2' : 'border-0'}
text-[var(--w-text)] dark:text-[var(--b-text)]
border-[var(--w-border)] dark:border-[var(--b-border)] rounded-xl
{is_resize ? '' : 'resize-none'}
transition-all
{class_name}"
></textarea>

12
src/ui/index.js Normal file
View File

@ -0,0 +1,12 @@
export { default as ThemeSwitch } from "./ThemeSwitch.svelte"
export { default as Button } from "./Button.svelte"
export { default as Window } from "./Window.svelte"
export { default as Dialog } from "./Dialog.svelte"
export { default as SideMenu } from "./SideMenu.svelte"
export { default as Input } from "./form/Input.svelte"
export { default as TextArea } from "./form/TextArea.svelte"
export { default as Box } from "./Box.svelte"
export { default as x } from "./x.svg"
export { default as burger } from "./burger.svg"
export { default as arrow } from "./arrow.svg"

6
src/ui/state.js Normal file
View File

@ -0,0 +1,6 @@
import { writable } from "svelte/store";
export const theme_switch_is_resize = writable(false);
export const theme_switch_is_transparent = writable(false);
export const theme_switch_is_dark = writable(true);
export const theme_switch_is_update = writable(false);

2
src/ui/x.svg Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path fill="#000000" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"/></svg>

After

Width:  |  Height:  |  Size: 507 B

2
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />

7
svelte.config.js Normal file
View File

@ -0,0 +1,7 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
export default {
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
// for more information about preprocessors
preprocess: vitePreprocess(),
}

12
tailwind.config.js Normal file
View File

@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,svelte}",
],
darkMode: 'selector',
theme: {
extend: {},
},
plugins: [],
}

12
vite.config.js Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [svelte()],
server: {
host: true
},
preview: {
host: true
}
})