add template in hard
This commit is contained in:
11
front/src/components/ui/Background.astro
Normal file
11
front/src/components/ui/Background.astro
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
export interface Props {
|
||||
isDark?: boolean;
|
||||
}
|
||||
|
||||
const { isDark = false } = Astro.props;
|
||||
---
|
||||
|
||||
<div class:list={['absolute inset-0', { 'bg-dark dark:bg-transparent': isDark }]}>
|
||||
<slot />
|
||||
</div>
|
40
front/src/components/ui/Button.astro
Normal file
40
front/src/components/ui/Button.astro
Normal file
@ -0,0 +1,40 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { CallToAction as Props } from '~/types';
|
||||
|
||||
const {
|
||||
variant = 'secondary',
|
||||
target,
|
||||
text = Astro.slots.render('default'),
|
||||
icon = '',
|
||||
class: className = '',
|
||||
type,
|
||||
...rest
|
||||
} = Astro.props;
|
||||
|
||||
const variants = {
|
||||
primary: 'btn-primary',
|
||||
secondary: 'btn-secondary',
|
||||
tertiary: 'btn btn-tertiary',
|
||||
link: 'cursor-pointer hover:text-primary',
|
||||
};
|
||||
---
|
||||
|
||||
{
|
||||
type === 'button' || type === 'submit' || type === 'reset' ? (
|
||||
<button type={type} class={twMerge(variants[variant] || '', className)} {...rest}>
|
||||
<Fragment set:html={text} />
|
||||
{icon && <Icon name={icon} class="w-5 h-5 ml-1 -mr-1.5 rtl:mr-1 rtl:-ml-1.5 inline-block" />}
|
||||
</button>
|
||||
) : (
|
||||
<a
|
||||
class={twMerge(variants[variant] || '', className)}
|
||||
{...(target ? { target: target, rel: 'noopener noreferrer' } : {})}
|
||||
{...rest}
|
||||
>
|
||||
<Fragment set:html={text} />
|
||||
{icon && <Icon name={icon} class="w-5 h-5 ml-1 -mr-1.5 rtl:mr-1 rtl:-ml-1.5 inline-block" />}
|
||||
</a>
|
||||
)
|
||||
}
|
22
front/src/components/ui/DListItem.astro
Normal file
22
front/src/components/ui/DListItem.astro
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
// component: DListItem
|
||||
//
|
||||
// Mimics the html 'dl' (description list)
|
||||
//
|
||||
// The 'dt' item is the item 'term' and is inserted into an 'h6' tag.
|
||||
// Caller needs to style the 'h6' tag appropriately.
|
||||
//
|
||||
// You can put pretty much any content you want between the open and
|
||||
// closing tags - it's simply contained in an enclosing div with a
|
||||
// margin left. No need for 'dd' items.
|
||||
//
|
||||
const { dt } = Astro.props;
|
||||
interface Props {
|
||||
dt: string;
|
||||
}
|
||||
|
||||
const content: string = await Astro.slots.render('default');
|
||||
---
|
||||
|
||||
<h6 set:html={dt} />
|
||||
<div class="dd ml-8" set:html={content} />
|
87
front/src/components/ui/Form.astro
Normal file
87
front/src/components/ui/Form.astro
Normal file
@ -0,0 +1,87 @@
|
||||
---
|
||||
import type { Form as Props } from '~/types';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
const { inputs, textarea, disclaimer, button = 'Contact us', description = '' } = Astro.props;
|
||||
---
|
||||
|
||||
<form>
|
||||
{
|
||||
inputs &&
|
||||
inputs.map(
|
||||
({ type = 'text', name, label = '', autocomplete = 'on', placeholder = '' }) =>
|
||||
name && (
|
||||
<div class="mb-6">
|
||||
{label && (
|
||||
<label for={name} class="block text-sm font-medium">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<input
|
||||
type={type}
|
||||
name={name}
|
||||
id={name}
|
||||
autocomplete={autocomplete}
|
||||
placeholder={placeholder}
|
||||
class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
textarea && (
|
||||
<div>
|
||||
<label for="textarea" class="block text-sm font-medium">
|
||||
{textarea.label}
|
||||
</label>
|
||||
<textarea
|
||||
id="textarea"
|
||||
name={textarea.name ? textarea.name : 'message'}
|
||||
rows={textarea.rows ? textarea.rows : 4}
|
||||
placeholder={textarea.placeholder}
|
||||
class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
disclaimer && (
|
||||
<div class="mt-3 flex items-start">
|
||||
<div class="flex mt-0.5">
|
||||
<input
|
||||
id="disclaimer"
|
||||
name="disclaimer"
|
||||
type="checkbox"
|
||||
class="cursor-pointer mt-1 py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<label for="disclaimer" class="cursor-pointer select-none text-sm text-gray-600 dark:text-gray-400">
|
||||
{disclaimer.label}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
button && (
|
||||
<div class="mt-10 grid">
|
||||
<Button variant="primary" type="submit">
|
||||
{button}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
description && (
|
||||
<div class="mt-3 text-center">
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</form>
|
35
front/src/components/ui/Headline.astro
Normal file
35
front/src/components/ui/Headline.astro
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
import type { Headline as Props } from '~/types';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
tagline,
|
||||
classes = {},
|
||||
} = Astro.props;
|
||||
|
||||
const {
|
||||
container: containerClass = 'max-w-3xl',
|
||||
title: titleClass = 'text-3xl md:text-4xl ',
|
||||
subtitle: subtitleClass = 'text-xl',
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
(title || subtitle || tagline) && (
|
||||
<div class={twMerge('mb-8 md:mx-auto md:mb-12 text-center', containerClass)}>
|
||||
{tagline && (
|
||||
<p class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase" set:html={tagline} />
|
||||
)}
|
||||
{title && (
|
||||
<h2
|
||||
class={twMerge('font-bold leading-tighter tracking-tighter font-heading text-heading text-3xl', titleClass)}
|
||||
set:html={title}
|
||||
/>
|
||||
)}
|
||||
|
||||
{subtitle && <p class={twMerge('mt-4 text-muted', subtitleClass)} set:html={subtitle} />}
|
||||
</div>
|
||||
)
|
||||
}
|
65
front/src/components/ui/ItemGrid.astro
Normal file
65
front/src/components/ui/ItemGrid.astro
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
import type { ItemGrid as Props } from '~/types';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import Button from './Button.astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
const { items = [], columns, defaultIcon = '', classes = {} } = Astro.props;
|
||||
|
||||
const {
|
||||
container: containerClass = '',
|
||||
panel: panelClass = '',
|
||||
title: titleClass = '',
|
||||
description: descriptionClass = '',
|
||||
icon: defaultIconClass = 'text-primary',
|
||||
action: actionClass = '',
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
items && (
|
||||
<div
|
||||
class={twMerge(
|
||||
`grid mx-auto gap-8 md:gap-y-12 ${
|
||||
columns === 4
|
||||
? 'lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 3
|
||||
? 'lg:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 2
|
||||
? 'sm:grid-cols-2 '
|
||||
: ''
|
||||
}`,
|
||||
containerClass
|
||||
)}
|
||||
>
|
||||
{items.map(({ title, description, icon, callToAction, classes: itemClasses = {} }) => (
|
||||
<div>
|
||||
<div class={twMerge('flex flex-row max-w-md', panelClass, itemClasses?.panel)}>
|
||||
<div class="flex justify-center">
|
||||
{(icon || defaultIcon) && (
|
||||
<Icon
|
||||
name={icon || defaultIcon}
|
||||
class={twMerge('w-7 h-7 mr-2 rtl:mr-0 rtl:ml-2', defaultIconClass, itemClasses?.icon)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div class="mt-0.5">
|
||||
{title && <h3 class={twMerge('text-xl font-bold', titleClass, itemClasses?.title)}>{title}</h3>}
|
||||
{description && (
|
||||
<p
|
||||
class={twMerge(`${title ? 'mt-3' : ''} text-muted`, descriptionClass, itemClasses?.description)}
|
||||
set:html={description}
|
||||
/>
|
||||
)}
|
||||
{callToAction && (
|
||||
<div class={twMerge(`${title || description ? 'mt-3' : ''}`, actionClass, itemClasses?.actionClass)}>
|
||||
<Button variant="link" {...callToAction} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
53
front/src/components/ui/ItemGrid2.astro
Normal file
53
front/src/components/ui/ItemGrid2.astro
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
import type { ItemGrid as Props } from '~/types';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import Button from './Button.astro';
|
||||
|
||||
const { items = [], columns, defaultIcon = '', classes = {} } = Astro.props;
|
||||
|
||||
const {
|
||||
container: containerClass = '',
|
||||
// container: containerClass = "sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
|
||||
panel: panelClass = '',
|
||||
title: titleClass = '',
|
||||
description: descriptionClass = '',
|
||||
icon: defaultIconClass = 'text-primary',
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
items && (
|
||||
<div
|
||||
class={twMerge(
|
||||
`grid gap-8 gap-x-12 sm:gap-y-8 ${
|
||||
columns === 4
|
||||
? 'lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 3
|
||||
? 'lg:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 2
|
||||
? 'sm:grid-cols-2 '
|
||||
: ''
|
||||
}`,
|
||||
containerClass
|
||||
)}
|
||||
>
|
||||
{items.map(({ title, description, icon, callToAction, classes: itemClasses = {} }) => (
|
||||
<div class={twMerge('relative flex flex-col', panelClass, itemClasses?.panel)}>
|
||||
{(icon || defaultIcon) && (
|
||||
<Icon name={icon || defaultIcon} class={twMerge('mb-2 w-10 h-10', defaultIconClass, itemClasses?.icon)} />
|
||||
)}
|
||||
<div class={twMerge('text-xl font-bold', titleClass, itemClasses?.title)}>{title}</div>
|
||||
{description && (
|
||||
<p class={twMerge('text-muted mt-2', descriptionClass, itemClasses?.description)} set:html={description} />
|
||||
)}
|
||||
{callToAction && (
|
||||
<div class="mt-2">
|
||||
<Button {...callToAction} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
54
front/src/components/ui/Timeline.astro
Normal file
54
front/src/components/ui/Timeline.astro
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { Item } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
items?: Array<Item>;
|
||||
defaultIcon?: string;
|
||||
classes?: Record<string, string>;
|
||||
}
|
||||
|
||||
const { items = [], classes = {}, defaultIcon } = Astro.props as Props;
|
||||
|
||||
const {
|
||||
container: containerClass = '',
|
||||
panel: panelClass = '',
|
||||
title: titleClass = '',
|
||||
description: descriptionClass = '',
|
||||
icon: defaultIconClass = 'text-primary dark:text-slate-200 border-primary dark:border-blue-700',
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
items && items.length && (
|
||||
<div class={containerClass}>
|
||||
{items.map(({ title, description, icon, classes: itemClasses = {} }, index = 0) => (
|
||||
<div class={twMerge('flex', panelClass, itemClasses?.panel)}>
|
||||
<div class="flex flex-col items-center mr-4 rtl:mr-0 rtl:ml-4">
|
||||
<div>
|
||||
<div class="flex items-center justify-center">
|
||||
{(icon || defaultIcon) && (
|
||||
<Icon
|
||||
name={icon || defaultIcon}
|
||||
class={twMerge('w-10 h-10 p-2 rounded-full border-2', defaultIconClass, itemClasses?.icon)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{index !== items.length - 1 && <div class="w-px h-full bg-black/10 dark:bg-slate-400/50" />}
|
||||
</div>
|
||||
<div class={`pt-1 ${index !== items.length - 1 ? 'pb-8' : ''}`}>
|
||||
{title && <p class={twMerge('text-xl font-bold', titleClass, itemClasses?.title)} set:html={title} />}
|
||||
{description && (
|
||||
<p
|
||||
class={twMerge('text-muted mt-2', descriptionClass, itemClasses?.description)}
|
||||
set:html={description}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
31
front/src/components/ui/WidgetWrapper.astro
Normal file
31
front/src/components/ui/WidgetWrapper.astro
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
import type { HTMLTag } from 'astro/types';
|
||||
import type { Widget } from '~/types';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import Background from './Background.astro';
|
||||
|
||||
export interface Props extends Widget {
|
||||
containerClass?: string;
|
||||
['as']?: HTMLTag;
|
||||
}
|
||||
|
||||
const { id, isDark = false, containerClass = '', bg, as = 'section' } = Astro.props;
|
||||
|
||||
const WrapperTag = as;
|
||||
---
|
||||
|
||||
<WrapperTag class="relative not-prose scroll-mt-[72px]" {...id ? { id } : {}}>
|
||||
<div class="absolute inset-0 pointer-events-none -z-[1]" aria-hidden="true">
|
||||
<slot name="bg">
|
||||
{bg ? <Fragment set:html={bg} /> : <Background isDark={isDark} />}
|
||||
</slot>
|
||||
</div>
|
||||
<div
|
||||
class:list={[
|
||||
twMerge('relative mx-auto max-w-7xl px-4 md:px-6 py-12 md:py-16 lg:py-20 text-default', containerClass),
|
||||
{ dark: isDark },
|
||||
]}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</WrapperTag>
|
Reference in New Issue
Block a user