cronixui 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -2
- package/packages/go/cronixui/cronixui.go +379 -0
- package/packages/go/cronixui/go.mod +3 -0
- package/packages/go/cronixui/tokens.go +129 -0
- package/packages/python/cronixui/__init__.py +109 -0
- package/packages/python/cronixui/accordion.py +29 -0
- package/packages/python/cronixui/command_palette.py +53 -0
- package/packages/python/cronixui/core.py +48 -0
- package/packages/python/cronixui/dropdown.py +26 -0
- package/packages/python/cronixui/modal.py +22 -0
- package/packages/python/cronixui/nav.py +31 -0
- package/packages/python/cronixui/pagination.py +58 -0
- package/packages/python/cronixui/pyproject.toml +11 -0
- package/packages/python/cronixui/search.py +50 -0
- package/packages/python/cronixui/tabs.py +18 -0
- package/packages/python/cronixui/toast.py +73 -0
- package/packages/python/cronixui/toggle.py +22 -0
- package/packages/python/cronixui/tokens.py +200 -0
- package/packages/rust/cronixui/src/accordion.rs +49 -0
- package/packages/rust/cronixui/src/command_palette.rs +62 -0
- package/packages/rust/cronixui/src/dropdown.rs +31 -0
- package/packages/rust/cronixui/src/lib.rs +81 -0
- package/packages/rust/cronixui/src/modal.rs +27 -0
- package/packages/rust/cronixui/src/pagination.rs +37 -0
- package/packages/rust/cronixui/src/search.rs +49 -0
- package/packages/rust/cronixui/src/tabs.rs +23 -0
- package/packages/rust/cronixui/src/toast.rs +70 -0
- package/packages/rust/cronixui/src/toggle.rs +27 -0
- package/packages/rust/cronixui/src/tokens.rs +137 -0
- package/packages/web/src/index.js +2 -0
- package/packages/web/src/index.mjs +2 -0
- package/packages/web/src/tokens.ts +120 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Command palette component."""
|
|
2
|
+
|
|
3
|
+
from typing import Callable, Optional, List
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class CommandPaletteItem:
|
|
9
|
+
"""Command palette item."""
|
|
10
|
+
|
|
11
|
+
title: str
|
|
12
|
+
kbd: Optional[str] = None
|
|
13
|
+
action: Optional[Callable] = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CommandPalette:
|
|
17
|
+
"""Command palette component."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, element=None):
|
|
20
|
+
"""Initialize command palette on element."""
|
|
21
|
+
self._element = element
|
|
22
|
+
self._items: List[CommandPaletteItem] = []
|
|
23
|
+
self._open: bool = False
|
|
24
|
+
|
|
25
|
+
def open(self) -> None:
|
|
26
|
+
"""Open command palette."""
|
|
27
|
+
self._open = True
|
|
28
|
+
|
|
29
|
+
def close(self) -> None:
|
|
30
|
+
"""Close command palette."""
|
|
31
|
+
self._open = False
|
|
32
|
+
|
|
33
|
+
def toggle(self) -> None:
|
|
34
|
+
"""Toggle command palette."""
|
|
35
|
+
self._open = not self._open
|
|
36
|
+
|
|
37
|
+
def is_open(self) -> bool:
|
|
38
|
+
"""Check if command palette is open."""
|
|
39
|
+
return self._open
|
|
40
|
+
|
|
41
|
+
def set_items(self, items: List[CommandPaletteItem]) -> None:
|
|
42
|
+
"""Set command items."""
|
|
43
|
+
self._items = items
|
|
44
|
+
|
|
45
|
+
def get_items(self) -> List[CommandPaletteItem]:
|
|
46
|
+
"""Get all items."""
|
|
47
|
+
return self._items
|
|
48
|
+
|
|
49
|
+
def execute(self, index: int) -> None:
|
|
50
|
+
"""Execute item by index."""
|
|
51
|
+
if 0 <= index < len(self._items) and self._items[index].action:
|
|
52
|
+
self._items[index].action()
|
|
53
|
+
self.close()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Core CronixUI functions."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Optional, List, Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def init() -> None:
|
|
7
|
+
"""Initialize all CronixUI components."""
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def query(selector: str) -> Optional[Any]:
|
|
12
|
+
"""Query single element.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
selector: CSS selector
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
Element or None
|
|
19
|
+
"""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def query_all(selector: str) -> List[Any]:
|
|
24
|
+
"""Query all matching elements.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
selector: CSS selector
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
List of elements
|
|
31
|
+
"""
|
|
32
|
+
return []
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def create_el(
|
|
36
|
+
tag: str, class_name: Optional[str] = None, attrs: Optional[Dict[str, str]] = None
|
|
37
|
+
) -> Any:
|
|
38
|
+
"""Create an element.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
tag: HTML tag name
|
|
42
|
+
class_name: Optional CSS class name
|
|
43
|
+
attrs: Optional attributes dict
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Created element
|
|
47
|
+
"""
|
|
48
|
+
pass
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Dropdown menu component."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Dropdown:
|
|
5
|
+
"""Dropdown menu component."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, element=None):
|
|
8
|
+
"""Initialize dropdown on element."""
|
|
9
|
+
self._element = element
|
|
10
|
+
self._open: bool = False
|
|
11
|
+
|
|
12
|
+
def open(self) -> None:
|
|
13
|
+
"""Open the dropdown."""
|
|
14
|
+
self._open = True
|
|
15
|
+
|
|
16
|
+
def close(self) -> None:
|
|
17
|
+
"""Close the dropdown."""
|
|
18
|
+
self._open = False
|
|
19
|
+
|
|
20
|
+
def toggle(self) -> None:
|
|
21
|
+
"""Toggle the dropdown."""
|
|
22
|
+
self._open = not self._open
|
|
23
|
+
|
|
24
|
+
def is_open(self) -> bool:
|
|
25
|
+
"""Check if dropdown is open."""
|
|
26
|
+
return self._open
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Modal dialog component."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Modal:
|
|
5
|
+
"""Modal dialog component."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, element=None):
|
|
8
|
+
"""Initialize modal on element."""
|
|
9
|
+
self._element = element
|
|
10
|
+
self._open: bool = False
|
|
11
|
+
|
|
12
|
+
def open(self) -> None:
|
|
13
|
+
"""Open the modal."""
|
|
14
|
+
self._open = True
|
|
15
|
+
|
|
16
|
+
def close(self) -> None:
|
|
17
|
+
"""Close the modal."""
|
|
18
|
+
self._open = False
|
|
19
|
+
|
|
20
|
+
def is_open(self) -> bool:
|
|
21
|
+
"""Check if modal is open."""
|
|
22
|
+
return self._open
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Navigation component."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Nav:
|
|
5
|
+
"""Navigation component."""
|
|
6
|
+
|
|
7
|
+
_instances = []
|
|
8
|
+
|
|
9
|
+
def __init__(self, element=None):
|
|
10
|
+
"""Initialize navigation on element."""
|
|
11
|
+
self._element = element
|
|
12
|
+
self._active: str = ""
|
|
13
|
+
Nav._instances.append(self)
|
|
14
|
+
|
|
15
|
+
def set_active(self, item: str) -> None:
|
|
16
|
+
"""Set active navigation item."""
|
|
17
|
+
self._active = item
|
|
18
|
+
|
|
19
|
+
def get_active(self) -> str:
|
|
20
|
+
"""Get active navigation item."""
|
|
21
|
+
return self._active
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def init(cls) -> None:
|
|
25
|
+
"""Initialize all navigation components."""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def get_all(cls) -> list:
|
|
30
|
+
"""Get all navigation instances."""
|
|
31
|
+
return cls._instances
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Pagination component."""
|
|
2
|
+
|
|
3
|
+
from typing import Callable, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Pagination:
|
|
7
|
+
"""Pagination component."""
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
element=None,
|
|
12
|
+
total: int = 1,
|
|
13
|
+
current: int = 1,
|
|
14
|
+
on_change: Optional[Callable[[int], None]] = None,
|
|
15
|
+
):
|
|
16
|
+
"""Initialize pagination.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
element: Target element
|
|
20
|
+
total: Total number of pages
|
|
21
|
+
current: Current page (default: 1)
|
|
22
|
+
on_change: Callback when page changes
|
|
23
|
+
"""
|
|
24
|
+
self._element = element
|
|
25
|
+
self._total = total
|
|
26
|
+
self._current = current
|
|
27
|
+
self._on_change = on_change
|
|
28
|
+
|
|
29
|
+
def go_to(self, page: int) -> None:
|
|
30
|
+
"""Go to specific page."""
|
|
31
|
+
if 1 <= page <= self._total:
|
|
32
|
+
self._current = page
|
|
33
|
+
if self._on_change:
|
|
34
|
+
self._on_change(page)
|
|
35
|
+
|
|
36
|
+
def next(self) -> None:
|
|
37
|
+
"""Go to next page."""
|
|
38
|
+
if self._current < self._total:
|
|
39
|
+
self.go_to(self._current + 1)
|
|
40
|
+
|
|
41
|
+
def prev(self) -> None:
|
|
42
|
+
"""Go to previous page."""
|
|
43
|
+
if self._current > 1:
|
|
44
|
+
self.go_to(self._current - 1)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def current(self) -> int:
|
|
48
|
+
"""Get current page."""
|
|
49
|
+
return self._current
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def total(self) -> int:
|
|
53
|
+
"""Get total pages."""
|
|
54
|
+
return self._total
|
|
55
|
+
|
|
56
|
+
def render(self) -> None:
|
|
57
|
+
"""Re-render pagination."""
|
|
58
|
+
pass
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cronixui"
|
|
3
|
+
version = "1.0.2"
|
|
4
|
+
description = "CronixUI - A dark-themed UI toolkit with crimson accents and Outfit typography"
|
|
5
|
+
license = {text = "GPL-3.0"}
|
|
6
|
+
authors = [{name = "CazyUndee"}]
|
|
7
|
+
keywords = ["ui", "dark-mode", "design-system", "components"]
|
|
8
|
+
requires-python = ">=3.8"
|
|
9
|
+
|
|
10
|
+
[project.urls]
|
|
11
|
+
Repository = "https://github.com/CazyUndee/CronixUI"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Search component."""
|
|
2
|
+
|
|
3
|
+
from typing import Callable, Optional, List
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class SearchItem:
|
|
9
|
+
"""Search result item."""
|
|
10
|
+
|
|
11
|
+
title: str
|
|
12
|
+
subtitle: Optional[str] = None
|
|
13
|
+
action: Optional[Callable] = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Search:
|
|
17
|
+
"""Search component."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, element=None):
|
|
20
|
+
"""Initialize search on element."""
|
|
21
|
+
self._element = element
|
|
22
|
+
self._items: List[SearchItem] = []
|
|
23
|
+
self._open: bool = False
|
|
24
|
+
|
|
25
|
+
def set_items(self, items: List[SearchItem]) -> None:
|
|
26
|
+
"""Set searchable items."""
|
|
27
|
+
self._items = items
|
|
28
|
+
|
|
29
|
+
def filter(self, query: str) -> List[SearchItem]:
|
|
30
|
+
"""Filter items by query."""
|
|
31
|
+
query_lower = query.lower()
|
|
32
|
+
return [item for item in self._items if query_lower in item.title.lower()]
|
|
33
|
+
|
|
34
|
+
def open(self) -> None:
|
|
35
|
+
"""Open search results."""
|
|
36
|
+
self._open = True
|
|
37
|
+
|
|
38
|
+
def close(self) -> None:
|
|
39
|
+
"""Close search results."""
|
|
40
|
+
self._open = False
|
|
41
|
+
|
|
42
|
+
def is_open(self) -> bool:
|
|
43
|
+
"""Check if search is open."""
|
|
44
|
+
return self._open
|
|
45
|
+
|
|
46
|
+
def select(self, index: int) -> None:
|
|
47
|
+
"""Select and execute item by index."""
|
|
48
|
+
if 0 <= index < len(self._items) and self._items[index].action:
|
|
49
|
+
self._items[index].action()
|
|
50
|
+
self.close()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Tabs component."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Tabs:
|
|
5
|
+
"""Tabs component."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, element=None):
|
|
8
|
+
"""Initialize tabs on element."""
|
|
9
|
+
self._element = element
|
|
10
|
+
self._active_index: int = 0
|
|
11
|
+
|
|
12
|
+
def set_active(self, index: int) -> None:
|
|
13
|
+
"""Set active tab by index."""
|
|
14
|
+
self._active_index = index
|
|
15
|
+
|
|
16
|
+
def active_index(self) -> int:
|
|
17
|
+
"""Get active tab index."""
|
|
18
|
+
return self._active_index
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Toast notification component."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ToastType(Enum):
|
|
8
|
+
SUCCESS = "success"
|
|
9
|
+
ERROR = "error"
|
|
10
|
+
WARNING = "warning"
|
|
11
|
+
INFO = "info"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Toast:
|
|
15
|
+
"""Toast notification component."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, element=None):
|
|
18
|
+
self._element = element
|
|
19
|
+
self._title: Optional[str] = None
|
|
20
|
+
self._message: str = ""
|
|
21
|
+
self._type: ToastType = ToastType.INFO
|
|
22
|
+
self._duration: int = 4000
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def show(
|
|
26
|
+
title: Optional[str] = None,
|
|
27
|
+
message: str = "",
|
|
28
|
+
type: str = "info",
|
|
29
|
+
duration: int = 4000,
|
|
30
|
+
) -> "Toast":
|
|
31
|
+
"""Show a toast notification."""
|
|
32
|
+
toast = Toast()
|
|
33
|
+
toast._title = title
|
|
34
|
+
toast._message = message
|
|
35
|
+
toast._type = ToastType(type)
|
|
36
|
+
toast._duration = duration
|
|
37
|
+
return toast
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def success(message: str, title: Optional[str] = None) -> "Toast":
|
|
41
|
+
"""Show a success toast."""
|
|
42
|
+
return Toast.show(title=title, message=message, type="success")
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def error(message: str, title: Optional[str] = None) -> "Toast":
|
|
46
|
+
"""Show an error toast."""
|
|
47
|
+
return Toast.show(title=title, message=message, type="error")
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def warning(message: str, title: Optional[str] = None) -> "Toast":
|
|
51
|
+
"""Show a warning toast."""
|
|
52
|
+
return Toast.show(title=title, message=message, type="warning")
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def info(message: str, title: Optional[str] = None) -> "Toast":
|
|
56
|
+
"""Show an info toast."""
|
|
57
|
+
return Toast.show(title=title, message=message, type="info")
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def title(self) -> Optional[str]:
|
|
61
|
+
return self._title
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def message(self) -> str:
|
|
65
|
+
return self._message
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def type(self) -> ToastType:
|
|
69
|
+
return self._type
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def duration(self) -> int:
|
|
73
|
+
return self._duration
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Toggle switch component."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Toggle:
|
|
5
|
+
"""Toggle switch component."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, element=None):
|
|
8
|
+
"""Initialize toggle on element."""
|
|
9
|
+
self._element = element
|
|
10
|
+
self._on: bool = False
|
|
11
|
+
|
|
12
|
+
def toggle(self) -> None:
|
|
13
|
+
"""Toggle the state."""
|
|
14
|
+
self._on = not self._on
|
|
15
|
+
|
|
16
|
+
def is_on(self) -> bool:
|
|
17
|
+
"""Check if toggle is on."""
|
|
18
|
+
return self._on
|
|
19
|
+
|
|
20
|
+
def set_on(self, value: bool) -> None:
|
|
21
|
+
"""Set toggle state."""
|
|
22
|
+
self._on = value
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CronixUI Design Tokens
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class Color:
|
|
11
|
+
"""Color token."""
|
|
12
|
+
|
|
13
|
+
hex: str
|
|
14
|
+
rgb: Tuple[int, int, int]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Background colors
|
|
18
|
+
BG = Color("#0a0a0a", (10, 10, 10))
|
|
19
|
+
SURFACE = Color("#111111", (17, 17, 17))
|
|
20
|
+
SURFACE_2 = Color("#1a1a1a", (26, 26, 26))
|
|
21
|
+
SURFACE_3 = Color("#222222", (34, 34, 34))
|
|
22
|
+
SURFACE_4 = Color("#2a2a2a", (42, 42, 42))
|
|
23
|
+
|
|
24
|
+
# Text colors
|
|
25
|
+
TEXT = Color("#f0ede8", (240, 237, 232))
|
|
26
|
+
TEXT_MUTED = "rgba(240, 237, 232, 0.5)"
|
|
27
|
+
TEXT_DIM = "rgba(240, 237, 232, 0.25)"
|
|
28
|
+
|
|
29
|
+
# Accent (Crimson)
|
|
30
|
+
ACCENT = Color("#6b2323", (107, 35, 35))
|
|
31
|
+
ACCENT_HOVER = Color("#7d2a2a", (125, 42, 42))
|
|
32
|
+
ACCENT_LIGHT = Color("#8a3535", (138, 53, 53))
|
|
33
|
+
ACCENT_GLOW = "rgba(107, 35, 35, 0.3)"
|
|
34
|
+
ACCENT_TEXT = Color("#c97a7a", (201, 122, 122))
|
|
35
|
+
|
|
36
|
+
# Semantic colors
|
|
37
|
+
SUCCESS = Color("#1e5028", (30, 80, 40))
|
|
38
|
+
SUCCESS_BORDER = "rgba(60, 140, 70, 0.4)"
|
|
39
|
+
SUCCESS_TEXT = Color("#6bc47a", (107, 196, 122))
|
|
40
|
+
|
|
41
|
+
WARNING = Color("#503c14", (80, 60, 20))
|
|
42
|
+
WARNING_BORDER = "rgba(150, 110, 30, 0.4)"
|
|
43
|
+
WARNING_TEXT = Color("#c4a43a", (196, 164, 58))
|
|
44
|
+
|
|
45
|
+
ERROR = Color("#501414", (80, 20, 20))
|
|
46
|
+
ERROR_BORDER = "rgba(180, 60, 60, 0.4)"
|
|
47
|
+
ERROR_TEXT = Color("#c46b6b", (196, 107, 107))
|
|
48
|
+
|
|
49
|
+
INFO = Color("#143550", (20, 53, 80))
|
|
50
|
+
INFO_BORDER = "rgba(60, 140, 200, 0.4)"
|
|
51
|
+
INFO_TEXT = Color("#6ba8c4", (107, 168, 196))
|
|
52
|
+
|
|
53
|
+
# Border
|
|
54
|
+
BORDER = "rgba(255, 255, 255, 0.08)"
|
|
55
|
+
BORDER_HOVER = "rgba(255, 255, 255, 0.15)"
|
|
56
|
+
BORDER_FOCUS = "rgba(255, 255, 255, 0.25)"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True)
|
|
60
|
+
class Typography:
|
|
61
|
+
"""Typography tokens."""
|
|
62
|
+
|
|
63
|
+
font_family: str = (
|
|
64
|
+
"'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
|
|
65
|
+
)
|
|
66
|
+
font_mono: str = "'JetBrains Mono', 'Fira Code', 'Consolas', monospace"
|
|
67
|
+
|
|
68
|
+
# Font sizes
|
|
69
|
+
xs: int = 11
|
|
70
|
+
sm: int = 12
|
|
71
|
+
base: int = 13
|
|
72
|
+
md: int = 14
|
|
73
|
+
lg: int = 16
|
|
74
|
+
xl: int = 20
|
|
75
|
+
xxl: int = 28
|
|
76
|
+
xxxl: int = 36
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass(frozen=True)
|
|
80
|
+
class Spacing:
|
|
81
|
+
"""Spacing tokens."""
|
|
82
|
+
|
|
83
|
+
space_1: int = 4
|
|
84
|
+
space_2: int = 8
|
|
85
|
+
space_3: int = 12
|
|
86
|
+
space_4: int = 16
|
|
87
|
+
space_5: int = 20
|
|
88
|
+
space_6: int = 24
|
|
89
|
+
space_8: int = 32
|
|
90
|
+
space_10: int = 40
|
|
91
|
+
space_12: int = 48
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass(frozen=True)
|
|
95
|
+
class Radius:
|
|
96
|
+
"""Border radius tokens."""
|
|
97
|
+
|
|
98
|
+
sm: int = 6
|
|
99
|
+
default: int = 10
|
|
100
|
+
lg: int = 14
|
|
101
|
+
xl: int = 20
|
|
102
|
+
full: int = 9999
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class Shadow:
|
|
107
|
+
"""Shadow tokens."""
|
|
108
|
+
|
|
109
|
+
sm: str = "0 1px 2px rgba(0, 0, 0, 0.3)"
|
|
110
|
+
default: str = "0 4px 12px rgba(0, 0, 0, 0.4)"
|
|
111
|
+
lg: str = "0 8px 24px rgba(0, 0, 0, 0.5)"
|
|
112
|
+
glow: str = "0 0 20px rgba(107, 35, 35, 0.3)"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass(frozen=True)
|
|
116
|
+
class Transition:
|
|
117
|
+
"""Transition tokens."""
|
|
118
|
+
|
|
119
|
+
fast: str = "0.1s ease"
|
|
120
|
+
default: str = "0.15s ease"
|
|
121
|
+
slow: str = "0.25s ease"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass(frozen=True)
|
|
125
|
+
class ZIndex:
|
|
126
|
+
"""Z-index tokens."""
|
|
127
|
+
|
|
128
|
+
dropdown: int = 100
|
|
129
|
+
sticky: int = 200
|
|
130
|
+
fixed: int = 300
|
|
131
|
+
modal_backdrop: int = 400
|
|
132
|
+
modal: int = 500
|
|
133
|
+
popover: int = 600
|
|
134
|
+
tooltip: int = 700
|
|
135
|
+
toast: int = 800
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass(frozen=True)
|
|
139
|
+
class Layout:
|
|
140
|
+
"""Layout tokens."""
|
|
141
|
+
|
|
142
|
+
container_max: int = 1200
|
|
143
|
+
sidebar_width: int = 260
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# Token instances
|
|
147
|
+
typography = Typography()
|
|
148
|
+
spacing = Spacing()
|
|
149
|
+
radius = Radius()
|
|
150
|
+
shadow = Shadow()
|
|
151
|
+
transition = Transition()
|
|
152
|
+
z_index = ZIndex()
|
|
153
|
+
layout = Layout()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
__all__ = [
|
|
157
|
+
"Color",
|
|
158
|
+
"BG",
|
|
159
|
+
"SURFACE",
|
|
160
|
+
"SURFACE_2",
|
|
161
|
+
"SURFACE_3",
|
|
162
|
+
"SURFACE_4",
|
|
163
|
+
"TEXT",
|
|
164
|
+
"TEXT_MUTED",
|
|
165
|
+
"TEXT_DIM",
|
|
166
|
+
"ACCENT",
|
|
167
|
+
"ACCENT_HOVER",
|
|
168
|
+
"ACCENT_LIGHT",
|
|
169
|
+
"ACCENT_GLOW",
|
|
170
|
+
"ACCENT_TEXT",
|
|
171
|
+
"SUCCESS",
|
|
172
|
+
"SUCCESS_BORDER",
|
|
173
|
+
"SUCCESS_TEXT",
|
|
174
|
+
"WARNING",
|
|
175
|
+
"WARNING_BORDER",
|
|
176
|
+
"WARNING_TEXT",
|
|
177
|
+
"ERROR",
|
|
178
|
+
"ERROR_BORDER",
|
|
179
|
+
"ERROR_TEXT",
|
|
180
|
+
"INFO",
|
|
181
|
+
"INFO_BORDER",
|
|
182
|
+
"INFO_TEXT",
|
|
183
|
+
"BORDER",
|
|
184
|
+
"BORDER_HOVER",
|
|
185
|
+
"BORDER_FOCUS",
|
|
186
|
+
"Typography",
|
|
187
|
+
"typography",
|
|
188
|
+
"Spacing",
|
|
189
|
+
"spacing",
|
|
190
|
+
"Radius",
|
|
191
|
+
"radius",
|
|
192
|
+
"Shadow",
|
|
193
|
+
"shadow",
|
|
194
|
+
"Transition",
|
|
195
|
+
"transition",
|
|
196
|
+
"ZIndex",
|
|
197
|
+
"z_index",
|
|
198
|
+
"Layout",
|
|
199
|
+
"layout",
|
|
200
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
use std::collections::HashSet;
|
|
2
|
+
|
|
3
|
+
pub struct Accordion {
|
|
4
|
+
open_indices: HashSet<usize>,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
impl Accordion {
|
|
8
|
+
pub fn new() -> Self {
|
|
9
|
+
Self {
|
|
10
|
+
open_indices: HashSet::new(),
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pub fn toggle(&mut self, index: usize) {
|
|
15
|
+
if self.open_indices.contains(&index) {
|
|
16
|
+
self.open_indices.remove(&index);
|
|
17
|
+
} else {
|
|
18
|
+
self.open_indices.insert(index);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn open(&mut self, index: usize) {
|
|
23
|
+
self.open_indices.insert(index);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn close(&mut self, index: usize) {
|
|
27
|
+
self.open_indices.remove(&index);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pub fn open_all(&mut self, total: usize) {
|
|
31
|
+
for i in 0..total {
|
|
32
|
+
self.open_indices.insert(i);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub fn close_all(&mut self) {
|
|
37
|
+
self.open_indices.clear();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pub fn is_open(&self, index: usize) -> bool {
|
|
41
|
+
self.open_indices.contains(&index)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
impl Default for Accordion {
|
|
46
|
+
fn default() -> Self {
|
|
47
|
+
Self::new()
|
|
48
|
+
}
|
|
49
|
+
}
|