Packages
corex
0.1.0-alpha.33
0.1.2
0.1.1
0.1.0
0.1.0-rc.1
0.1.0-rc.0
0.1.0-beta.5
0.1.0-beta.4
0.1.0-beta.3
0.1.0-beta.2
0.1.0-beta.1
0.1.0-alpha.33
0.1.0-alpha.32
0.1.0-alpha.31
0.1.0-alpha.30
0.1.0-alpha.29
0.1.0-alpha.28
0.1.0-alpha.27
0.1.0-alpha.26
0.1.0-alpha.25
0.1.0-alpha.24
0.1.0-alpha.23
0.1.0-alpha.22
0.1.0-alpha.21
0.1.0-alpha.20
0.1.0-alpha.19
0.1.0-alpha.18
0.1.0-alpha.17
0.1.0-alpha.16
0.1.0-alpha.15
0.1.0-alpha.14
0.1.0-alpha.13
0.1.0-alpha.12
0.1.0-alpha.11
0.1.0-alpha.10
0.1.0-alpha.9
0.1.0-alpha.8
0.1.0-alpha.7
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
Accessible and unstyled UI components library written in Elixir and TypeScript that integrates Zag.js state machines into the Phoenix Framework.
Current section
Files
Jump to
Current section
Files
lib/components/timer.ex
defmodule Corex.Timer do
@moduledoc ~S'''
Phoenix implementation of [Zag.js Timer](https://zagjs.com/components/react/timer).
## Examples
### Basic
```heex
<.timer id="t" start_ms={60_000} class="timer">
<:start_trigger><.heroicon name="hero-play" class="icon" /></:start_trigger>
<:pause_trigger><.heroicon name="hero-pause" class="icon" /></:pause_trigger>
<:resume_trigger><.heroicon name="hero-play" class="icon" /></:resume_trigger>
<:reset_trigger><.heroicon name="hero-arrow-path" class="icon" /></:reset_trigger>
</.timer>
```
### Countdown
```heex
<.timer id="t" countdown start_ms={90_000} target_ms={0} auto_start class="timer">
<:start_trigger><.heroicon name="hero-play" class="icon" /></:start_trigger>
<:pause_trigger><.heroicon name="hero-pause" class="icon" /></:pause_trigger>
<:resume_trigger><.heroicon name="hero-play" class="icon" /></:resume_trigger>
<:reset_trigger><.heroicon name="hero-arrow-path" class="icon" /></:reset_trigger>
</.timer>
```
Required slots: `:start_trigger`, `:pause_trigger`, `:resume_trigger`, `:reset_trigger`.
## Styling
Use data attributes to target elements:
```css
[data-scope="timer"][data-part="root"] {}
[data-scope="timer"][data-part="area"] {}
[data-scope="timer"][data-part="item"] {}
[data-scope="timer"][data-part="separator"] {}
[data-scope="timer"][data-part="control"] {}
[data-scope="timer"][data-part="action-trigger"] {}
```
If you wish to use the default Corex styling, you can use the class `timer` on the component.
This requires to install `Mix.Tasks.Corex.Design` first and import the component css file.
```css
@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/timer.css";
```
You can then use modifiers
```heex
<.timer class="timer timer--accent timer--lg">
</.timer>
```
Learn more about modifiers and [Corex Design](https://corex-ui.com/components/timer#modifiers)
'''
@doc type: :component
use Phoenix.Component
alias Corex.Timer.Anatomy.{ActionTrigger, Area, Control, Item, Props, Root, Separator}
alias Corex.Timer.Connect
attr(:id, :string, required: false)
attr(:countdown, :boolean, default: false)
attr(:start_ms, :integer, default: 0)
attr(:target_ms, :integer, default: nil)
attr(:auto_start, :boolean, default: false)
attr(:interval, :integer, default: 1000)
attr(:on_tick, :string, default: nil)
attr(:on_complete, :string, default: nil)
attr(:rest, :global)
slot :start_trigger, required: true do
attr(:class, :string, required: false)
end
slot :pause_trigger, required: true do
attr(:class, :string, required: false)
end
slot :resume_trigger, required: true do
attr(:class, :string, required: false)
end
slot :reset_trigger, required: true do
attr(:class, :string, required: false)
end
def timer(assigns) do
assigns =
assigns
|> assign_new(:id, fn -> "timer-#{System.unique_integer([:positive])}" end)
|> assign(:time_values, time_values(assigns.start_ms))
|> assign(:running, assigns.auto_start)
|> assign(:paused, false)
~H"""
<div
id={@id}
phx-hook="Timer"
{@rest}
{Connect.props(%Props{
id: @id,
countdown: @countdown,
start_ms: @start_ms,
target_ms: @target_ms,
auto_start: @auto_start,
interval: @interval,
on_tick: @on_tick,
on_complete: @on_complete
})}
>
<div phx-update="ignore" {Connect.root(%Root{id: @id})}>
<div {Connect.area(%Area{id: @id})}>
<div {Connect.item(%Item{type: "days", value: @time_values.days})}></div>
<div {Connect.separator(%Separator{})}>:</div>
<div {Connect.item(%Item{type: "hours", value: @time_values.hours})}></div>
<div {Connect.separator(%Separator{})}>:</div>
<div {Connect.item(%Item{type: "minutes", value: @time_values.minutes})}></div>
<div {Connect.separator(%Separator{})}>:</div>
<div {Connect.item(%Item{type: "seconds", value: @time_values.seconds})}></div>
</div>
<div {Connect.control(%Control{id: @id})}>
<button type="button" {Connect.action_trigger(%ActionTrigger{action: "start", hidden: @running or @paused})}>
{render_slot(@start_trigger)}
</button>
<button type="button" {Connect.action_trigger(%ActionTrigger{action: "pause", hidden: not @running})}>
{render_slot(@pause_trigger)}
</button>
<button type="button" {Connect.action_trigger(%ActionTrigger{action: "resume", hidden: not @paused})}>
{render_slot(@resume_trigger)}
</button>
<button type="button" {Connect.action_trigger(%ActionTrigger{action: "reset", hidden: not @running and not @paused})}>
{render_slot(@reset_trigger)}
</button>
</div>
</div>
</div>
"""
end
defp time_values(ms) do
ms = max(0, ms)
seconds = div(rem(ms, 60_000), 1_000)
minutes = div(rem(ms, 3_600_000), 60_000)
hours = div(rem(ms, 86_400_000), 3_600_000)
days = div(ms, 86_400_000)
%{days: days, hours: hours, minutes: minutes, seconds: seconds}
end
end