From 516eb0e8bc784e6ff6e9a41a656052eb53472fc8 Mon Sep 17 00:00:00 2001 From: reya Date: Tue, 10 Dec 2024 09:40:27 +0700 Subject: [PATCH] move gpui-components to ui crate --- .DS_Store | Bin 6148 -> 6148 bytes Cargo.lock | 242 +-- Cargo.toml | 3 +- crates/.DS_Store | Bin 0 -> 6148 bytes crates/app/Cargo.toml | 5 +- crates/app/src/main.rs | 4 +- crates/app/src/views/app.rs | 2 +- crates/app/src/views/dock/chat/mod.rs | 2 +- crates/app/src/views/dock/inbox/chat.rs | 2 +- crates/app/src/views/dock/inbox/mod.rs | 2 +- crates/app/src/views/dock/left_dock.rs | 2 +- crates/app/src/views/dock/welcome.rs | 2 +- crates/app/src/views/onboarding.rs | 4 +- crates/ui/.DS_Store | Bin 0 -> 6148 bytes crates/ui/Cargo.toml | 19 +- crates/ui/LICENSE | 201 +++ crates/ui/README.md | 0 crates/ui/colors.json | 2000 ++++++++++++++++++++++ crates/ui/src/.DS_Store | Bin 0 -> 6148 bytes crates/ui/src/accordion.rs | 300 ++++ crates/ui/src/animation.rs | 19 + crates/ui/src/badge.rs | 123 ++ crates/ui/src/breadcrumb.rs | 118 ++ crates/ui/src/button.rs | 667 ++++++++ crates/ui/src/button_group.rs | 198 +++ crates/ui/src/checkbox.rs | 131 ++ crates/ui/src/clipboard.rs | 152 ++ crates/ui/src/color_picker.rs | 377 ++++ crates/ui/src/colors.rs | 297 ++++ crates/ui/src/context_menu.rs | 230 +++ crates/ui/src/divider.rs | 84 + crates/ui/src/dock/dock.rs | 443 +++++ crates/ui/src/dock/invalid_panel.rs | 55 + crates/ui/src/dock/mod.rs | 811 +++++++++ crates/ui/src/dock/panel.rs | 186 ++ crates/ui/src/dock/stack_panel.rs | 379 ++++ crates/ui/src/dock/state.rs | 201 +++ crates/ui/src/dock/tab_panel.rs | 888 ++++++++++ crates/ui/src/drawer.rs | 244 +++ crates/ui/src/dropdown.rs | 713 ++++++++ crates/ui/src/event.rs | 22 + crates/ui/src/focusable.rs | 40 + crates/ui/src/history.rs | 197 +++ crates/ui/src/icon.rs | 319 ++++ crates/ui/src/indicator.rs | 60 + crates/ui/src/input/blink_cursor.rs | 88 + crates/ui/src/input/change.rs | 39 + crates/ui/src/input/clear_button.rs | 18 + crates/ui/src/input/element.rs | 537 ++++++ crates/ui/src/input/input.rs | 1269 ++++++++++++++ crates/ui/src/input/mod.rs | 10 + crates/ui/src/input/otp_input.rs | 274 +++ crates/ui/src/label.rs | 94 + crates/ui/src/lib.rs | 81 + crates/ui/src/link.rs | 93 + crates/ui/src/list/list.rs | 465 +++++ crates/ui/src/list/list_item.rs | 169 ++ crates/ui/src/list/mod.rs | 5 + crates/ui/src/modal.rs | 248 +++ crates/ui/src/notification.rs | 369 ++++ crates/ui/src/number_input.rs | 168 ++ crates/ui/src/popover.rs | 408 +++++ crates/ui/src/popup_menu.rs | 778 +++++++++ crates/ui/src/prelude.rs | 9 + crates/ui/src/progress.rs | 56 + crates/ui/src/radio.rs | 109 ++ crates/ui/src/resizable/mod.rs | 18 + crates/ui/src/resizable/panel.rs | 487 ++++++ crates/ui/src/resizable/resize_handle.rs | 72 + crates/ui/src/root.rs | 355 ++++ crates/ui/src/scroll/mod.rs | 7 + crates/ui/src/scroll/scrollable.rs | 231 +++ crates/ui/src/scroll/scrollable_mask.rs | 160 ++ crates/ui/src/scroll/scrollbar.rs | 630 +++++++ crates/ui/src/sidebar/footer.rs | 77 + crates/ui/src/sidebar/group.rs | 71 + crates/ui/src/sidebar/header.rs | 77 + crates/ui/src/sidebar/menu.rs | 237 +++ crates/ui/src/sidebar/mod.rs | 212 +++ crates/ui/src/skeleton.rs | 42 + crates/ui/src/slider.rs | 196 +++ crates/ui/src/styled.rs | 426 +++++ crates/ui/src/svg_img.rs | 295 ++++ crates/ui/src/switch.rs | 234 +++ crates/ui/src/tab.rs | 5 + crates/ui/src/tab/tab.rs | 99 ++ crates/ui/src/tab/tab_bar.rs | 95 + crates/ui/src/table.rs | 1226 +++++++++++++ crates/ui/src/theme.rs | 537 ++++++ crates/ui/src/title_bar.rs | 330 ++++ crates/ui/src/tooltip.rs | 38 + 91 files changed, 20957 insertions(+), 231 deletions(-) create mode 100644 crates/.DS_Store create mode 100644 crates/ui/.DS_Store create mode 100644 crates/ui/LICENSE create mode 100644 crates/ui/README.md create mode 100644 crates/ui/colors.json create mode 100644 crates/ui/src/.DS_Store create mode 100644 crates/ui/src/accordion.rs create mode 100644 crates/ui/src/animation.rs create mode 100644 crates/ui/src/badge.rs create mode 100644 crates/ui/src/breadcrumb.rs create mode 100644 crates/ui/src/button.rs create mode 100644 crates/ui/src/button_group.rs create mode 100644 crates/ui/src/checkbox.rs create mode 100644 crates/ui/src/clipboard.rs create mode 100644 crates/ui/src/color_picker.rs create mode 100644 crates/ui/src/colors.rs create mode 100644 crates/ui/src/context_menu.rs create mode 100644 crates/ui/src/divider.rs create mode 100644 crates/ui/src/dock/dock.rs create mode 100644 crates/ui/src/dock/invalid_panel.rs create mode 100644 crates/ui/src/dock/mod.rs create mode 100644 crates/ui/src/dock/panel.rs create mode 100644 crates/ui/src/dock/stack_panel.rs create mode 100644 crates/ui/src/dock/state.rs create mode 100644 crates/ui/src/dock/tab_panel.rs create mode 100644 crates/ui/src/drawer.rs create mode 100644 crates/ui/src/dropdown.rs create mode 100644 crates/ui/src/event.rs create mode 100644 crates/ui/src/focusable.rs create mode 100644 crates/ui/src/history.rs create mode 100644 crates/ui/src/icon.rs create mode 100644 crates/ui/src/indicator.rs create mode 100644 crates/ui/src/input/blink_cursor.rs create mode 100644 crates/ui/src/input/change.rs create mode 100644 crates/ui/src/input/clear_button.rs create mode 100644 crates/ui/src/input/element.rs create mode 100644 crates/ui/src/input/input.rs create mode 100644 crates/ui/src/input/mod.rs create mode 100644 crates/ui/src/input/otp_input.rs create mode 100644 crates/ui/src/label.rs create mode 100644 crates/ui/src/link.rs create mode 100644 crates/ui/src/list/list.rs create mode 100644 crates/ui/src/list/list_item.rs create mode 100644 crates/ui/src/list/mod.rs create mode 100644 crates/ui/src/modal.rs create mode 100644 crates/ui/src/notification.rs create mode 100644 crates/ui/src/number_input.rs create mode 100644 crates/ui/src/popover.rs create mode 100644 crates/ui/src/popup_menu.rs create mode 100644 crates/ui/src/prelude.rs create mode 100644 crates/ui/src/progress.rs create mode 100644 crates/ui/src/radio.rs create mode 100644 crates/ui/src/resizable/mod.rs create mode 100644 crates/ui/src/resizable/panel.rs create mode 100644 crates/ui/src/resizable/resize_handle.rs create mode 100644 crates/ui/src/root.rs create mode 100644 crates/ui/src/scroll/mod.rs create mode 100644 crates/ui/src/scroll/scrollable.rs create mode 100644 crates/ui/src/scroll/scrollable_mask.rs create mode 100644 crates/ui/src/scroll/scrollbar.rs create mode 100644 crates/ui/src/sidebar/footer.rs create mode 100644 crates/ui/src/sidebar/group.rs create mode 100644 crates/ui/src/sidebar/header.rs create mode 100644 crates/ui/src/sidebar/menu.rs create mode 100644 crates/ui/src/sidebar/mod.rs create mode 100644 crates/ui/src/skeleton.rs create mode 100644 crates/ui/src/slider.rs create mode 100644 crates/ui/src/styled.rs create mode 100644 crates/ui/src/svg_img.rs create mode 100644 crates/ui/src/switch.rs create mode 100644 crates/ui/src/tab.rs create mode 100644 crates/ui/src/tab/tab.rs create mode 100644 crates/ui/src/tab/tab_bar.rs create mode 100644 crates/ui/src/table.rs create mode 100644 crates/ui/src/theme.rs create mode 100644 crates/ui/src/title_bar.rs create mode 100644 crates/ui/src/tooltip.rs diff --git a/.DS_Store b/.DS_Store index 4de7a025d2c46811ea31e708fcb7a7790949c6f2..13bff6e17a670e2be3473577fb040edfb28151d0 100644 GIT binary patch delta 69 zcmZoMXfc@J&&a(oU^g=(_hufJ&5Yb^49N^d42cXS45^ddSWb(>c?`v6!9{sF`FZIK S3=E8$_pnAYZf58B%MSpFk`nI# delta 32 ocmZoMXfc@J&&atkU^g=(=Vl(3&5WDZvqdmYZ1}L5o#QV*0Ik;xA^-pY diff --git a/Cargo.lock b/Cargo.lock index c903f86..b9f570b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,12 +94,6 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - [[package]] name = "arg_enum_proc_macro" version = "0.3.4" @@ -503,12 +497,6 @@ dependencies = [ "bitcoin_hashes 0.14.0", ] -[[package]] -name = "base62" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fa474cf7492f9a299ba6019fb99ec673e1739556d48e8a90eabaea282ef0e4" - [[package]] name = "base64" version = "0.22.1" @@ -868,7 +856,7 @@ dependencies = [ "serde_json", "syn 2.0.90", "tempfile", - "toml 0.8.19", + "toml", ] [[package]] @@ -1118,15 +1106,29 @@ dependencies = [ "smol", "tokio", "tracing-subscriber", - "ui", ] [[package]] name = "coop-ui" version = "0.1.0" dependencies = [ + "anyhow", + "chrono", "gpui", - "ui", + "image", + "itertools 0.13.0", + "once_cell", + "paste", + "regex", + "resvg", + "rust-embed", + "serde", + "serde_json", + "smallvec", + "smol", + "unicode-segmentation", + "usvg", + "uuid", ] [[package]] @@ -1510,7 +1512,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.19", + "toml", "vswhom", "winreg", ] @@ -2045,17 +2047,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags 1.3.2", - "ignore", - "walkdir", -] - [[package]] name = "gloo-timers" version = "0.3.0" @@ -2648,22 +2639,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "ignore" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - [[package]] name = "image" version = "0.25.5" @@ -2788,15 +2763,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -2961,12 +2927,6 @@ dependencies = [ "libc", ] -[[package]] -name = "libyml" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64804cc6a5042d4f05379909ba25b503ec04e2c082151d62122d5dcaa274b961" - [[package]] name = "linkme" version = "0.3.31" @@ -3271,15 +3231,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" -[[package]] -name = "normpath" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "nostr" version = "0.37.0" @@ -3897,7 +3848,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.22.22", + "toml_edit", ] [[package]] @@ -4389,60 +4340,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "rust-i18n" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039f57d22229db401af3458ca939300178e99e88b938573cea12b7c2b0f09724" -dependencies = [ - "globwalk", - "once_cell", - "regex", - "rust-i18n-macro", - "rust-i18n-support", - "smallvec", -] - -[[package]] -name = "rust-i18n-macro" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde5c022360a2e54477882843d56b6f9bcb4bc62f504b651a2f497f0028d174f" -dependencies = [ - "glob", - "once_cell", - "proc-macro2", - "quote", - "rust-i18n-support", - "serde", - "serde_json", - "serde_yml", - "syn 2.0.90", -] - -[[package]] -name = "rust-i18n-support" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75d2844d36f62b5d6b66f9cf8f8cbdbbbdcdb5fd37a473a9cc2fb45fdcf485d2" -dependencies = [ - "arc-swap", - "base62", - "globwalk", - "itertools 0.11.0", - "lazy_static", - "normpath", - "once_cell", - "proc-macro2", - "regex", - "serde", - "serde_json", - "serde_yml", - "siphasher 1.0.1", - "toml 0.7.8", - "triomphe", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -4862,23 +4759,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_yml" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e76bab63c3fd98d27c17f9cbce177f64a91f5e69ac04cafe04e1bb25d1dc3c" -dependencies = [ - "indexmap", - "itoa", - "libyml", - "log", - "memchr", - "ryu", - "serde", - "serde_json", - "tempfile", -] - [[package]] name = "sha1" version = "0.10.6" @@ -5310,7 +5190,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.19", + "toml", "version-compare", ] @@ -5573,18 +5453,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.19" @@ -5594,7 +5462,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.22", + "toml_edit", ] [[package]] @@ -5606,19 +5474,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.22" @@ -5629,7 +5484,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.20", + "winnow", ] [[package]] @@ -5695,17 +5550,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "triomphe" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" -dependencies = [ - "arc-swap", - "serde", - "stable_deref_trait", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -5770,31 +5614,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "ui" -version = "0.1.0" -source = "git+https://github.com/lumehq/gpui-component#ca5232d1e7e66defe02271781ceadf2a81ed0e3d" -dependencies = [ - "anyhow", - "chrono", - "gpui", - "image", - "itertools 0.13.0", - "once_cell", - "paste", - "regex", - "resvg", - "rust-embed", - "rust-i18n", - "serde", - "serde_json", - "smallvec", - "smol", - "unicode-segmentation", - "usvg", - "uuid", -] - [[package]] name = "unicase" version = "2.8.0" @@ -6575,15 +6394,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.6.20" @@ -6829,7 +6639,7 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.59.0", - "winnow 0.6.20", + "winnow", "xdg-home", "zbus_macros 5.1.1", "zbus_names 4.1.0", @@ -6883,7 +6693,7 @@ checksum = "856b7a38811f71846fd47856ceee8bccaec8399ff53fb370247e66081ace647b" dependencies = [ "serde", "static_assertions", - "winnow 0.6.20", + "winnow", "zvariant 5.1.0", ] @@ -7025,7 +6835,7 @@ dependencies = [ "serde", "static_assertions", "url", - "winnow 0.6.20", + "winnow", "zvariant_derive 5.1.0", "zvariant_utils 3.0.2", ] @@ -7078,5 +6888,5 @@ dependencies = [ "serde", "static_assertions", "syn 2.0.90", - "winnow 0.6.20", + "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 9218e2f..f9f8599 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ coop = { path = "crates/*" } # UI gpui = { git = "https://github.com/zed-industries/zed" } reqwest_client = { git = "https://github.com/zed-industries/zed" } -components = { package = "ui", git = "https://github.com/lumehq/gpui-component" } # Nostr nostr-relay-builder = { git = "https://github.com/rust-nostr/nostr" } @@ -18,6 +17,7 @@ nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = [ "all-nips", ] } +smol = "1" tokio = { version = "1", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -28,6 +28,7 @@ chrono = "0.4.38" tracing = "0.1.40" anyhow = "1.0.44" smallvec = "1.13.2" +rust-embed = "8.5.0" keyring-search = "1.2.0" keyring = { version = "3", features = [ "apple-native", diff --git a/crates/.DS_Store b/crates/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e7d927b8f528029c0d9b8f148dc4680807d749cb GIT binary patch literal 6148 zcmeHKQA@)x5Kgw~GKSCxg*^s*9k_{3#g|g&AF!eiDzl|Si?tbRYahm-&-#b_CH@}o zk`$coSw!w2`R;OeN%KK-31f_VMKom0VT=iAh#ZwQg2QWF4I7NeagH!iA?WYnEQzh} zuLFL2gB2_e%WU@b`?Gv7_~4CZYh$x5+M+A&{6|&#d5{;G7tC+ax>PC&Ydr|B;%PZ? zw$D_W2XQ)`>x4L(LdflPoJOkjRFOuR&h?E$bVO(3^cIW#lTlv|M}uWwE{;zQ`f_x5 zv|M(?&hFmn#pEe@PSvX+lLOaUwk+2028!Oxi$6~jl|F!7$JQ}~!~iis3=jjvfUOOp zOH>d8!~ikyjR8C#Bs4_VVr5Vt9nj(R8T~Cp6wvW4fhaAy7Au3`0pTVU(4=zv#NZ|! z#-)vOEmj6iI^%j}n8&VMKVGvTZ6 N2xvm6BL;qffltz^NU#6^ literal 0 HcmV?d00001 diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index 1069879..f9a2c6a 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -12,7 +12,6 @@ path = "src/main.rs" coop-ui = { path = "../ui" } gpui.workspace = true -components.workspace = true reqwest_client.workspace = true tokio.workspace = true @@ -25,7 +24,7 @@ serde_json.workspace = true itertools.workspace = true chrono.workspace = true dirs.workspace = true +rust-embed.workspace = true +smol.workspace = true tracing-subscriber = { version = "0.3.18", features = ["fmt"] } -rust-embed = "8.5.0" -smol = "1" diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index 89ec862..fb71539 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -1,5 +1,5 @@ use asset::Assets; -use components::Root; +use coop_ui::Root; use dirs::config_dir; use gpui::*; use nostr_sdk::prelude::*; @@ -76,7 +76,7 @@ async fn main() { AccountState::set_global(cx); // Initialize components - components::init(cx); + coop_ui::init(cx); // Set quit action cx.on_action(quit); diff --git a/crates/app/src/views/app.rs b/crates/app/src/views/app.rs index 69a8663..122b617 100644 --- a/crates/app/src/views/app.rs +++ b/crates/app/src/views/app.rs @@ -1,4 +1,4 @@ -use components::{ +use coop_ui::{ dock::{DockArea, DockItem, DockPlacement, PanelStyle}, theme::{ActiveTheme, Theme}, Root, TitleBar, diff --git a/crates/app/src/views/dock/chat/mod.rs b/crates/app/src/views/dock/chat/mod.rs index 7614497..f4960d5 100644 --- a/crates/app/src/views/dock/chat/mod.rs +++ b/crates/app/src/views/dock/chat/mod.rs @@ -1,4 +1,4 @@ -use components::{ +use coop_ui::{ button::Button, button_group::ButtonGroup, dock::{DockItemState, Panel, PanelEvent, TitleStyle}, diff --git a/crates/app/src/views/dock/inbox/chat.rs b/crates/app/src/views/dock/inbox/chat.rs index 47004b4..28e50cd 100644 --- a/crates/app/src/views/dock/inbox/chat.rs +++ b/crates/app/src/views/dock/inbox/chat.rs @@ -1,4 +1,4 @@ -use components::{theme::ActiveTheme, Collapsible, Selectable, StyledExt}; +use coop_ui::{theme::ActiveTheme, Collapsible, Selectable, StyledExt}; use gpui::*; use nostr_sdk::prelude::*; use prelude::FluentBuilder; diff --git a/crates/app/src/views/dock/inbox/mod.rs b/crates/app/src/views/dock/inbox/mod.rs index 004c657..33f7818 100644 --- a/crates/app/src/views/dock/inbox/mod.rs +++ b/crates/app/src/views/dock/inbox/mod.rs @@ -1,5 +1,5 @@ use chat::{Chat, ChatDelegate}; -use components::{theme::ActiveTheme, v_flex, StyledExt}; +use coop_ui::{theme::ActiveTheme, v_flex, StyledExt}; use gpui::*; use itertools::Itertools; use nostr_sdk::prelude::*; diff --git a/crates/app/src/views/dock/left_dock.rs b/crates/app/src/views/dock/left_dock.rs index 4fc75ae..13ea581 100644 --- a/crates/app/src/views/dock/left_dock.rs +++ b/crates/app/src/views/dock/left_dock.rs @@ -1,4 +1,4 @@ -use components::{ +use coop_ui::{ button::Button, dock::{DockItemState, Panel, PanelEvent, TitleStyle}, popup_menu::PopupMenu, diff --git a/crates/app/src/views/dock/welcome.rs b/crates/app/src/views/dock/welcome.rs index 3103a24..fce2478 100644 --- a/crates/app/src/views/dock/welcome.rs +++ b/crates/app/src/views/dock/welcome.rs @@ -1,4 +1,4 @@ -use components::{ +use coop_ui::{ button::Button, dock::{DockItemState, Panel, PanelEvent, TitleStyle}, popup_menu::PopupMenu, diff --git a/crates/app/src/views/onboarding.rs b/crates/app/src/views/onboarding.rs index 0c7f8cc..bd43333 100644 --- a/crates/app/src/views/onboarding.rs +++ b/crates/app/src/views/onboarding.rs @@ -1,5 +1,5 @@ use async_utility::task::spawn; -use components::{ +use coop_ui::{ input::{InputEvent, TextInput}, label::Label, }; @@ -17,7 +17,7 @@ impl Onboarding { pub fn new(cx: &mut ViewContext<'_, Self>) -> Self { let input = cx.new_view(|cx| { let mut input = TextInput::new(cx); - input.set_size(components::Size::Medium, cx); + input.set_size(coop_ui::Size::Medium, cx); input }); diff --git a/crates/ui/.DS_Store b/crates/ui/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d7921e0c37016f623aa4250f2df2f6d6a73c60bd GIT binary patch literal 6148 zcmeHK%}T>S5Z<-brW7Fug&r5Y7HmbU#Y>3w1&ruHr6#6mFlI}VnnNk%tS{t~_&m<+ zZop#BB6bFLzxmzGevtiPjB$4!4j8i;V-_?-j!KQ7yEe38k`XzMk)WEN4hGg;{tWaa}1UmaTN5c QazMHWC_<H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0, + on_toggle_click: Option>, +} + +impl Accordion { + pub fn new(id: impl Into) -> Self { + Self { + id: id.into(), + base: v_flex().gap_1(), + multiple: false, + size: Size::default(), + bordered: true, + children: Vec::new(), + disabled: false, + on_toggle_click: None, + } + } + + pub fn multiple(mut self, multiple: bool) -> Self { + self.multiple = multiple; + self + } + + pub fn bordered(mut self, bordered: bool) -> Self { + self.bordered = bordered; + self + } + + pub fn disabled(mut self, disabled: bool) -> Self { + self.disabled = disabled; + self + } + + pub fn item(mut self, child: F) -> Self + where + F: FnOnce(AccordionItem) -> AccordionItem, + { + let item = child(AccordionItem::new()); + self.children.push(item); + self + } + + /// Sets the on_toggle_click callback for the AccordionGroup. + /// + /// The first argument `Vec` is the indices of the open accordions. + pub fn on_toggle_click( + mut self, + on_toggle_click: impl Fn(&[usize], &mut WindowContext) + Send + Sync + 'static, + ) -> Self { + self.on_toggle_click = Some(Arc::new(on_toggle_click)); + self + } +} + +impl Sizable for Accordion { + fn with_size(mut self, size: impl Into) -> Self { + self.size = size.into(); + self + } +} + +impl RenderOnce for Accordion { + fn render(self, _: &mut WindowContext) -> impl IntoElement { + let mut open_ixs: Vec = Vec::new(); + let multiple = self.multiple; + let state = Rc::new(Cell::new(None)); + + self.children + .iter() + .enumerate() + .for_each(|(ix, accordion)| { + if accordion.open { + open_ixs.push(ix); + } + }); + + self.base + .id(self.id) + .children( + self.children + .into_iter() + .enumerate() + .map(|(ix, accordion)| { + let state = Rc::clone(&state); + accordion + .with_size(self.size) + .bordered(self.bordered) + .when(self.disabled, |this| this.disabled(true)) + .on_toggle_click(move |_, _| { + state.set(Some(ix)); + }) + }), + ) + .when_some( + self.on_toggle_click.filter(|_| !self.disabled), + move |this, on_toggle_click| { + this.on_click(move |_, cx| { + let mut open_ixs = open_ixs.clone(); + if let Some(ix) = state.get() { + if multiple { + if let Some(pos) = open_ixs.iter().position(|&i| i == ix) { + open_ixs.remove(pos); + } else { + open_ixs.push(ix); + } + } else { + let was_open = open_ixs.iter().any(|&i| i == ix); + open_ixs.clear(); + if !was_open { + open_ixs.push(ix); + } + } + } + + on_toggle_click(&open_ixs, cx); + }) + }, + ) + } +} + +/// An Accordion is a vertically stacked list of items, each of which can be expanded to reveal the content associated with it. +#[derive(IntoElement)] +pub struct AccordionItem { + icon: Option, + title: AnyElement, + content: AnyElement, + open: bool, + size: Size, + bordered: bool, + disabled: bool, + on_toggle_click: Option>, +} + +impl AccordionItem { + pub fn new() -> Self { + Self { + icon: None, + title: SharedString::default().into_any_element(), + content: SharedString::default().into_any_element(), + open: false, + disabled: false, + on_toggle_click: None, + size: Size::default(), + bordered: true, + } + } + + pub fn icon(mut self, icon: impl Into) -> Self { + self.icon = Some(icon.into()); + self + } + + pub fn title(mut self, title: impl IntoElement) -> Self { + self.title = title.into_any_element(); + self + } + + pub fn content(mut self, content: impl IntoElement) -> Self { + self.content = content.into_any_element(); + self + } + + pub fn bordered(mut self, bordered: bool) -> Self { + self.bordered = bordered; + self + } + + pub fn open(mut self, open: bool) -> Self { + self.open = open; + self + } + + pub fn disabled(mut self, disabled: bool) -> Self { + self.disabled = disabled; + self + } + + fn on_toggle_click( + mut self, + on_toggle_click: impl Fn(&bool, &mut WindowContext) + 'static, + ) -> Self { + self.on_toggle_click = Some(Arc::new(on_toggle_click)); + self + } +} + +impl Sizable for AccordionItem { + fn with_size(mut self, size: impl Into) -> Self { + self.size = size.into(); + self + } +} + +impl RenderOnce for AccordionItem { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + let text_size = match self.size { + Size::XSmall => rems(0.875), + Size::Small => rems(0.875), + _ => rems(1.0), + }; + + v_flex() + .bg(cx.theme().accordion) + .overflow_hidden() + .when(self.bordered, |this| { + this.border_1().border_color(cx.theme().border).rounded_md() + }) + .text_size(text_size) + .child( + h_flex() + .id("accordion-title") + .justify_between() + .map(|this| match self.size { + Size::XSmall => this.py_0().px_1p5(), + Size::Small => this.py_0p5().px_2(), + Size::Large => this.py_1p5().px_4(), + _ => this.py_1().px_3(), + }) + .when(self.open, |this| { + this.when(self.bordered, |this| { + this.bg(cx.theme().accordion_active) + .text_color(cx.theme().foreground) + .border_b_1() + .border_color(cx.theme().border) + }) + }) + .child( + h_flex() + .items_center() + .map(|this| match self.size { + Size::XSmall => this.gap_1(), + Size::Small => this.gap_1(), + _ => this.gap_2(), + }) + .when_some(self.icon, |this, icon| { + this.child( + icon.with_size(self.size) + .text_color(cx.theme().muted_foreground), + ) + }) + .child(self.title), + ) + .when(!self.disabled, |this| { + this.cursor_pointer() + .hover(|this| this.bg(cx.theme().accordion_hover)) + .child( + Icon::new(if self.open { + IconName::ChevronUp + } else { + IconName::ChevronDown + }) + .xsmall() + .text_color(cx.theme().muted_foreground), + ) + }) + .when_some( + self.on_toggle_click.filter(|_| !self.disabled), + |this, on_toggle_click| { + this.on_click({ + move |_, cx| { + on_toggle_click(&!self.open, cx); + } + }) + }, + ), + ) + .when(self.open, |this| { + this.child( + div() + .map(|this| match self.size { + Size::XSmall => this.p_1p5(), + Size::Small => this.p_2(), + Size::Large => this.p_4(), + _ => this.p_3(), + }) + .child(self.content), + ) + }) + } +} diff --git a/crates/ui/src/animation.rs b/crates/ui/src/animation.rs new file mode 100644 index 0000000..be3539e --- /dev/null +++ b/crates/ui/src/animation.rs @@ -0,0 +1,19 @@ +/// A cubic bezier function like CSS `cubic-bezier`. +/// +/// Builder: +/// +/// https://cubic-bezier.com +pub fn cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32) -> impl Fn(f32) -> f32 { + move |t: f32| { + let one_t = 1.0 - t; + let one_t2 = one_t * one_t; + let t2 = t * t; + let t3 = t2 * t; + + // The Bezier curve function for x and y, where x0 = 0, y0 = 0, x3 = 1, y3 = 1 + let _x = 3.0 * x1 * one_t2 * t + 3.0 * x2 * one_t * t2 + t3; + let y = 3.0 * y1 * one_t2 * t + 3.0 * y2 * one_t * t2 + t3; + + y + } +} diff --git a/crates/ui/src/badge.rs b/crates/ui/src/badge.rs new file mode 100644 index 0000000..0e7e0f1 --- /dev/null +++ b/crates/ui/src/badge.rs @@ -0,0 +1,123 @@ +use crate::{theme::ActiveTheme as _, Sizable, Size}; +use gpui::{ + div, prelude::FluentBuilder as _, relative, Div, Hsla, InteractiveElement as _, IntoElement, + ParentElement, RenderOnce, Styled, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum BadgeVariant { + #[default] + Primary, + Secondary, + Outline, + Destructive, + Custom { + color: Hsla, + foreground: Hsla, + border: Hsla, + }, +} +impl BadgeVariant { + fn bg(&self, cx: &gpui::WindowContext) -> Hsla { + match self { + Self::Primary => cx.theme().primary, + Self::Secondary => cx.theme().secondary, + Self::Outline => gpui::transparent_black(), + Self::Destructive => cx.theme().destructive, + Self::Custom { color, .. } => *color, + } + } + + fn border(&self, cx: &gpui::WindowContext) -> Hsla { + match self { + Self::Primary => cx.theme().primary, + Self::Secondary => cx.theme().secondary, + Self::Outline => cx.theme().border, + Self::Destructive => cx.theme().destructive, + Self::Custom { border, .. } => *border, + } + } + + fn fg(&self, cx: &gpui::WindowContext) -> Hsla { + match self { + Self::Primary => cx.theme().primary_foreground, + Self::Secondary => cx.theme().secondary_foreground, + Self::Outline => cx.theme().foreground, + Self::Destructive => cx.theme().destructive_foreground, + Self::Custom { foreground, .. } => *foreground, + } + } +} + +/// Badge is a small status indicator for UI elements. +/// +/// Only support: Medium, Small +#[derive(IntoElement)] +pub struct Badge { + base: Div, + veriant: BadgeVariant, + size: Size, +} +impl Badge { + fn new() -> Self { + Self { + base: div().flex().items_center().rounded_md().border_1(), + veriant: BadgeVariant::default(), + size: Size::Medium, + } + } + + pub fn with_variant(mut self, variant: BadgeVariant) -> Self { + self.veriant = variant; + self + } + + pub fn primary() -> Self { + Self::new().with_variant(BadgeVariant::Primary) + } + + pub fn secondary() -> Self { + Self::new().with_variant(BadgeVariant::Secondary) + } + + pub fn outline() -> Self { + Self::new().with_variant(BadgeVariant::Outline) + } + + pub fn destructive() -> Self { + Self::new().with_variant(BadgeVariant::Destructive) + } + + pub fn custom(color: Hsla, foreground: Hsla, border: Hsla) -> Self { + Self::new().with_variant(BadgeVariant::Custom { + color, + foreground, + border, + }) + } +} +impl Sizable for Badge { + fn with_size(mut self, size: impl Into) -> Self { + self.size = size.into(); + self + } +} +impl ParentElement for Badge { + fn extend(&mut self, elements: impl IntoIterator) { + self.base.extend(elements); + } +} +impl RenderOnce for Badge { + fn render(self, cx: &mut gpui::WindowContext) -> impl IntoElement { + self.base + .line_height(relative(1.3)) + .map(|this| match self.size { + Size::XSmall | Size::Small => this.text_xs().px_1p5().py_0(), + _ => this.text_xs().px_2p5().py_0p5(), + }) + .bg(self.veriant.bg(cx)) + .text_color(self.veriant.fg(cx)) + .border_color(self.veriant.border(cx)) + .hover(|this| this.opacity(0.9)) + } +} diff --git a/crates/ui/src/breadcrumb.rs b/crates/ui/src/breadcrumb.rs new file mode 100644 index 0000000..4751e93 --- /dev/null +++ b/crates/ui/src/breadcrumb.rs @@ -0,0 +1,118 @@ +use std::rc::Rc; + +use gpui::{ + div, prelude::FluentBuilder as _, ClickEvent, ElementId, InteractiveElement as _, IntoElement, + ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, WindowContext, +}; + +use crate::{h_flex, theme::ActiveTheme, Icon, IconName}; + +#[derive(IntoElement)] +pub struct Breadcrumb { + items: Vec, +} + +#[derive(IntoElement)] +pub struct BreadcrumbItem { + id: ElementId, + text: SharedString, + on_click: Option>, + disabled: bool, + is_last: bool, +} + +impl BreadcrumbItem { + pub fn new(id: impl Into, text: impl Into) -> Self { + Self { + id: id.into(), + text: text.into(), + on_click: None, + disabled: false, + is_last: false, + } + } + + pub fn disabled(mut self, disabled: bool) -> Self { + self.disabled = disabled; + self + } + + pub fn on_click( + mut self, + on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static, + ) -> Self { + self.on_click = Some(Rc::new(on_click)); + self + } + + /// For internal use only. + fn is_last(mut self, is_last: bool) -> Self { + self.is_last = is_last; + self + } +} + +impl RenderOnce for BreadcrumbItem { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + div() + .id(self.id) + .child(self.text) + .text_color(cx.theme().muted_foreground) + .when(self.is_last, |this| this.text_color(cx.theme().foreground)) + .when(self.disabled, |this| { + this.text_color(cx.theme().muted_foreground) + }) + .when(!self.disabled, |this| { + this.when_some(self.on_click, |this, on_click| { + this.cursor_pointer().on_click(move |event, cx| { + on_click(event, cx); + }) + }) + }) + } +} + +impl Breadcrumb { + pub fn new() -> Self { + Self { items: Vec::new() } + } + + /// Add an item to the breadcrumb. + pub fn item(mut self, item: BreadcrumbItem) -> Self { + self.items.push(item); + self + } +} + +#[derive(IntoElement)] +struct BreadcrumbSeparator; +impl RenderOnce for BreadcrumbSeparator { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + Icon::new(IconName::ChevronRight) + .text_color(cx.theme().muted_foreground) + .size_3p5() + .into_any_element() + } +} + +impl RenderOnce for Breadcrumb { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + let items_count = self.items.len(); + + let mut children = vec![]; + for (ix, item) in self.items.into_iter().enumerate() { + let is_last = ix == items_count - 1; + + children.push(item.is_last(is_last).into_any_element()); + if !is_last { + children.push(BreadcrumbSeparator.into_any_element()); + } + } + + h_flex() + .gap_1p5() + .text_sm() + .text_color(cx.theme().muted_foreground) + .children(children) + } +} diff --git a/crates/ui/src/button.rs b/crates/ui/src/button.rs new file mode 100644 index 0000000..53883bf --- /dev/null +++ b/crates/ui/src/button.rs @@ -0,0 +1,667 @@ +use crate::{ + h_flex, + indicator::Indicator, + theme::{ActiveTheme, Colorize as _}, + tooltip::Tooltip, + Disableable, Icon, Selectable, Sizable, Size, +}; +use gpui::{ + div, prelude::FluentBuilder as _, px, relative, AnyElement, ClickEvent, Corners, Div, Edges, + ElementId, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels, + RenderOnce, SharedString, StatefulInteractiveElement as _, Styled, WindowContext, +}; + +pub enum ButtonRounded { + None, + Small, + Medium, + Large, + Size(Pixels), +} + +impl From for ButtonRounded { + fn from(px: Pixels) -> Self { + ButtonRounded::Size(px) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ButtonCustomVariant { + color: Hsla, + foreground: Hsla, + border: Hsla, + shadow: bool, + hover: Hsla, + active: Hsla, +} + +pub trait ButtonVariants: Sized { + fn with_variant(self, variant: ButtonVariant) -> Self; + + /// With the primary style for the Button. + fn primary(self) -> Self { + self.with_variant(ButtonVariant::Primary) + } + + /// With the danger style for the Button. + fn danger(self) -> Self { + self.with_variant(ButtonVariant::Danger) + } + + /// With the outline style for the Button. + fn outline(self) -> Self { + self.with_variant(ButtonVariant::Outline) + } + + /// With the ghost style for the Button. + fn ghost(self) -> Self { + self.with_variant(ButtonVariant::Ghost) + } + + /// With the link style for the Button. + fn link(self) -> Self { + self.with_variant(ButtonVariant::Link) + } + + /// With the text style for the Button, it will no padding look like a normal text. + fn text(self) -> Self { + self.with_variant(ButtonVariant::Text) + } + + /// With the custom style for the Button. + fn custom(self, style: ButtonCustomVariant) -> Self { + self.with_variant(ButtonVariant::Custom(style)) + } +} + +impl ButtonCustomVariant { + pub fn new(cx: &WindowContext) -> Self { + Self { + color: cx.theme().secondary, + foreground: cx.theme().secondary_foreground, + border: cx.theme().border, + hover: cx.theme().secondary_hover, + active: cx.theme().secondary_active, + shadow: true, + } + } + + pub fn color(mut self, color: Hsla) -> Self { + self.color = color; + self + } + + pub fn foreground(mut self, color: Hsla) -> Self { + self.foreground = color; + self + } + + pub fn border(mut self, color: Hsla) -> Self { + self.border = color; + self + } + + pub fn hover(mut self, color: Hsla) -> Self { + self.hover = color; + self + } + + pub fn active(mut self, color: Hsla) -> Self { + self.active = color; + self + } + + pub fn shadow(mut self, shadow: bool) -> Self { + self.shadow = shadow; + self + } +} + +/// The veriant of the Button. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum ButtonVariant { + Primary, + Secondary, + Danger, + Outline, + Ghost, + Link, + Text, + Custom(ButtonCustomVariant), +} + +impl Default for ButtonVariant { + fn default() -> Self { + Self::Secondary + } +} + +impl ButtonVariant { + fn is_link(&self) -> bool { + matches!(self, Self::Link) + } + + fn is_text(&self) -> bool { + matches!(self, Self::Text) + } + + fn no_padding(&self) -> bool { + self.is_link() || self.is_text() + } +} + +/// A Button element. +#[derive(IntoElement)] +pub struct Button { + pub base: Div, + id: ElementId, + icon: Option, + label: Option, + children: Vec, + disabled: bool, + pub(crate) selected: bool, + variant: ButtonVariant, + rounded: ButtonRounded, + border_corners: Corners, + border_edges: Edges, + size: Size, + compact: bool, + tooltip: Option, + on_click: Option>, + pub(crate) stop_propagation: bool, + loading: bool, + loading_icon: Option, +} + +impl From