Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions assets/js/dashboard/components/form-elements.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React, { ReactNode } from 'react'
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'

export const LabeledTextInput = ({
label,
id,
value,
onChange,
placeholder
}: {
label: string
id: string
value: string
onChange: (value: string) => void
placeholder: string
}) => {
return (
<div className="flex flex-col">
<label
htmlFor={id}
className="block mb-1.5 text-sm font-medium dark:text-gray-100 text-gray-700 dark:text-gray-300"
>
{label}
</label>
<input
autoComplete="off"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
id={id}
className="block px-3.5 py-2.5 w-full text-sm dark:text-gray-300 rounded-md border border-gray-300 dark:border-gray-750 dark:bg-gray-750 focus:outline-none focus:ring-3 focus:ring-indigo-500/20 dark:focus:ring-indigo-500/25 focus:border-indigo-500"
/>
</div>
)
}

export const TypeSelector = <T extends string>({
value,
onChange,
options,
idPrefix
}: {
value: T
onChange: (value: T) => void
options: { type: T; name: string; description: string }[]
idPrefix: string
}) => {
return (
<div className="flex flex-col gap-y-2">
{options.map(({ type, name, description }) => (
<div key={type}>
<div className="flex">
<input
checked={value === type}
id={`${idPrefix}-${type}`}
type="radio"
value=""
onChange={() => onChange(type)}
className="mt-px size-4.5 cursor-pointer text-indigo-600 dark:bg-transparent border-gray-400 dark:border-gray-600 checked:border-indigo-600 dark:checked:border-white"
/>
<label
htmlFor={`${idPrefix}-${type}`}
className="block ml-3 text-sm font-medium dark:text-gray-100 flex flex-col flex-inline"
>
<div>{name}</div>
<div className="text-gray-500 dark:text-gray-400 text-sm font-normal">
{description}
</div>
</label>
</div>
</div>
))}
</div>
)
}

export const TypeDisabledMessage = ({
message
}: {
message: ReactNode | null
}) => {
if (!message) return null

return (
<div className="mt-2 flex gap-x-2 text-sm">
<ExclamationTriangleIcon className="mt-1 block w-4 h-4 shrink-0" />
<div>{message}</div>
</div>
)
}

/** Keep this component styled the same as checkboxes in PlausibleWeb.Live.Installation.Instructions */
export const Checkbox = ({
id,
checked,
onChange,
children
}: React.DetailedHTMLProps<
React.InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>) => {
return (
<label
className="text-sm block font-medium dark:text-gray-100 font-normal gap-x-2 flex flex-inline items-center justify-start"
htmlFor={id}
>
<input
className="block size-5 rounded-sm dark:bg-gray-600 border-gray-300 dark:border-gray-600 text-indigo-600"
id={id}
type="checkbox"
checked={checked}
onChange={onChange}
/>
{children}
</label>
)
}
19 changes: 19 additions & 0 deletions assets/js/dashboard/components/modal-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { ReactNode } from 'react'
import { XMarkIcon } from '@heroicons/react/20/solid'
import ModalWithRouting from '../stats/modals/modal'
import { Button } from './button'

export function ModalLayout({
title,
Expand Down Expand Up @@ -44,3 +45,21 @@ export function ModalLayout({
export function ModalFooter({ children }: { children: ReactNode }) {
return <div className="flex gap-x-3 items-center justify-end">{children}</div>
}

export function SaveButton({
disabled,
onSave
}: {
disabled: boolean
onSave: () => void
}) {
return (
<Button
size="sm"
disabled={disabled}
onClick={disabled ? () => {} : onSave}
>
Save
</Button>
)
}
20 changes: 20 additions & 0 deletions assets/js/dashboard/components/placeholder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import classNames from 'classnames'
import React, { ReactNode } from 'react'

export const Placeholder = ({
children,
placeholder
}: {
children: ReactNode | false
placeholder: ReactNode
}) => (
<span
className={classNames(
'rounded',
children === false &&
'bg-gray-100 dark:bg-gray-700 text-gray-100 dark:text-gray-700'
)}
>
{children === false ? placeholder : children}
</span>
)
Loading
Loading