Skip to content

Commit

Permalink
- update stepper with better responsiveness across vertical and horiz…
Browse files Browse the repository at this point in the history
…ontal orientations

- update input to accept clearable, copyable and viewable
  • Loading branch information
nhobes committed Oct 31, 2024
1 parent 7d02611 commit a1a4130
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 61 deletions.
63 changes: 40 additions & 23 deletions assets/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,11 @@
}

/* Clearable Field Styles */
.pc-clearable-field-input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
appearance: none;
}

.pc-clearable-field-wrapper {
@apply relative;
}
Expand Down Expand Up @@ -909,7 +914,7 @@
}

.pc-date-input-icon {
@apply absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none;
@apply absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer hover:text-gray-500 dark:hover:text-gray-300;
}

.pc-date-picker-indicator {
Expand Down Expand Up @@ -2040,7 +2045,7 @@
}

.pc-stepper__container {
@apply relative flex md:gap-4;
@apply flex md:gap-4;
}

.pc-stepper--horizontal .pc-stepper__container {
Expand All @@ -2053,25 +2058,16 @@

/* Item */
.pc-stepper__item {
@apply relative flex w-full md:w-auto;
}

.pc-stepper--horizontal .pc-stepper__item {
@apply flex-col md:flex-row md:flex-1 md:items-center;
@apply flex-shrink-0;
}

.pc-stepper--vertical .pc-stepper__item {
@apply flex-col pl-5;
}

/* Item Content */
.pc-stepper__item-content {
@apply flex md:flex-1 md:min-w-[180px] focus:outline-none;
@apply flex items-center gap-4 focus:outline-none ring-0;
}

/* Node */
.pc-stepper__node {
@apply flex items-center w-full gap-4 transition-all duration-200 cursor-pointer hover:opacity-90;
@apply flex items-center gap-4 transition-all duration-200 cursor-pointer hover:opacity-90;
}

/* Indicator */
Expand Down Expand Up @@ -2136,7 +2132,7 @@

/* Content */
.pc-stepper__content {
@apply flex flex-col flex-1 gap-1;
@apply flex flex-col w-full gap-1 text-left grow;
}

/* Title */
Expand All @@ -2151,33 +2147,53 @@

/* Connector Wrapper */
.pc-stepper__connector-wrapper {
@apply flex self-start md:self-center;
@apply flex self-start shrink md:self-center;
}

/* Size Variants */
.pc-stepper--sm .pc-stepper__connector-wrapper {
@apply pl-4 md:pl-0; /* Adjust padding-left for small size */
@apply pl-4 md:pl-0;
}

.pc-stepper--md .pc-stepper__connector-wrapper {
@apply pl-5 md:pl-0; /* Adjust padding-left for medium size */
@apply pl-5 md:pl-0;
}

.pc-stepper--lg .pc-stepper__connector-wrapper {
@apply pl-6 md:pl-0; /* Adjust padding-left for large size */
@apply pl-6 md:pl-0;
}

.pc-stepper--horizontal .pc-stepper__connector-wrapper {
@apply h-full md:w-full md:h-auto;
@apply flex h-full md:w-full md:h-auto;
}

.pc-stepper--vertical .pc-stepper__connector-wrapper {
@apply w-10 h-full ml-5;

/* Vertical Orientation Connector Wrapper Adjustments */

/* Small Size */
.pc-stepper--vertical.pc-stepper--sm .pc-stepper__connector-wrapper {
@apply self-start;
@apply md:pl-4;
}

/* Medium Size */
.pc-stepper--vertical.pc-stepper--md .pc-stepper__connector-wrapper {
@apply self-start;
@apply md:pl-5;
}

/* Large Size */
.pc-stepper--vertical.pc-stepper--lg .pc-stepper__connector-wrapper {
@apply self-start;
@apply md:pl-6;
}
.pc-stepper--vertical .pc-stepper__container {
@apply md:gap-1;
}

/* Connector */
.pc-stepper__connector {
@apply flex-shrink bg-gray-200 dark:bg-gray-600;
@apply bg-gray-200 shrink dark:bg-gray-600;
}

.pc-stepper__connector--complete {
Expand All @@ -2192,6 +2208,7 @@
@apply w-0.5 h-8 mx-auto;
}


/* Rating */

.pc-rating__wrapper {
Expand Down
10 changes: 3 additions & 7 deletions lib/petal_components/field.ex
Original file line number Diff line number Diff line change
Expand Up @@ -472,13 +472,9 @@ defmodule PetalComponents.Field do
"""
end

def field(%{clearable: true} = assigns) do
assigns =
assigns
|> assign(:class, [assigns.class, get_class_for_type(assigns.type || "text")])
|> assign_new(:value, fn ->
Phoenix.HTML.Form.normalize_value(assigns.type || "text", assigns.value) || ""
end)
def field(%{type: type, clearable: true} = assigns)
when type in ["text", "search", "url", "email", "tel"] do
assigns = assign(assigns, class: [assigns.class, get_class_for_type(assigns.type)])

~H"""
<.field_wrapper errors={@errors} name={@name} class={@wrapper_class}>
Expand Down
147 changes: 147 additions & 0 deletions lib/petal_components/input.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule PetalComponents.Input do
use Phoenix.Component
import PetalComponents.Icon

@moduledoc """
Renders pure inputs (no label or errors).
Expand All @@ -17,6 +18,18 @@ defmodule PetalComponents.Input do

attr :size, :string, default: "md", values: ~w(xs sm md lg xl), doc: "the size of the switch"

attr :viewable, :boolean,
default: false,
doc: "If true, adds a toggle to show/hide the password text"

attr :copyable, :boolean,
default: false,
doc: "If true, adds a copy button to the field and disables the input"

attr :clearable, :boolean,
default: false,
doc: "If true, adds a clear button to clear the field value"

attr :field, Phoenix.HTML.FormField,
doc: "a form field struct retrieved from the form, for example: @form[:email]"

Expand Down Expand Up @@ -81,6 +94,134 @@ defmodule PetalComponents.Input do
"""
end

def input(%{type: "password", viewable: true} = assigns) do
assigns = assign(assigns, class: [assigns.class, get_class_for_type(assigns.type)])

~H"""
<div class="pc-password-field-wrapper" x-data="{ show: false }">
<input
x-bind:type="show ? 'text' : 'password'"
name={@name}
id={@id}
value={Phoenix.HTML.Form.normalize_value(@type, @value)}
class={[@class, "pc-password-field-input"]}
{@rest}
/>
<button type="button" class="pc-password-field-toggle-button" @click="show = !show">
<span x-show="!show" class="pc-password-field-toggle-icon-container">
<.icon name="hero-eye-solid" class="pc-password-field-toggle-icon" />
</span>
<span x-show="show" class="pc-password-field-toggle-icon-container" style="display: none;">
<.icon name="hero-eye-slash-solid" class="pc-password-field-toggle-icon" />
</span>
</button>
</div>
"""
end

def input(%{type: type, copyable: true} = assigns) when type in ["text", "url", "email"] do
assigns = assign(assigns, class: [assigns.class, get_class_for_type(assigns.type)])

~H"""
<div class="pc-copyable-field-wrapper" x-data="{ copied: false }">
<input
x-ref="copyInput"
type={@type || "text"}
name={@name}
id={@id}
value={Phoenix.HTML.Form.normalize_value(@type || "text", @value)}
class={[@class, "pc-copyable-field-input"]}
readonly
{@rest}
/>
<button
type="button"
class="pc-copyable-field-button"
@click="
navigator.clipboard.writeText($refs.copyInput.value)
.then(() => { copied = true; setTimeout(() => copied = false, 2000); })
"
>
<span x-show="!copied" class="pc-copyable-field-icon-container">
<.icon name="hero-clipboard-document-solid" class="pc-copyable-field-icon" />
</span>
<span x-show="copied" class="pc-copyable-field-icon-container" style="display: none;">
<.icon name="hero-clipboard-document-check-solid" class="pc-copyable-field-icon" />
</span>
</button>
</div>
"""
end

def input(%{type: type, clearable: true} = assigns)
when type in ["text", "search", "url", "email", "tel"] do
assigns = assign(assigns, class: [assigns.class, get_class_for_type(assigns.type)])

~H"""
<div
class="pc-clearable-field-wrapper"
x-data="{ showClearButton: false }"
x-init="showClearButton = $refs.clearInput.value.length > 0"
>
<input
x-ref="clearInput"
type={@type || "text"}
name={@name}
id={@id}
value={@value}
class={[@class, "pc-clearable-field-input"]}
{@rest}
x-on:input="showClearButton = $event.target.value.length > 0"
/>
<button
type="button"
class="pc-clearable-field-button"
x-show="showClearButton"
x-on:click="
$refs.clearInput.value = '';
showClearButton = false;
$refs.clearInput.dispatchEvent(new Event('input'));
"
style="display: none;"
aria-label="Clear input"
>
<span class="pc-clearable-field-icon-container">
<.icon name="hero-x-mark-solid" class="pc-clearable-field-icon" />
</span>
</button>
</div>
"""
end

def input(%{type: type} = assigns)
when type in ["date", "datetime-local", "time", "month", "week"] do
assigns =
assign(assigns,
class: [assigns.class, "pc-date-input pc-date-picker-indicator"],
icon_name: get_icon_for_type(assigns.type)
)

~H"""
<div class="pc-date-input-wrapper">
<input
type={@type}
name={@name}
id={@id}
value={Phoenix.HTML.Form.normalize_value(@type, @value)}
class={@class}
{@rest}
/>
<button
type="button"
class="pc-date-input-icon"
onclick="this.previousElementSibling.showPicker()"
>
<.icon name={@icon_name} class="w-5 h-5 text-gray-400" />
</button>
</div>
"""
end

def input(assigns) do
~H"""
<input
Expand All @@ -100,4 +241,10 @@ defmodule PetalComponents.Input do
defp get_class_for_type("file"), do: "pc-file-input"
defp get_class_for_type("range"), do: "pc-range-input"
defp get_class_for_type(_), do: "pc-text-input"

defp get_icon_for_type("date"), do: "hero-calendar"
defp get_icon_for_type("datetime-local"), do: "hero-calendar"
defp get_icon_for_type("month"), do: "hero-calendar"
defp get_icon_for_type("week"), do: "hero-calendar"
defp get_icon_for_type("time"), do: "hero-clock"
end
Loading

0 comments on commit a1a4130

Please sign in to comment.