Packages
phoenix_kit
1.7.156
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
221 Versions
Jump to
Current section
221 Versions
Compare versions
7
files changed
+163
additions
-13
deletions
unknownCHANGELOG.md
File is too large to be displayed (100 KB limit).
| @@ -1,6 +1,6 @@ | |
| 1 1 | {<<"links">>,[{<<"GitHub">>,<<"https://github.com/BeamLabEU/phoenix_kit">>}]}. |
| 2 2 | {<<"name">>,<<"phoenix_kit">>}. |
| 3 | - {<<"version">>,<<"1.7.155">>}. |
| 3 | + {<<"version">>,<<"1.7.156">>}. |
| 4 4 | {<<"description">>, |
| 5 5 | <<65,32,102,111,117,110,100,97,116,105,111,110,32,102,111,114,32,98,117,105, |
| 6 6 | 108,100,105,110,103,32,69,108,105,120,105,114,32,80,104,111,101,110,105, |
| @@ -764,7 +764,7 @@ | |
| 764 764 | [{<<"name">>,<<"leaf">>}, |
| 765 765 | {<<"app">>,<<"leaf">>}, |
| 766 766 | {<<"optional">>,false}, |
| 767 | - {<<"requirement">>,<<"~> 0.2.23">>}, |
| 767 | + {<<"requirement">>,<<"~> 0.3">>}, |
| 768 768 | {<<"repository">>,<<"hexpm">>}], |
| 769 769 | [{<<"name">>,<<"fresco">>}, |
| 770 770 | {<<"app">>,<<"fresco">>}, |
unknownlib/modules/storage/storage.ex
File is too large to be displayed (100 KB limit).
| @@ -15,7 +15,7 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 15 15 | implement the relevant `handle_event/3` clauses: |
| 16 16 | |
| 17 17 | navigate_folder, navigate_root, navigate_view_all, |
| 18 | - toggle_folder_expand, toggle_sidebar, create_untitled_folder, |
| 18 | + toggle_folder_expand, toggle_sidebar, open_new_folder_modal, |
| 19 19 | start_rename_folder, rename_folder_input, rename_folder, |
| 20 20 | cancel_rename_folder, toggle_trash_filter |
| 21 21 | |
| @@ -84,6 +84,16 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 84 84 | attr :show_trash, :boolean, default: true |
| 85 85 | |
| 86 86 | def folder_explorer(assigns) do |
| 87 | + # UUIDs on the path from a root folder down to (and including) the |
| 88 | + # current folder. Each node's guide-line connector is darkened when its |
| 89 | + # uuid is in this set, so the user can trace the branch they're inside. |
| 90 | + assigns = |
| 91 | + assign( |
| 92 | + assigns, |
| 93 | + :active_path, |
| 94 | + active_path_uuids(assigns.folder_tree, assigns.current_folder) |
| 95 | + ) |
| 96 | + |
| 87 97 | ~H""" |
| 88 98 | <div |
| 89 99 | id={@id} |
| @@ -119,7 +129,7 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 119 129 | <div class="flex gap-0.5"> |
| 120 130 | <button |
| 121 131 | :if={@show_create} |
| 122 | - phx-click="create_untitled_folder" |
| 132 | + phx-click="open_new_folder_modal" |
| 123 133 | phx-target={@myself} |
| 124 134 | class="btn btn-ghost btn-xs" |
| 125 135 | title={gettext("New folder")} |
| @@ -178,6 +188,7 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 178 188 | <.folder_tree_node |
| 179 189 | node={node} |
| 180 190 | current_folder={@current_folder} |
| 191 | + active_path={@active_path} |
| 181 192 | expanded_folders={@expanded_folders} |
| 182 193 | renaming_folder={@renaming_folder} |
| 183 194 | renaming_source={@renaming_source} |
| @@ -222,6 +233,17 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 222 233 | |
| 223 234 | attr :node, :map, required: true |
| 224 235 | attr :current_folder, :any, required: true |
| 236 | + |
| 237 | + attr :active_path, :any, |
| 238 | + default: MapSet.new(), |
| 239 | + doc: "UUIDs from a root folder to the current folder; darkens their connector lines." |
| 240 | + |
| 241 | + attr :connector_mode, :atom, |
| 242 | + default: :normal, |
| 243 | + values: [:normal, :active_trunk, :active_turn], |
| 244 | + doc: |
| 245 | + "How this node's guide line is drawn: normal, a darkened pass-through trunk, or the darkened turn into the active branch." |
| 246 | + |
| 225 247 | attr :expanded_folders, :any, required: true |
| 226 248 | attr :renaming_folder, :any, default: nil |
| 227 249 | attr :renaming_text, :string, default: "" |
| @@ -278,11 +300,30 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 278 300 | assigns.renaming_source == "sidebar" |
| 279 301 | ) |
| 280 302 | |
| 303 | + # This node's own connector style comes from its parent (`@connector_mode`). |
| 304 | + # For ITS children we find which one (if any) continues the active branch: |
| 305 | + # children above it get a darkened vertical trunk (`:active_trunk`), the |
| 306 | + # branch child itself gets the darkened turn (`:active_turn`), the rest stay |
| 307 | + # normal. Suppressed in trash view (the tree highlight is off there). |
| 308 | + assigns = |
| 309 | + assign( |
| 310 | + assigns, |
| 311 | + :on_path_child_index, |
| 312 | + if(assigns.filter_trash, |
| 313 | + do: nil, |
| 314 | + else: |
| 315 | + Enum.find_index( |
| 316 | + assigns.node.children, |
| 317 | + &MapSet.member?(assigns.active_path, &1.folder.uuid) |
| 318 | + ) |
| 319 | + ) |
| 320 | + ) |
| 321 | + |
| 281 322 | assigns = |
| 282 323 | assign( |
| 283 324 | assigns, |
| 284 325 | :tree_connector_class, |
| 285 | - tree_connector_class(assigns.depth, assigns.has_children) |
| 326 | + tree_connector_class(assigns.depth, assigns.has_children, assigns.connector_mode) |
| 286 327 | ) |
| 287 328 | |
| 288 329 | ~H""" |
| @@ -419,12 +460,14 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 419 460 | --%> |
| 420 461 | <ul |
| 421 462 | class="ml-3 overflow-hidden" |
| 422 | - style={"--pk-tree-line: #{tree_line_color(@node.folder.color)}"} |
| 463 | + style={"--pk-tree-line: #{tree_line_color(@node.folder.color)}; --pk-tree-line-active: #{tree_line_color_active(@node.folder.color)}"} |
| 423 464 | > |
| 424 | - <%= for child <- @node.children do %> |
| 465 | + <%= for {child, idx} <- Enum.with_index(@node.children) do %> |
| 425 466 | <.folder_tree_node |
| 426 467 | node={child} |
| 427 468 | current_folder={@current_folder} |
| 469 | + active_path={@active_path} |
| 470 | + connector_mode={child_connector_mode(@on_path_child_index, idx)} |
| 428 471 | expanded_folders={@expanded_folders} |
| 429 472 | renaming_folder={@renaming_folder} |
| 430 473 | renaming_source={@renaming_source} |
| @@ -476,10 +519,49 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 476 519 | end |
| 477 520 | end |
| 478 521 | |
| 522 | + # Darker (less transparent) variant of the same line color, used for the |
| 523 | + # connectors on the active root→current branch — same hue, just bolder. A |
| 524 | + # colored folder bumps alpha `80` (~50%) → `E6` (~90%); the neutral falls |
| 525 | + # back to currentColor at 85% (still theme-adaptive, not solid black). |
| 479 526 | @doc false |
| 480 | - def tree_connector_class(0, _has_children), do: false |
| 527 | + def tree_line_color_active(color) do |
| 528 | + case folder_color_hex(color) do |
| 529 | + nil -> "color-mix(in oklab, currentColor 85%, transparent)" |
| 530 | + hex -> hex <> "E6" |
| 531 | + end |
| 532 | + end |
| 481 533 | |
| 482 | - def tree_connector_class(_depth, true = _has_children) do |
| 534 | + # Which connector mode a child renders, given the index of the branch child |
| 535 | + # in the same group (or nil when none): everything above the branch child is |
| 536 | + # a darkened pass-through trunk, the branch child is the darkened turn, the |
| 537 | + # rest are normal. |
| 538 | + defp child_connector_mode(nil, _idx), do: :normal |
| 539 | + defp child_connector_mode(branch_idx, idx) when idx < branch_idx, do: :active_trunk |
| 540 | + defp child_connector_mode(branch_idx, idx) when idx == branch_idx, do: :active_turn |
| 541 | + defp child_connector_mode(_branch_idx, _idx), do: :normal |
| 542 | + |
| 543 | + # Connector classes are returned as whole literal Tailwind strings (never |
| 544 | + # interpolate the utility tokens — the JIT scans source for complete class |
| 545 | + # names, so each variant is spelled out in full). |
| 546 | + # |
| 547 | + # Three modes: |
| 548 | + # * :normal — light vertical trunk + light elbow into the row. |
| 549 | + # * :active_trunk — the active branch descends PAST this side row, so its |
| 550 | + # vertical trunk is darkened while the elbow into the row |
| 551 | + # stays light (the path doesn't enter here). |
| 552 | + # * :active_turn — the active branch turns INTO this row. The trunk stays |
| 553 | + # light so it can continue down to later siblings, and a |
| 554 | + # darkened L-elbow (`after`) draws the turn over its top. |
| 555 | + # A last child has no trunk below, so its `before` becomes |
| 556 | + # the darkened elbow instead. |
| 557 | + # |
| 558 | + # `w-4` vs `w-9`: a row with a disclosure chevron stops the elbow at the |
| 559 | + # chevron (`w-4`); a childless row runs it across the empty chevron column to |
| 560 | + # the folder icon (`w-9`). |
| 561 | + @doc false |
| 562 | + def tree_connector_class(0, _has_children, _mode), do: false |
| 563 | + |
| 564 | + def tree_connector_class(_depth, true = _has_children, :normal) do |
| 483 565 | "relative pl-3.5 " <> |
| 484 566 | "before:content-[''] before:absolute before:left-0 before:top-0 before:h-full before:w-0.5 before:bg-[var(--pk-tree-line)] " <> |
| 485 567 | "after:content-[''] after:absolute after:left-0 after:top-[0.8125rem] after:h-0.5 after:w-4 after:bg-[var(--pk-tree-line)] " <> |
| @@ -488,7 +570,7 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 488 570 | "last:after:hidden" |
| 489 571 | end |
| 490 572 | |
| 491 | - def tree_connector_class(_depth, false = _has_children) do |
| 573 | + def tree_connector_class(_depth, false = _has_children, :normal) do |
| 492 574 | "relative pl-3.5 " <> |
| 493 575 | "before:content-[''] before:absolute before:left-0 before:top-0 before:h-full before:w-0.5 before:bg-[var(--pk-tree-line)] " <> |
| 494 576 | "after:content-[''] after:absolute after:left-0 after:top-[0.8125rem] after:h-0.5 after:w-9 after:bg-[var(--pk-tree-line)] " <> |
| @@ -497,6 +579,74 @@ defmodule PhoenixKitWeb.Components.FolderExplorer do | |
| 497 579 | "last:after:hidden" |
| 498 580 | end |
| 499 581 | |
| 582 | + def tree_connector_class(_depth, true = _has_children, :active_trunk) do |
| 583 | + "relative pl-3.5 " <> |
| 584 | + "before:content-[''] before:absolute before:left-0 before:top-0 before:h-full before:w-0.5 before:bg-[var(--pk-tree-line-active)] " <> |
| 585 | + "after:content-[''] after:absolute after:left-0 after:top-[0.8125rem] after:h-0.5 after:w-4 after:bg-[var(--pk-tree-line)] " <> |
| 586 | + "last:before:h-[0.875rem] last:before:w-4 last:before:bg-transparent " <> |
| 587 | + "last:before:border-l-2 last:before:border-b-2 last:before:border-[var(--pk-tree-line-active)] last:before:rounded-bl-lg " <> |
| 588 | + "last:after:hidden" |
| 589 | + end |
| 590 | + |
| 591 | + def tree_connector_class(_depth, false = _has_children, :active_trunk) do |
| 592 | + "relative pl-3.5 " <> |
| 593 | + "before:content-[''] before:absolute before:left-0 before:top-0 before:h-full before:w-0.5 before:bg-[var(--pk-tree-line-active)] " <> |
| 594 | + "after:content-[''] after:absolute after:left-0 after:top-[0.8125rem] after:h-0.5 after:w-9 after:bg-[var(--pk-tree-line)] " <> |
| 595 | + "last:before:h-[0.875rem] last:before:w-9 last:before:bg-transparent " <> |
| 596 | + "last:before:border-l-2 last:before:border-b-2 last:before:border-[var(--pk-tree-line-active)] last:before:rounded-bl-lg " <> |
| 597 | + "last:after:hidden" |
| 598 | + end |
| 599 | + |
| 600 | + def tree_connector_class(_depth, true = _has_children, :active_turn) do |
| 601 | + "relative pl-3.5 " <> |
| 602 | + "before:content-[''] before:absolute before:left-0 before:top-0 before:h-full before:w-0.5 before:bg-[var(--pk-tree-line)] " <> |
| 603 | + "after:content-[''] after:absolute after:left-0 after:top-0 after:h-[0.8125rem] after:w-4 after:bg-transparent " <> |
| 604 | + "after:border-l-2 after:border-b-2 after:border-[var(--pk-tree-line-active)] " <> |
| 605 | + "last:before:h-[0.875rem] last:before:w-4 last:before:bg-transparent " <> |
| 606 | + "last:before:border-l-2 last:before:border-b-2 last:before:border-[var(--pk-tree-line-active)] last:before:rounded-bl-lg " <> |
| 607 | + "last:after:hidden" |
| 608 | + end |
| 609 | + |
| 610 | + def tree_connector_class(_depth, false = _has_children, :active_turn) do |
| 611 | + "relative pl-3.5 " <> |
| 612 | + "before:content-[''] before:absolute before:left-0 before:top-0 before:h-full before:w-0.5 before:bg-[var(--pk-tree-line)] " <> |
| 613 | + "after:content-[''] after:absolute after:left-0 after:top-0 after:h-[0.8125rem] after:w-9 after:bg-transparent " <> |
| 614 | + "after:border-l-2 after:border-b-2 after:border-[var(--pk-tree-line-active)] " <> |
| 615 | + "last:before:h-[0.875rem] last:before:w-9 last:before:bg-transparent " <> |
| 616 | + "last:before:border-l-2 last:before:border-b-2 last:before:border-[var(--pk-tree-line-active)] last:before:rounded-bl-lg " <> |
| 617 | + "last:after:hidden" |
| 618 | + end |
| 619 | + |
| 620 | + # ────────────────────────────────────────────────────────────── |
| 621 | + # Active-branch path (root → current folder) |
| 622 | + # ────────────────────────────────────────────────────────────── |
| 623 | + |
| 624 | + # Set of folder UUIDs on the path from a root node down to (and including) |
| 625 | + # the current folder. Empty when there is no current folder or it isn't in |
| 626 | + # the tree. Walks the already-nested tree, so it's O(n) over visible nodes. |
| 627 | + @doc false |
| 628 | + def active_path_uuids(_tree, nil), do: MapSet.new() |
| 629 | + |
| 630 | + def active_path_uuids(tree, current_folder) do |
| 631 | + case find_node_path(tree, current_folder.uuid) do |
| 632 | + nil -> MapSet.new() |
| 633 | + path -> MapSet.new(path) |
| 634 | + end |
| 635 | + end |
| 636 | + |
| 637 | + defp find_node_path(nodes, target_uuid) do |
| 638 | + Enum.find_value(nodes, fn node -> |
| 639 | + if node.folder.uuid == target_uuid do |
| 640 | + [target_uuid] |
| 641 | + else |
| 642 | + case find_node_path(node.children, target_uuid) do |
| 643 | + nil -> nil |
| 644 | + sub -> [node.folder.uuid | sub] |
| 645 | + end |
| 646 | + end |
| 647 | + end) |
| 648 | + end |
| 649 | + |
| 500 650 | # ────────────────────────────────────────────────────────────── |
| 501 651 | # Folder color helpers (shared with grid/list folder cards) |
| 502 652 | # ────────────────────────────────────────────────────────────── |
unknownlib/phoenix_kit_web/components/media_browser.ex
File is too large to be displayed (100 KB limit).
Loading more files…