cronixui 1.0.3 → 1.0.5
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 +21 -2
- package/packages/go/cronixui/cronixui.go +379 -0
- package/packages/go/cronixui/go.mod +10 -0
- package/packages/python/cronixui/__init__.py +35 -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/react/src/index.d.ts +103 -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 +79 -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/web/src/index.js +2 -0
- package/packages/web/src/index.mjs +2 -0
|
@@ -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,103 @@
|
|
|
1
|
+
declare namespace CronixUI {
|
|
2
|
+
interface ToastOptions {
|
|
3
|
+
title?: string;
|
|
4
|
+
message: string;
|
|
5
|
+
type?: 'success' | 'error' | 'warning' | 'info';
|
|
6
|
+
duration?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface PaginationOptions {
|
|
10
|
+
total: number;
|
|
11
|
+
current: number;
|
|
12
|
+
onChange?: (page: number) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface SearchItem {
|
|
16
|
+
title: string;
|
|
17
|
+
subtitle?: string;
|
|
18
|
+
action: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface CommandPaletteItem {
|
|
22
|
+
title: string;
|
|
23
|
+
kbd?: string;
|
|
24
|
+
action: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ModalInstance {
|
|
28
|
+
open(): void;
|
|
29
|
+
close(): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface DropdownInstance {
|
|
33
|
+
open(): void;
|
|
34
|
+
close(): void;
|
|
35
|
+
toggle(): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface ToggleInstance {
|
|
39
|
+
toggle(): void;
|
|
40
|
+
isOn(): boolean;
|
|
41
|
+
setOn(value: boolean): void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface TabsInstance {
|
|
45
|
+
setActive(index: number): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface AccordionInstance {
|
|
49
|
+
toggle(item: HTMLElement): void;
|
|
50
|
+
openAll(): void;
|
|
51
|
+
closeAll(): void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface PaginationInstance {
|
|
55
|
+
goTo(page: number): void;
|
|
56
|
+
render(): void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface CommandPaletteInstance {
|
|
60
|
+
open(): void;
|
|
61
|
+
close(): void;
|
|
62
|
+
setItems(items: CommandPaletteItem[]): void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface SearchInstance {
|
|
66
|
+
setItems(items: SearchItem[]): void;
|
|
67
|
+
filter(query: string): SearchItem[];
|
|
68
|
+
open(): void;
|
|
69
|
+
close(): void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface ToastStatic {
|
|
73
|
+
show(options: ToastOptions): void;
|
|
74
|
+
success(message: string): void;
|
|
75
|
+
error(message: string): void;
|
|
76
|
+
warning(message: string): void;
|
|
77
|
+
info(message: string): void;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface NavStatic {
|
|
81
|
+
init(): void;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface CronixUIStatic {
|
|
85
|
+
init(): void;
|
|
86
|
+
$(selector: string): HTMLElement | null;
|
|
87
|
+
$$(selector: string): NodeListOf<HTMLElement>;
|
|
88
|
+
createEl(tag: string, className?: string, attrs?: Record<string, string>): HTMLElement;
|
|
89
|
+
Toast: ToastStatic;
|
|
90
|
+
Nav: NavStatic;
|
|
91
|
+
Toggle: new (element: HTMLElement) => ToggleInstance;
|
|
92
|
+
Modal: new (element: HTMLElement) => ModalInstance;
|
|
93
|
+
Dropdown: new (element: HTMLElement) => DropdownInstance;
|
|
94
|
+
Tabs: new (element: HTMLElement) => TabsInstance;
|
|
95
|
+
Accordion: new (element: HTMLElement) => AccordionInstance;
|
|
96
|
+
Pagination: new (element: HTMLElement, options: PaginationOptions) => PaginationInstance;
|
|
97
|
+
CommandPalette: new (element: HTMLElement) => CommandPaletteInstance;
|
|
98
|
+
Search: new (element: HTMLElement) => SearchInstance;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
declare const CronixUI: CronixUI.CronixUIStatic;
|
|
103
|
+
export default CronixUI;
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
pub struct CommandPaletteItem {
|
|
2
|
+
pub title: String,
|
|
3
|
+
pub kbd: Option<String>,
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
impl CommandPaletteItem {
|
|
7
|
+
pub fn new(title: impl Into<String>) -> Self {
|
|
8
|
+
Self {
|
|
9
|
+
title: title.into(),
|
|
10
|
+
kbd: None,
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pub fn kbd(mut self, kbd: impl Into<String>) -> Self {
|
|
15
|
+
self.kbd = Some(kbd.into());
|
|
16
|
+
self
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pub struct CommandPalette {
|
|
21
|
+
open: bool,
|
|
22
|
+
items: Vec<CommandPaletteItem>,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
impl CommandPalette {
|
|
26
|
+
pub fn new() -> Self {
|
|
27
|
+
Self {
|
|
28
|
+
open: false,
|
|
29
|
+
items: Vec::new(),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub fn open(&mut self) {
|
|
34
|
+
self.open = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub fn close(&mut self) {
|
|
38
|
+
self.open = false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub fn toggle(&mut self) {
|
|
42
|
+
self.open = !self.open;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
pub fn is_open(&self) -> bool {
|
|
46
|
+
self.open
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
pub fn set_items(&mut self, items: Vec<CommandPaletteItem>) {
|
|
50
|
+
self.items = items;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pub fn items(&self) -> &[CommandPaletteItem] {
|
|
54
|
+
&self.items
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
impl Default for CommandPalette {
|
|
59
|
+
fn default() -> Self {
|
|
60
|
+
Self::new()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
pub struct Dropdown {
|
|
2
|
+
open: bool,
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
impl Dropdown {
|
|
6
|
+
pub fn new() -> Self {
|
|
7
|
+
Self { open: false }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
pub fn open(&mut self) {
|
|
11
|
+
self.open = true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pub fn close(&mut self) {
|
|
15
|
+
self.open = false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
pub fn toggle(&mut self) {
|
|
19
|
+
self.open = !self.open;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn is_open(&self) -> bool {
|
|
23
|
+
self.open
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl Default for Dropdown {
|
|
28
|
+
fn default() -> Self {
|
|
29
|
+
Self::new()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//! CronixUI - A dark-themed UI toolkit with crimson accents and Outfit typography
|
|
2
|
+
//!
|
|
3
|
+
//! ## Example
|
|
4
|
+
//!
|
|
5
|
+
//! ```rust
|
|
6
|
+
//! use cronixui::{Toast, Toggle, Modal};
|
|
7
|
+
//!
|
|
8
|
+
//! // Create a toast
|
|
9
|
+
//! let toast = Toast::success("Operation completed!");
|
|
10
|
+
//!
|
|
11
|
+
//! // Create a toggle
|
|
12
|
+
//! let mut toggle = Toggle::new();
|
|
13
|
+
//! toggle.toggle();
|
|
14
|
+
//! assert!(toggle.is_on());
|
|
15
|
+
//! ```
|
|
16
|
+
|
|
17
|
+
pub const VERSION: &str = "1.0.4";
|
|
18
|
+
|
|
19
|
+
mod toast;
|
|
20
|
+
mod toggle;
|
|
21
|
+
mod modal;
|
|
22
|
+
mod dropdown;
|
|
23
|
+
mod tabs;
|
|
24
|
+
mod accordion;
|
|
25
|
+
mod pagination;
|
|
26
|
+
mod command_palette;
|
|
27
|
+
mod search;
|
|
28
|
+
|
|
29
|
+
pub use toast::{Toast, ToastType};
|
|
30
|
+
pub use toggle::Toggle;
|
|
31
|
+
pub use modal::Modal;
|
|
32
|
+
pub use dropdown::Dropdown;
|
|
33
|
+
pub use tabs::Tabs;
|
|
34
|
+
pub use accordion::Accordion;
|
|
35
|
+
pub use pagination::Pagination;
|
|
36
|
+
pub use command_palette::{CommandPalette, CommandPaletteItem};
|
|
37
|
+
pub use search::{Search, SearchItem};
|
|
38
|
+
|
|
39
|
+
/// Initialize CronixUI
|
|
40
|
+
pub fn init() {
|
|
41
|
+
println!("CronixUI {} initialized", VERSION);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#[cfg(test)]
|
|
45
|
+
mod tests {
|
|
46
|
+
use super::*;
|
|
47
|
+
|
|
48
|
+
#[test]
|
|
49
|
+
fn test_toggle() {
|
|
50
|
+
let mut toggle = Toggle::new();
|
|
51
|
+
assert!(!toggle.is_on());
|
|
52
|
+
toggle.toggle();
|
|
53
|
+
assert!(toggle.is_on());
|
|
54
|
+
toggle.set_on(false);
|
|
55
|
+
assert!(!toggle.is_on());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#[test]
|
|
59
|
+
fn test_pagination() {
|
|
60
|
+
let mut pagination = Pagination::new(10, 1);
|
|
61
|
+
assert_eq!(pagination.current(), 1);
|
|
62
|
+
pagination.next();
|
|
63
|
+
assert_eq!(pagination.current(), 2);
|
|
64
|
+
pagination.go_to(5);
|
|
65
|
+
assert_eq!(pagination.current(), 5);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#[test]
|
|
69
|
+
fn test_search() {
|
|
70
|
+
let mut search = Search::new();
|
|
71
|
+
search.set_items(vec![
|
|
72
|
+
SearchItem::new("Apple"),
|
|
73
|
+
SearchItem::new("Banana"),
|
|
74
|
+
SearchItem::new("Apricot"),
|
|
75
|
+
]);
|
|
76
|
+
let results = search.filter("ap");
|
|
77
|
+
assert_eq!(results.len(), 2);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
pub struct Modal {
|
|
2
|
+
open: bool,
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
impl Modal {
|
|
6
|
+
pub fn new() -> Self {
|
|
7
|
+
Self { open: false }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
pub fn open(&mut self) {
|
|
11
|
+
self.open = true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pub fn close(&mut self) {
|
|
15
|
+
self.open = false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
pub fn is_open(&self) -> bool {
|
|
19
|
+
self.open
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
impl Default for Modal {
|
|
24
|
+
fn default() -> Self {
|
|
25
|
+
Self::new()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
pub struct Pagination {
|
|
2
|
+
total: usize,
|
|
3
|
+
current: usize,
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
impl Pagination {
|
|
7
|
+
pub fn new(total: usize, current: usize) -> Self {
|
|
8
|
+
let current = current.clamp(1, total.max(1));
|
|
9
|
+
Self { total, current }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
pub fn go_to(&mut self, page: usize) {
|
|
13
|
+
if page >= 1 && page <= self.total {
|
|
14
|
+
self.current = page;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
pub fn current(&self) -> usize {
|
|
19
|
+
self.current
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn total(&self) -> usize {
|
|
23
|
+
self.total
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn next(&mut self) {
|
|
27
|
+
if self.current < self.total {
|
|
28
|
+
self.current += 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pub fn prev(&mut self) {
|
|
33
|
+
if self.current > 1 {
|
|
34
|
+
self.current -= 1;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
pub struct SearchItem {
|
|
2
|
+
pub title: String,
|
|
3
|
+
pub subtitle: Option<String>,
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
impl SearchItem {
|
|
7
|
+
pub fn new(title: impl Into<String>) -> Self {
|
|
8
|
+
Self {
|
|
9
|
+
title: title.into(),
|
|
10
|
+
subtitle: None,
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pub fn subtitle(mut self, subtitle: impl Into<String>) -> Self {
|
|
15
|
+
self.subtitle = Some(subtitle.into());
|
|
16
|
+
self
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pub struct Search {
|
|
21
|
+
items: Vec<SearchItem>,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
impl Search {
|
|
25
|
+
pub fn new() -> Self {
|
|
26
|
+
Self { items: Vec::new() }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
pub fn set_items(&mut self, items: Vec<SearchItem>) {
|
|
30
|
+
self.items = items;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub fn filter(&self, query: &str) -> Vec<&SearchItem> {
|
|
34
|
+
self.items
|
|
35
|
+
.iter()
|
|
36
|
+
.filter(|item| item.title.to_lowercase().contains(&query.to_lowercase()))
|
|
37
|
+
.collect()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pub fn items(&self) -> &[SearchItem] {
|
|
41
|
+
&self.items
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
impl Default for Search {
|
|
46
|
+
fn default() -> Self {
|
|
47
|
+
Self::new()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
pub struct Tabs {
|
|
2
|
+
active_index: usize,
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
impl Tabs {
|
|
6
|
+
pub fn new() -> Self {
|
|
7
|
+
Self { active_index: 0 }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
pub fn set_active(&mut self, index: usize) {
|
|
11
|
+
self.active_index = index;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pub fn active_index(&self) -> usize {
|
|
15
|
+
self.active_index
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
impl Default for Tabs {
|
|
20
|
+
fn default() -> Self {
|
|
21
|
+
Self::new()
|
|
22
|
+
}
|
|
23
|
+
}
|