ppij-design-system 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/banners.d.ts +31 -0
- package/dist/banners.js +18 -0
- package/dist/card-themes.d.ts +14 -0
- package/dist/card-themes.js +325 -0
- package/dist/components/account-menu.d.ts +24 -0
- package/dist/components/account-menu.js +13 -0
- package/dist/components/admin-drawer.d.ts +34 -0
- package/dist/components/admin-drawer.js +30 -0
- package/dist/components/alert-dialog.d.ts +16 -0
- package/dist/components/alert-dialog.js +39 -0
- package/dist/components/alert.d.ts +11 -0
- package/dist/components/alert.js +29 -0
- package/dist/components/avatar.d.ts +11 -0
- package/dist/components/avatar.js +22 -0
- package/dist/components/badge.d.ts +9 -0
- package/dist/components/badge.js +27 -0
- package/dist/components/banner-carousel.d.ts +14 -0
- package/dist/components/banner-carousel.js +90 -0
- package/dist/components/bottom-bar.d.ts +22 -0
- package/dist/components/bottom-bar.js +12 -0
- package/dist/components/button.d.ts +10 -0
- package/dist/components/button.js +37 -0
- package/dist/components/card-upload.d.ts +30 -0
- package/dist/components/card-upload.js +19 -0
- package/dist/components/card.d.ts +9 -0
- package/dist/components/card.js +24 -0
- package/dist/components/checkbox.d.ts +4 -0
- package/dist/components/checkbox.js +9 -0
- package/dist/components/data-table.d.ts +26 -0
- package/dist/components/data-table.js +17 -0
- package/dist/components/dialog.d.ts +17 -0
- package/dist/components/dialog.js +37 -0
- package/dist/components/drawer.d.ts +13 -0
- package/dist/components/drawer.js +34 -0
- package/dist/components/dropdown-menu.d.ts +25 -0
- package/dist/components/dropdown-menu.js +50 -0
- package/dist/components/editable-panel.d.ts +25 -0
- package/dist/components/editable-panel.js +18 -0
- package/dist/components/empty.d.ts +12 -0
- package/dist/components/empty.js +33 -0
- package/dist/components/file-upload.d.ts +28 -0
- package/dist/components/file-upload.js +21 -0
- package/dist/components/form-panel.d.ts +15 -0
- package/dist/components/form-panel.js +9 -0
- package/dist/components/funding-picker.d.ts +20 -0
- package/dist/components/funding-picker.js +16 -0
- package/dist/components/input.d.ts +3 -0
- package/dist/components/input.js +6 -0
- package/dist/components/invitation-card.d.ts +14 -0
- package/dist/components/invitation-card.js +13 -0
- package/dist/components/label.d.ts +4 -0
- package/dist/components/label.js +7 -0
- package/dist/components/member-card-block.d.ts +47 -0
- package/dist/components/member-card-block.js +329 -0
- package/dist/components/member-card.css +92 -0
- package/dist/components/member-card.d.ts +29 -0
- package/dist/components/member-card.js +22 -0
- package/dist/components/month-picker.d.ts +16 -0
- package/dist/components/month-picker.js +19 -0
- package/dist/components/offline-banner.d.ts +14 -0
- package/dist/components/offline-banner.js +14 -0
- package/dist/components/page-header.d.ts +14 -0
- package/dist/components/page-header.js +10 -0
- package/dist/components/phone-field.d.ts +25 -0
- package/dist/components/phone-field.js +64 -0
- package/dist/components/pill-radio.d.ts +21 -0
- package/dist/components/pill-radio.js +15 -0
- package/dist/components/portal-shell.d.ts +53 -0
- package/dist/components/portal-shell.js +19 -0
- package/dist/components/postcode-field.d.ts +26 -0
- package/dist/components/postcode-field.js +14 -0
- package/dist/components/progress.d.ts +4 -0
- package/dist/components/progress.js +8 -0
- package/dist/components/radio-group.d.ts +5 -0
- package/dist/components/radio-group.js +11 -0
- package/dist/components/review-panel.d.ts +20 -0
- package/dist/components/review-panel.js +14 -0
- package/dist/components/saving-indicator.d.ts +15 -0
- package/dist/components/saving-indicator.js +19 -0
- package/dist/components/scroll-area.d.ts +5 -0
- package/dist/components/scroll-area.js +13 -0
- package/dist/components/search-filter.d.ts +39 -0
- package/dist/components/search-filter.js +19 -0
- package/dist/components/segmented-progress.d.ts +16 -0
- package/dist/components/segmented-progress.js +12 -0
- package/dist/components/select.d.ts +15 -0
- package/dist/components/select.js +38 -0
- package/dist/components/separator.d.ts +4 -0
- package/dist/components/separator.js +7 -0
- package/dist/components/service-tiles.d.ts +26 -0
- package/dist/components/service-tiles.js +17 -0
- package/dist/components/sheet.d.ts +14 -0
- package/dist/components/sheet.js +40 -0
- package/dist/components/sidebar.d.ts +69 -0
- package/dist/components/sidebar.js +210 -0
- package/dist/components/skeleton.d.ts +11 -0
- package/dist/components/skeleton.js +11 -0
- package/dist/components/sonner.d.ts +9 -0
- package/dist/components/sonner.js +25 -0
- package/dist/components/spinner.d.ts +14 -0
- package/dist/components/spinner.js +22 -0
- package/dist/components/stat-card.d.ts +22 -0
- package/dist/components/stat-card.js +12 -0
- package/dist/components/status-card.d.ts +24 -0
- package/dist/components/status-card.js +26 -0
- package/dist/components/stepper.d.ts +25 -0
- package/dist/components/stepper.js +23 -0
- package/dist/components/submit-overlay.d.ts +16 -0
- package/dist/components/submit-overlay.js +15 -0
- package/dist/components/suggestion-sheet.d.ts +20 -0
- package/dist/components/suggestion-sheet.js +36 -0
- package/dist/components/switch.d.ts +6 -0
- package/dist/components/switch.js +8 -0
- package/dist/components/table.d.ts +10 -0
- package/dist/components/table.js +28 -0
- package/dist/components/tabs.d.ts +11 -0
- package/dist/components/tabs.js +28 -0
- package/dist/components/tag-input.d.ts +15 -0
- package/dist/components/tag-input.js +31 -0
- package/dist/components/textarea.d.ts +3 -0
- package/dist/components/textarea.js +6 -0
- package/dist/components/timeline.d.ts +18 -0
- package/dist/components/timeline.js +11 -0
- package/dist/components/todo-strip.d.ts +14 -0
- package/dist/components/todo-strip.js +11 -0
- package/dist/components/tooltip.d.ts +7 -0
- package/dist/components/tooltip.js +16 -0
- package/dist/components/top-bar.d.ts +23 -0
- package/dist/components/top-bar.js +14 -0
- package/dist/components/type-ahead.d.ts +20 -0
- package/dist/components/type-ahead.js +22 -0
- package/dist/components/unit-card.d.ts +26 -0
- package/dist/components/unit-card.js +18 -0
- package/dist/components/view-toggle.d.ts +20 -0
- package/dist/components/view-toggle.js +15 -0
- package/dist/components/wizard-footer.d.ts +24 -0
- package/dist/components/wizard-footer.js +17 -0
- package/dist/components/wizard-header.d.ts +29 -0
- package/dist/components/wizard-header.js +18 -0
- package/dist/hooks/use-mobile.d.ts +1 -0
- package/dist/hooks/use-mobile.js +11 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +70 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/sample-banners.d.ts +8 -0
- package/dist/sample-banners.js +28 -0
- package/dist/tokens.css +119 -0
- package/dist/tokens.json +50 -0
- package/package.json +60 -4
- package/README.md +0 -3
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
/**
|
|
4
|
+
* Top navigation bar pattern for the public site and the portal.
|
|
5
|
+
* Brand left, section links in the middle, quiet actions right.
|
|
6
|
+
* The language switch stays a quiet text action, never boxed codes.
|
|
7
|
+
* On narrow screens the links hide and the caller provides a menu
|
|
8
|
+
* through the actions slot.
|
|
9
|
+
*/
|
|
10
|
+
export function TopBar({ brand, brandHref = "/", links = [], actions, className }) {
|
|
11
|
+
return (_jsx("header", { "data-slot": "top-bar", className: cn("w-full border-b border-brand-border bg-white", className), children: _jsxs("div", { className: "mx-auto flex h-14 max-w-6xl items-center gap-4 px-4 sm:px-6", children: [_jsx("a", { href: brandHref, className: "flex shrink-0 items-center gap-2 text-brand-ink no-underline", children: brand }), links.length > 0 ? (_jsx("nav", { "aria-label": "Utama", className: "ml-2 hidden items-center gap-1 md:flex", children: links.map((link) => (_jsx("a", { href: link.href, "aria-current": link.current ? "page" : undefined, className: cn("rounded-md px-3 py-2 text-sm no-underline", link.current
|
|
12
|
+
? "font-semibold text-brand-red"
|
|
13
|
+
: "text-brand-ink hover:bg-brand-bg hover:text-brand-red"), children: link.label }, link.label + link.href))) })) : null, actions ? _jsx("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: actions }) : null] }) }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface TypeAheadProps {
|
|
3
|
+
/** Current text in the field; the chosen option once picked. */
|
|
4
|
+
value: string;
|
|
5
|
+
onChange: (value: string) => void;
|
|
6
|
+
/** Called when the person picks a suggestion. */
|
|
7
|
+
onSelect?: (value: string) => void;
|
|
8
|
+
/** Suggestion source; filtered by the current text inside the component. */
|
|
9
|
+
options: string[];
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
id?: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A text field with suggestions: the person types, matching options
|
|
16
|
+
* appear under the field, and picking one fills the field. Free text
|
|
17
|
+
* stays allowed; the page decides whether the value must come from the
|
|
18
|
+
* list. Used for institutions, majors, and cities in the wizard.
|
|
19
|
+
*/
|
|
20
|
+
export declare function TypeAhead({ value, onChange, onSelect, options, placeholder, id, className }: TypeAheadProps): React.JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { IconChevronDown } from "@tabler/icons-react";
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
import { Input } from "./input";
|
|
6
|
+
/**
|
|
7
|
+
* A text field with suggestions: the person types, matching options
|
|
8
|
+
* appear under the field, and picking one fills the field. Free text
|
|
9
|
+
* stays allowed; the page decides whether the value must come from the
|
|
10
|
+
* list. Used for institutions, majors, and cities in the wizard.
|
|
11
|
+
*/
|
|
12
|
+
export function TypeAhead({ value, onChange, onSelect, options, placeholder, id, className }) {
|
|
13
|
+
const [open, setOpen] = React.useState(false);
|
|
14
|
+
const matches = options.filter((option) => option.toLowerCase().includes(value.toLowerCase()) && option !== value).slice(0, 8);
|
|
15
|
+
const showList = open && value.trim() !== "" && matches.length > 0;
|
|
16
|
+
return (_jsxs("div", { className: cn("relative", className), children: [_jsx(Input, { id: id, value: value, placeholder: placeholder, autoComplete: "off", onChange: (event) => { onChange(event.target.value); setOpen(true); }, onFocus: () => setOpen(true), onBlur: () => setTimeout(() => setOpen(false), 120), className: "pr-9" }), _jsx(IconChevronDown, { className: "pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground" }), showList && (_jsx("ul", { className: "absolute z-50 mt-1 max-h-60 w-full overflow-auto rounded-md border bg-popover p-1 shadow-md", role: "listbox", "aria-label": "Saran", children: matches.map((option) => (_jsx("li", { children: _jsx("button", { type: "button", role: "option", "aria-selected": "false", className: "w-full rounded-sm px-2 py-1.5 text-left text-sm outline-none hover:bg-accent focus-visible:bg-accent", onMouseDown: (event) => {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
onChange(option);
|
|
19
|
+
onSelect?.(option);
|
|
20
|
+
setOpen(false);
|
|
21
|
+
}, children: option }) }, option))) }))] }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface UnitCardProps {
|
|
3
|
+
/** Unit name, for example "Okayama". */
|
|
4
|
+
name: string;
|
|
5
|
+
/** Unit kind shown as a quiet badge, for example "Komsat" or "Wilayah". */
|
|
6
|
+
kind: string;
|
|
7
|
+
/** Number of members in the unit. */
|
|
8
|
+
memberCount: number;
|
|
9
|
+
/** Name of the person who leads the unit. */
|
|
10
|
+
leadName?: string;
|
|
11
|
+
/** Status label, for example "Aktif". */
|
|
12
|
+
statusLabel: string;
|
|
13
|
+
/** Status badge tone. */
|
|
14
|
+
statusVariant?: "default" | "success" | "warning" | "secondary";
|
|
15
|
+
/** Where the card leads. When set, the whole card is a link. */
|
|
16
|
+
href?: string;
|
|
17
|
+
/** Extra actions rendered at the card foot, for example an Edit button. */
|
|
18
|
+
actions?: React.ReactNode;
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* One organisational unit (komsat, wilayah) as a card: identity first,
|
|
23
|
+
* member count and lead as quiet facts, status as a badge beside the
|
|
24
|
+
* name. Presentation only; membership and permissions stay in the app.
|
|
25
|
+
*/
|
|
26
|
+
export declare function UnitCard({ name, kind, memberCount, leadName, statusLabel, statusVariant, href, actions, className, }: UnitCardProps): React.JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { IconUsers, IconChevronRight } from "@tabler/icons-react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { Card } from "./card";
|
|
5
|
+
import { Badge } from "./badge";
|
|
6
|
+
/**
|
|
7
|
+
* One organisational unit (komsat, wilayah) as a card: identity first,
|
|
8
|
+
* member count and lead as quiet facts, status as a badge beside the
|
|
9
|
+
* name. Presentation only; membership and permissions stay in the app.
|
|
10
|
+
*/
|
|
11
|
+
export function UnitCard({ name, kind, memberCount, leadName, statusLabel, statusVariant = "success", href, actions, className, }) {
|
|
12
|
+
const body = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "grid gap-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "text-base font-semibold text-brand-ink", children: name }), _jsx(Badge, { variant: "secondary", children: kind })] }), _jsx("span", { className: "text-sm text-muted-foreground", children: leadName ? "Ketua: " + leadName : "Belum ada ketua" })] }), _jsx(Badge, { variant: statusVariant, children: statusLabel })] }), _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("span", { className: "inline-flex items-center gap-1.5 text-sm text-muted-foreground", children: [_jsx(IconUsers, { className: "size-4" }), memberCount, " anggota"] }), href ? (_jsx(IconChevronRight, { className: "size-4 text-muted-foreground" })) : (actions)] })] }));
|
|
13
|
+
const classes = cn("grid gap-4 p-5", className);
|
|
14
|
+
if (href) {
|
|
15
|
+
return (_jsx(Card, { className: cn(classes, "transition-colors hover:border-brand-red/40"), children: _jsx("a", { href: href, className: "grid gap-4 no-underline outline-none focus-visible:ring-2 focus-visible:ring-ring", children: body }) }));
|
|
16
|
+
}
|
|
17
|
+
return _jsx(Card, { className: classes, children: body });
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface ViewToggleOption {
|
|
3
|
+
label: string;
|
|
4
|
+
href: string;
|
|
5
|
+
current?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ViewToggleProps {
|
|
8
|
+
options: ViewToggleOption[];
|
|
9
|
+
ariaLabel: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Segmented toggle, generalized from the portal ViewSwitch: a quiet
|
|
14
|
+
* grey track holds the segments; the current side is a white segment
|
|
15
|
+
* with ink text and a small shadow, the other side is muted text.
|
|
16
|
+
* Red is reserved for primary actions and focus, never for navigation
|
|
17
|
+
* state. Navigation arrives as props (labels and hrefs); role and
|
|
18
|
+
* visibility rules stay in the application.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ViewToggle({ options, ariaLabel, className }: ViewToggleProps): React.JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
/**
|
|
4
|
+
* Segmented toggle, generalized from the portal ViewSwitch: a quiet
|
|
5
|
+
* grey track holds the segments; the current side is a white segment
|
|
6
|
+
* with ink text and a small shadow, the other side is muted text.
|
|
7
|
+
* Red is reserved for primary actions and focus, never for navigation
|
|
8
|
+
* state. Navigation arrives as props (labels and hrefs); role and
|
|
9
|
+
* visibility rules stay in the application.
|
|
10
|
+
*/
|
|
11
|
+
export function ViewToggle({ options, ariaLabel, className }) {
|
|
12
|
+
return (_jsx("div", { className: className, children: _jsx("div", { role: "group", "aria-label": ariaLabel, className: "inline-flex items-center gap-1 rounded-full bg-secondary p-1", children: options.map((option) => (_jsx("a", { href: option.href, "aria-current": option.current ? "page" : undefined, className: cn("flex h-10 min-w-24 items-center justify-center rounded-full px-4 text-sm no-underline focus-visible:outline-2 focus-visible:outline-brand-red", option.current
|
|
13
|
+
? "bg-card font-semibold text-brand-ink shadow-sm"
|
|
14
|
+
: "text-muted-foreground hover:text-brand-ink"), children: option.label }, option.label + option.href))) }) }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface WizardFooterProps {
|
|
2
|
+
onBack: () => void;
|
|
3
|
+
onContinue: () => void;
|
|
4
|
+
backLabel?: string;
|
|
5
|
+
continueLabel?: string;
|
|
6
|
+
backDisabled?: boolean;
|
|
7
|
+
continueDisabled?: boolean;
|
|
8
|
+
/** While continuing submits: spinner plus loadingLabel inside the primary button. */
|
|
9
|
+
continueLoading?: boolean;
|
|
10
|
+
loadingLabel?: string;
|
|
11
|
+
/** The final submit carries no arrow. */
|
|
12
|
+
showArrow?: boolean;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The wizard's fixed bottom navigation bar, extracted from
|
|
17
|
+
* Wizard.tsx: two equal buttons (Kembali outline, Lanjut primary)
|
|
18
|
+
* in a white bar pinned to the viewport bottom. Named exception to
|
|
19
|
+
* the default button size: a fixed navigation bar is a navigation
|
|
20
|
+
* target, so its buttons are field height (44px) and text-base,
|
|
21
|
+
* like the portal bottom nav. The loading state follows the rule:
|
|
22
|
+
* spinner inside the primary button.
|
|
23
|
+
*/
|
|
24
|
+
export declare function WizardFooter({ onBack, onContinue, backLabel, continueLabel, backDisabled, continueDisabled, continueLoading, loadingLabel, showArrow, className, }: WizardFooterProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { IconArrowLeft, IconArrowRight } from "@tabler/icons-react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { Button } from "./button";
|
|
5
|
+
import { Spinner } from "./spinner";
|
|
6
|
+
/**
|
|
7
|
+
* The wizard's fixed bottom navigation bar, extracted from
|
|
8
|
+
* Wizard.tsx: two equal buttons (Kembali outline, Lanjut primary)
|
|
9
|
+
* in a white bar pinned to the viewport bottom. Named exception to
|
|
10
|
+
* the default button size: a fixed navigation bar is a navigation
|
|
11
|
+
* target, so its buttons are field height (44px) and text-base,
|
|
12
|
+
* like the portal bottom nav. The loading state follows the rule:
|
|
13
|
+
* spinner inside the primary button.
|
|
14
|
+
*/
|
|
15
|
+
export function WizardFooter({ onBack, onContinue, backLabel = "Kembali", continueLabel = "Lanjut", backDisabled = false, continueDisabled = false, continueLoading = false, loadingLabel = "Mengirim…", showArrow = true, className, }) {
|
|
16
|
+
return (_jsx("div", { className: cn("fixed inset-x-0 bottom-0 z-30 border-t border-border bg-card", className), children: _jsxs("div", { className: "mx-auto flex max-w-2xl gap-3 px-4 py-3", children: [_jsxs(Button, { type: "button", variant: "outline", onClick: onBack, disabled: backDisabled, className: "min-h-11 flex-1 text-base font-semibold", children: [_jsx(IconArrowLeft, { size: 18, "aria-hidden": "true" }), backLabel] }), _jsx(Button, { type: "submit", onClick: onContinue, disabled: continueDisabled || continueLoading, "aria-busy": continueLoading, className: "min-h-11 flex-1 text-base font-semibold", children: continueLoading ? (_jsxs(_Fragment, { children: [_jsx(Spinner, { size: "sm", decorative: true }), _jsx("span", { children: loadingLabel })] })) : (_jsxs(_Fragment, { children: [continueLabel, showArrow && _jsx(IconArrowRight, { size: 18, "aria-hidden": "true" })] })) })] }) }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type SavingStatus } from "./saving-indicator";
|
|
2
|
+
export interface WizardLanguage {
|
|
3
|
+
label: string;
|
|
4
|
+
current?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface WizardHeaderProps {
|
|
7
|
+
/** Counter text, for example "Langkah 3 dari 7". */
|
|
8
|
+
stepText: string;
|
|
9
|
+
/** Step title shown after the counter on sm and wider. */
|
|
10
|
+
title: string;
|
|
11
|
+
current: number;
|
|
12
|
+
total: number;
|
|
13
|
+
progressLabel: string;
|
|
14
|
+
savingStatus: SavingStatus;
|
|
15
|
+
locale?: "id" | "en";
|
|
16
|
+
languages: WizardLanguage[];
|
|
17
|
+
languageLabel?: string;
|
|
18
|
+
onLanguageSelect?: (language: WizardLanguage) => void;
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The wizard's sticky sub-header (WIZ106-09), extracted from
|
|
23
|
+
* Wizard.tsx: step counter with the step title, the draft
|
|
24
|
+
* SavingIndicator and the language menu on the right, and the
|
|
25
|
+
* segmented progress below. It sits under the portal header inside
|
|
26
|
+
* the shell content; the consumer supplies the horizontal bleed
|
|
27
|
+
* (for example -mx-4) its layout needs.
|
|
28
|
+
*/
|
|
29
|
+
export declare function WizardHeader({ stepText, title, current, total, progressLabel, savingStatus, locale, languages, languageLabel, onLanguageSelect, className, }: WizardHeaderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { IconCheck, IconLanguage } from "@tabler/icons-react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { Button } from "./button";
|
|
5
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "./dropdown-menu";
|
|
6
|
+
import { SavingIndicator } from "./saving-indicator";
|
|
7
|
+
import { SegmentedProgress } from "./segmented-progress";
|
|
8
|
+
/**
|
|
9
|
+
* The wizard's sticky sub-header (WIZ106-09), extracted from
|
|
10
|
+
* Wizard.tsx: step counter with the step title, the draft
|
|
11
|
+
* SavingIndicator and the language menu on the right, and the
|
|
12
|
+
* segmented progress below. It sits under the portal header inside
|
|
13
|
+
* the shell content; the consumer supplies the horizontal bleed
|
|
14
|
+
* (for example -mx-4) its layout needs.
|
|
15
|
+
*/
|
|
16
|
+
export function WizardHeader({ stepText, title, current, total, progressLabel, savingStatus, locale = "id", languages, languageLabel = "Pilih bahasa", onLanguageSelect, className, }) {
|
|
17
|
+
return (_jsxs("div", { className: cn("sticky top-0 z-40 border-b border-border bg-card px-4 py-3", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("p", { className: "min-w-0 text-sm leading-5 font-medium text-brand-ink", "aria-live": "polite", children: [stepText, _jsxs("span", { className: "hidden sm:inline", children: [" \u2014 ", title] })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [_jsx(SavingIndicator, { status: savingStatus, locale: locale }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": languageLabel, children: _jsx(IconLanguage, {}) }) }), _jsx(DropdownMenuContent, { align: "end", children: languages.map((language) => (_jsxs(DropdownMenuItem, { onSelect: () => onLanguageSelect?.(language), children: [_jsx("span", { className: "flex-1", children: language.label }), language.current && _jsx(IconCheck, { className: "size-4 text-brand-red", "aria-hidden": "true" })] }, language.label))) })] })] })] }), _jsx(SegmentedProgress, { current: current, total: total, label: progressLabel, className: "mt-3" })] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useIsMobile(): boolean;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
const MOBILE_BREAKPOINT = 768;
|
|
3
|
+
const MOBILE_QUERY = `(max-width: ${MOBILE_BREAKPOINT - 1}px)`;
|
|
4
|
+
function subscribeMobile(onChange) {
|
|
5
|
+
const mql = window.matchMedia(MOBILE_QUERY);
|
|
6
|
+
mql.addEventListener("change", onChange);
|
|
7
|
+
return () => mql.removeEventListener("change", onChange);
|
|
8
|
+
}
|
|
9
|
+
export function useIsMobile() {
|
|
10
|
+
return React.useSyncExternalStore(subscribeMobile, () => window.matchMedia(MOBILE_QUERY).matches, () => false);
|
|
11
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export { cn } from "./lib/utils";
|
|
2
|
+
export { Button, buttonVariants } from "./components/button";
|
|
3
|
+
export { Badge, badgeVariants } from "./components/badge";
|
|
4
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, } from "./components/card";
|
|
5
|
+
export { Input } from "./components/input";
|
|
6
|
+
export { Label } from "./components/label";
|
|
7
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./components/select";
|
|
8
|
+
export { Separator } from "./components/separator";
|
|
9
|
+
export { Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup, AvatarGroupCount, } from "./components/avatar";
|
|
10
|
+
export { Spinner } from "./components/spinner";
|
|
11
|
+
export { Skeleton } from "./components/skeleton";
|
|
12
|
+
export { Alert, AlertTitle, AlertDescription } from "./components/alert";
|
|
13
|
+
export { SavingIndicator, type SavingStatus, type SavingIndicatorProps } from "./components/saving-indicator";
|
|
14
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./components/tooltip";
|
|
15
|
+
export { ScrollArea, ScrollBar } from "./components/scroll-area";
|
|
16
|
+
export { Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, } from "./components/drawer";
|
|
17
|
+
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, } from "./components/sheet";
|
|
18
|
+
export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from "./components/dropdown-menu";
|
|
19
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } from "./components/tabs";
|
|
20
|
+
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, } from "./components/table";
|
|
21
|
+
export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar, } from "./components/sidebar";
|
|
22
|
+
export { AdminDrawer, useIsDesktop, type AdminDrawerProps, type Locale } from "./components/admin-drawer";
|
|
23
|
+
export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia } from "./components/empty";
|
|
24
|
+
export { FormPanel } from "./components/form-panel";
|
|
25
|
+
export { MemberCard, MemberCardBack, type MemberCardProps, type MemberCardBackProps, type MemberCardTheme } from "./components/member-card";
|
|
26
|
+
export { PageHeader, type PageHeaderProps } from "./components/page-header";
|
|
27
|
+
export { DataTable, type DataTableProps } from "./components/data-table";
|
|
28
|
+
export { TopBar, type TopBarProps, type TopBarLink } from "./components/top-bar";
|
|
29
|
+
export { ViewToggle, type ViewToggleProps, type ViewToggleOption } from "./components/view-toggle";
|
|
30
|
+
export { BottomBar, type BottomBarProps, type BottomBarItem } from "./components/bottom-bar";
|
|
31
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from "./components/dialog";
|
|
32
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, } from "./components/alert-dialog";
|
|
33
|
+
export { Toaster } from "./components/sonner";
|
|
34
|
+
export { SearchFilter, type SearchFilterProps, type ActiveFilter } from "./components/search-filter";
|
|
35
|
+
export { UnitCard, type UnitCardProps } from "./components/unit-card";
|
|
36
|
+
export { StatCard, type StatCardProps } from "./components/stat-card";
|
|
37
|
+
export { Checkbox } from "./components/checkbox";
|
|
38
|
+
export { RadioGroup, RadioGroupItem } from "./components/radio-group";
|
|
39
|
+
export { Switch } from "./components/switch";
|
|
40
|
+
export { Textarea } from "./components/textarea";
|
|
41
|
+
export { Stepper, type StepperProps, type StepperStep } from "./components/stepper";
|
|
42
|
+
export { FileUpload, type FileUploadProps, type FileUploadStatus } from "./components/file-upload";
|
|
43
|
+
export { Progress } from "./components/progress";
|
|
44
|
+
export { Timeline, type TimelineProps, type TimelineItem } from "./components/timeline";
|
|
45
|
+
export { AccountMenu, type AccountMenuProps, type AccountMenuItem } from "./components/account-menu";
|
|
46
|
+
export { TypeAhead, type TypeAheadProps } from "./components/type-ahead";
|
|
47
|
+
export { TagInput, type TagInputProps } from "./components/tag-input";
|
|
48
|
+
export { PortalShell, type PortalShellProps, type PortalNavItem, type PortalLanguage } from "./components/portal-shell";
|
|
49
|
+
export { StatusCard, type StatusCardProps, type StatusCardTone } from "./components/status-card";
|
|
50
|
+
export { ServiceTiles, type ServiceTilesProps, type ServiceTile, type ServiceTileGroup } from "./components/service-tiles";
|
|
51
|
+
export { MemberCardBlock, hasBarcode, type MemberCardBlockProps, type MemberCardState, type CardThemePreference } from "./components/member-card-block";
|
|
52
|
+
export { PhoneField, PHONE_COUNTRY_CODES, type PhoneFieldProps } from "./components/phone-field";
|
|
53
|
+
export { SegmentedProgress, type SegmentedProgressProps } from "./components/segmented-progress";
|
|
54
|
+
export { WizardHeader, type WizardHeaderProps, type WizardLanguage } from "./components/wizard-header";
|
|
55
|
+
export { PillRadio, pillRadioClass, type PillRadioProps, type PillRadioOption } from "./components/pill-radio";
|
|
56
|
+
export { MonthPicker, type MonthPickerProps } from "./components/month-picker";
|
|
57
|
+
export { PostcodeField, type PostcodeFieldProps, type PostcodeCandidate } from "./components/postcode-field";
|
|
58
|
+
export { FundingPicker, type FundingPickerProps } from "./components/funding-picker";
|
|
59
|
+
export { CardUpload, type CardUploadProps, type CardSide, type CardSideState } from "./components/card-upload";
|
|
60
|
+
export { SuggestionSheet, type SuggestionSheetProps, type SuggestKind } from "./components/suggestion-sheet";
|
|
61
|
+
export { SubmitOverlay, type SubmitOverlayProps } from "./components/submit-overlay";
|
|
62
|
+
export { ReviewPanel, type ReviewPanelProps } from "./components/review-panel";
|
|
63
|
+
export { WizardFooter, type WizardFooterProps } from "./components/wizard-footer";
|
|
64
|
+
export { BannerCarousel, type BannerCarouselProps } from "./components/banner-carousel";
|
|
65
|
+
export { OfflineBanner, type OfflineBannerProps } from "./components/offline-banner";
|
|
66
|
+
export { TodoStrip, type TodoStripProps } from "./components/todo-strip";
|
|
67
|
+
export { InvitationCard, type InvitationCardProps } from "./components/invitation-card";
|
|
68
|
+
export { cardThemes, resolveCardTheme, type CardTheme } from "./card-themes";
|
|
69
|
+
export { activeBanners, jstToday, MAX_ACTIVE, type PromoBanner } from "./banners";
|
|
70
|
+
export { EditablePanel, type EditablePanelProps } from "./components/editable-panel";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export { cn } from "./lib/utils";
|
|
2
|
+
export { Button, buttonVariants } from "./components/button";
|
|
3
|
+
export { Badge, badgeVariants } from "./components/badge";
|
|
4
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, } from "./components/card";
|
|
5
|
+
export { Input } from "./components/input";
|
|
6
|
+
export { Label } from "./components/label";
|
|
7
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./components/select";
|
|
8
|
+
export { Separator } from "./components/separator";
|
|
9
|
+
export { Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup, AvatarGroupCount, } from "./components/avatar";
|
|
10
|
+
export { Spinner } from "./components/spinner";
|
|
11
|
+
export { Skeleton } from "./components/skeleton";
|
|
12
|
+
export { Alert, AlertTitle, AlertDescription } from "./components/alert";
|
|
13
|
+
export { SavingIndicator } from "./components/saving-indicator";
|
|
14
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./components/tooltip";
|
|
15
|
+
export { ScrollArea, ScrollBar } from "./components/scroll-area";
|
|
16
|
+
export { Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, } from "./components/drawer";
|
|
17
|
+
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, } from "./components/sheet";
|
|
18
|
+
export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from "./components/dropdown-menu";
|
|
19
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } from "./components/tabs";
|
|
20
|
+
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, } from "./components/table";
|
|
21
|
+
export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar, } from "./components/sidebar";
|
|
22
|
+
export { AdminDrawer, useIsDesktop } from "./components/admin-drawer";
|
|
23
|
+
export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia } from "./components/empty";
|
|
24
|
+
export { FormPanel } from "./components/form-panel";
|
|
25
|
+
export { MemberCard, MemberCardBack } from "./components/member-card";
|
|
26
|
+
export { PageHeader } from "./components/page-header";
|
|
27
|
+
export { DataTable } from "./components/data-table";
|
|
28
|
+
export { TopBar } from "./components/top-bar";
|
|
29
|
+
export { ViewToggle } from "./components/view-toggle";
|
|
30
|
+
export { BottomBar } from "./components/bottom-bar";
|
|
31
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from "./components/dialog";
|
|
32
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, } from "./components/alert-dialog";
|
|
33
|
+
export { Toaster } from "./components/sonner";
|
|
34
|
+
export { SearchFilter } from "./components/search-filter";
|
|
35
|
+
export { UnitCard } from "./components/unit-card";
|
|
36
|
+
export { StatCard } from "./components/stat-card";
|
|
37
|
+
export { Checkbox } from "./components/checkbox";
|
|
38
|
+
export { RadioGroup, RadioGroupItem } from "./components/radio-group";
|
|
39
|
+
export { Switch } from "./components/switch";
|
|
40
|
+
export { Textarea } from "./components/textarea";
|
|
41
|
+
export { Stepper } from "./components/stepper";
|
|
42
|
+
export { FileUpload } from "./components/file-upload";
|
|
43
|
+
export { Progress } from "./components/progress";
|
|
44
|
+
export { Timeline } from "./components/timeline";
|
|
45
|
+
export { AccountMenu } from "./components/account-menu";
|
|
46
|
+
export { TypeAhead } from "./components/type-ahead";
|
|
47
|
+
export { TagInput } from "./components/tag-input";
|
|
48
|
+
export { PortalShell } from "./components/portal-shell";
|
|
49
|
+
export { StatusCard } from "./components/status-card";
|
|
50
|
+
export { ServiceTiles } from "./components/service-tiles";
|
|
51
|
+
export { MemberCardBlock, hasBarcode } from "./components/member-card-block";
|
|
52
|
+
export { PhoneField, PHONE_COUNTRY_CODES } from "./components/phone-field";
|
|
53
|
+
export { SegmentedProgress } from "./components/segmented-progress";
|
|
54
|
+
export { WizardHeader } from "./components/wizard-header";
|
|
55
|
+
export { PillRadio, pillRadioClass } from "./components/pill-radio";
|
|
56
|
+
export { MonthPicker } from "./components/month-picker";
|
|
57
|
+
export { PostcodeField } from "./components/postcode-field";
|
|
58
|
+
export { FundingPicker } from "./components/funding-picker";
|
|
59
|
+
export { CardUpload } from "./components/card-upload";
|
|
60
|
+
export { SuggestionSheet } from "./components/suggestion-sheet";
|
|
61
|
+
export { SubmitOverlay } from "./components/submit-overlay";
|
|
62
|
+
export { ReviewPanel } from "./components/review-panel";
|
|
63
|
+
export { WizardFooter } from "./components/wizard-footer";
|
|
64
|
+
export { BannerCarousel } from "./components/banner-carousel";
|
|
65
|
+
export { OfflineBanner } from "./components/offline-banner";
|
|
66
|
+
export { TodoStrip } from "./components/todo-strip";
|
|
67
|
+
export { InvitationCard } from "./components/invitation-card";
|
|
68
|
+
export { cardThemes, resolveCardTheme } from "./card-themes";
|
|
69
|
+
export { activeBanners, jstToday, MAX_ACTIVE } from "./banners";
|
|
70
|
+
export { EditablePanel } from "./components/editable-panel";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PromoBanner } from "./banners";
|
|
2
|
+
/**
|
|
3
|
+
* The real banners.json entries from ppij-identity-sso (October
|
|
4
|
+
* 2026) with the real artwork from brand/banners, for stories and
|
|
5
|
+
* prototypes. Dates are JST; expired entries drop out by
|
|
6
|
+
* themselves in BannerCarousel.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SAMPLE_BANNERS: PromoBanner[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
|
|
2
|
+
import b1d from "../../../brand/banners/banner_1.jpg";
|
|
3
|
+
// @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
|
|
4
|
+
import b1m from "../../../brand/banners/banner_1_mobile.jpg";
|
|
5
|
+
// @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
|
|
6
|
+
import b2d from "../../../brand/banners/banner_2.jpg";
|
|
7
|
+
// @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
|
|
8
|
+
import b2m from "../../../brand/banners/banner_2_mobile.jpg";
|
|
9
|
+
// @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
|
|
10
|
+
import b3d from "../../../brand/banners/banner_3.jpg";
|
|
11
|
+
// @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
|
|
12
|
+
import b3m from "../../../brand/banners/banner_3_mobile.jpg";
|
|
13
|
+
// @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
|
|
14
|
+
import b4d from "../../../brand/banners/banner_4.jpg";
|
|
15
|
+
// @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
|
|
16
|
+
import b4m from "../../../brand/banners/banner_4_mobile.jpg";
|
|
17
|
+
/**
|
|
18
|
+
* The real banners.json entries from ppij-identity-sso (October
|
|
19
|
+
* 2026) with the real artwork from brand/banners, for stories and
|
|
20
|
+
* prototypes. Dates are JST; expired entries drop out by
|
|
21
|
+
* themselves in BannerCarousel.
|
|
22
|
+
*/
|
|
23
|
+
export const SAMPLE_BANNERS = [
|
|
24
|
+
{ id: "banner_4", image: { desktop: b4d, mobile: b4m }, alt: { id: "Webinar gratis Communication Survival Skills 2027 bersama John Robert Powers, Sabtu 31 Oktober 2026 pukul 14.00 WIB via Zoom. Daftar sekarang.", en: "Free Communication Survival Skills 2027 webinar with John Robert Powers, Saturday 31 October 2026, 14.00 WIB on Zoom. Register now." }, href: "https://bit.ly/CSSSeminar2026", start: "2026-10-24", end: "2026-11-01", order: 1 },
|
|
25
|
+
{ id: "banner_1", image: { desktop: b1d, mobile: b1m }, alt: { id: "Diskon 10% di Toko Indonesia Tokyo untuk anggota PPI Jepang", en: "10% off at Toko Indonesia Tokyo for PPI Japan members" }, href: "https://ppijepang.org", start: "2026-10-20", end: "2026-12-31", order: 2 },
|
|
26
|
+
{ id: "banner_2", image: { desktop: b2d, mobile: b2m }, alt: { id: "Gratis ongkir kirim paket ke Indonesia untuk anggota aktif", en: "Free shipping to Indonesia for active members" }, href: "https://ppijepang.org", start: "2026-10-20", end: "2026-12-31", order: 3 },
|
|
27
|
+
{ id: "banner_3", image: { desktop: b3d, mobile: b3m }, alt: { id: "Ski & Snowboard bareng PPI Okayama musim dingin ini. Daftar sekarang.", en: "Join the PPI Okayama Winter Ski and Snowboard trip. Register now." }, href: "https://ppijepang.org", start: "2026-10-21", order: 4 },
|
|
28
|
+
];
|
package/dist/tokens.css
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* GENERATED from tokens/tokens.json by scripts/generate-tokens.mjs.
|
|
5
|
+
* Do not edit by hand. PPIJ design tokens: brand primitives in :root,
|
|
6
|
+
* the shadcn semantic layer mapped to them, Tailwind v4 theme names.
|
|
7
|
+
*/
|
|
8
|
+
:root {
|
|
9
|
+
--brand-red: #c8102e;
|
|
10
|
+
--brand-ink: #1c1f23;
|
|
11
|
+
--brand-muted: #5b6168;
|
|
12
|
+
--brand-border: #e3e5e8;
|
|
13
|
+
--brand-bg: #f6f6f4;
|
|
14
|
+
--status-green: #15803d;
|
|
15
|
+
--status-amber: #b45309;
|
|
16
|
+
--status-blue: #1d4ed8;
|
|
17
|
+
|
|
18
|
+
--background: var(--brand-bg);
|
|
19
|
+
--foreground: var(--brand-ink);
|
|
20
|
+
--card: #ffffff;
|
|
21
|
+
--card-foreground: var(--brand-ink);
|
|
22
|
+
--popover: #ffffff;
|
|
23
|
+
--popover-foreground: var(--brand-ink);
|
|
24
|
+
--primary: var(--brand-red);
|
|
25
|
+
--primary-foreground: #ffffff;
|
|
26
|
+
--secondary: #eceef0;
|
|
27
|
+
--secondary-foreground: var(--brand-ink);
|
|
28
|
+
--muted: #eceef0;
|
|
29
|
+
--muted-foreground: var(--brand-muted);
|
|
30
|
+
--accent: #eceef0;
|
|
31
|
+
--accent-foreground: var(--brand-ink);
|
|
32
|
+
--destructive: var(--brand-red);
|
|
33
|
+
--border: var(--brand-border);
|
|
34
|
+
--input: var(--brand-border);
|
|
35
|
+
--ring: var(--brand-red);
|
|
36
|
+
--sidebar: #ffffff;
|
|
37
|
+
--sidebar-foreground: var(--brand-ink);
|
|
38
|
+
--sidebar-primary: var(--brand-red);
|
|
39
|
+
--sidebar-primary-foreground: #ffffff;
|
|
40
|
+
--sidebar-accent: #eceef0;
|
|
41
|
+
--sidebar-accent-foreground: var(--brand-ink);
|
|
42
|
+
--sidebar-border: var(--brand-border);
|
|
43
|
+
--sidebar-ring: var(--brand-red);
|
|
44
|
+
--success: var(--status-green);
|
|
45
|
+
--success-foreground: #ffffff;
|
|
46
|
+
--warning: var(--status-amber);
|
|
47
|
+
--warning-foreground: #ffffff;
|
|
48
|
+
--info: var(--status-blue);
|
|
49
|
+
--info-foreground: #ffffff;
|
|
50
|
+
--radius: 0.625rem;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@theme inline {
|
|
54
|
+
--font-sans: "Plus Jakarta Sans", "Noto Sans JP", system-ui, sans-serif;
|
|
55
|
+
|
|
56
|
+
--color-brand-red: var(--brand-red);
|
|
57
|
+
--color-brand-ink: var(--brand-ink);
|
|
58
|
+
--color-brand-muted: var(--brand-muted);
|
|
59
|
+
--color-brand-border: var(--brand-border);
|
|
60
|
+
--color-brand-bg: var(--brand-bg);
|
|
61
|
+
--color-status-green: var(--status-green);
|
|
62
|
+
--color-status-amber: var(--status-amber);
|
|
63
|
+
--color-status-blue: var(--status-blue);
|
|
64
|
+
--color-background: var(--background);
|
|
65
|
+
--color-foreground: var(--foreground);
|
|
66
|
+
--color-card: var(--card);
|
|
67
|
+
--color-card-foreground: var(--card-foreground);
|
|
68
|
+
--color-popover: var(--popover);
|
|
69
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
70
|
+
--color-primary: var(--primary);
|
|
71
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
72
|
+
--color-secondary: var(--secondary);
|
|
73
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
74
|
+
--color-muted: var(--muted);
|
|
75
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
76
|
+
--color-accent: var(--accent);
|
|
77
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
78
|
+
--color-destructive: var(--destructive);
|
|
79
|
+
--color-border: var(--border);
|
|
80
|
+
--color-input: var(--input);
|
|
81
|
+
--color-ring: var(--ring);
|
|
82
|
+
--color-sidebar: var(--sidebar);
|
|
83
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
84
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
85
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
86
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
87
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
88
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
89
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
90
|
+
--color-success: var(--success);
|
|
91
|
+
--color-success-foreground: var(--success-foreground);
|
|
92
|
+
--color-warning: var(--warning);
|
|
93
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
94
|
+
--color-info: var(--info);
|
|
95
|
+
--color-info-foreground: var(--info-foreground);
|
|
96
|
+
|
|
97
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
98
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
99
|
+
--radius-lg: var(--radius);
|
|
100
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@layer base {
|
|
104
|
+
* {
|
|
105
|
+
border-color: var(--border);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
html {
|
|
109
|
+
-webkit-text-size-adjust: 100%;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
body {
|
|
113
|
+
margin: 0;
|
|
114
|
+
background-color: var(--brand-bg);
|
|
115
|
+
color: var(--brand-ink);
|
|
116
|
+
font-family: var(--font-sans);
|
|
117
|
+
line-height: 1.6;
|
|
118
|
+
}
|
|
119
|
+
}
|
package/dist/tokens.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"primitive": {
|
|
3
|
+
"brand-red": "#c8102e",
|
|
4
|
+
"brand-ink": "#1c1f23",
|
|
5
|
+
"brand-muted": "#5b6168",
|
|
6
|
+
"brand-border": "#e3e5e8",
|
|
7
|
+
"brand-bg": "#f6f6f4",
|
|
8
|
+
"white": "#ffffff",
|
|
9
|
+
"secondary-surface": "#eceef0",
|
|
10
|
+
"radius": "0.625rem",
|
|
11
|
+
"font-sans": "\"Plus Jakarta Sans\", \"Noto Sans JP\", system-ui, sans-serif",
|
|
12
|
+
"status-green": "#15803d",
|
|
13
|
+
"status-amber": "#b45309",
|
|
14
|
+
"status-blue": "#1d4ed8"
|
|
15
|
+
},
|
|
16
|
+
"semantic": {
|
|
17
|
+
"background": "var(--brand-bg)",
|
|
18
|
+
"foreground": "var(--brand-ink)",
|
|
19
|
+
"card": "#ffffff",
|
|
20
|
+
"card-foreground": "var(--brand-ink)",
|
|
21
|
+
"popover": "#ffffff",
|
|
22
|
+
"popover-foreground": "var(--brand-ink)",
|
|
23
|
+
"primary": "var(--brand-red)",
|
|
24
|
+
"primary-foreground": "#ffffff",
|
|
25
|
+
"secondary": "#eceef0",
|
|
26
|
+
"secondary-foreground": "var(--brand-ink)",
|
|
27
|
+
"muted": "#eceef0",
|
|
28
|
+
"muted-foreground": "var(--brand-muted)",
|
|
29
|
+
"accent": "#eceef0",
|
|
30
|
+
"accent-foreground": "var(--brand-ink)",
|
|
31
|
+
"destructive": "var(--brand-red)",
|
|
32
|
+
"border": "var(--brand-border)",
|
|
33
|
+
"input": "var(--brand-border)",
|
|
34
|
+
"ring": "var(--brand-red)",
|
|
35
|
+
"sidebar": "#ffffff",
|
|
36
|
+
"sidebar-foreground": "var(--brand-ink)",
|
|
37
|
+
"sidebar-primary": "var(--brand-red)",
|
|
38
|
+
"sidebar-primary-foreground": "#ffffff",
|
|
39
|
+
"sidebar-accent": "#eceef0",
|
|
40
|
+
"sidebar-accent-foreground": "var(--brand-ink)",
|
|
41
|
+
"sidebar-border": "var(--brand-border)",
|
|
42
|
+
"sidebar-ring": "var(--brand-red)",
|
|
43
|
+
"success": "var(--status-green)",
|
|
44
|
+
"success-foreground": "#ffffff",
|
|
45
|
+
"warning": "var(--status-amber)",
|
|
46
|
+
"warning-foreground": "#ffffff",
|
|
47
|
+
"info": "var(--status-blue)",
|
|
48
|
+
"info-foreground": "#ffffff"
|
|
49
|
+
}
|
|
50
|
+
}
|