parent
f1dfc893b4
commit
9f60f035c6
@ -1,17 +1,24 @@
|
||||
<script lang="ts">
|
||||
import type { TerraConfigProps } from '../types.ts';
|
||||
import { useTerraConfig } from '../terra-config.ts';
|
||||
import { readableActive } from '../../../utils';
|
||||
|
||||
let { children, defaultPortalTo, defaultLocale, defaultDateOrder, defaultTimeFormat }: TerraConfigProps = $props();
|
||||
|
||||
useTerraConfig({
|
||||
defaultPortalTo: readableActive(() => defaultPortalTo),
|
||||
defaultLocale: readableActive(() => defaultLocale),
|
||||
defaultDateOrder: readableActive(() => defaultDateOrder),
|
||||
defaultTimeFormat: readableActive(() => defaultTimeFormat),
|
||||
});
|
||||
</script>
|
||||
|
||||
{@render children?.()}
|
||||
|
||||
<script lang="ts">
|
||||
import type { TerraConfigProps } from '../types.ts';
|
||||
import { useTerraConfig } from '../terra-config.ts';
|
||||
import { readableActive } from '../../../utils';
|
||||
|
||||
let {
|
||||
children,
|
||||
defaultPortalTo,
|
||||
defaultLocale,
|
||||
defaultDir,
|
||||
defaultDateOrder,
|
||||
defaultTimeFormat,
|
||||
}: TerraConfigProps = $props();
|
||||
|
||||
useTerraConfig({
|
||||
defaultPortalTo: readableActive(() => defaultPortalTo),
|
||||
defaultLocale: readableActive(() => defaultLocale),
|
||||
defaultDir: readableActive(() => defaultDir),
|
||||
defaultDateOrder: readableActive(() => defaultDateOrder),
|
||||
defaultTimeFormat: readableActive(() => defaultTimeFormat),
|
||||
});
|
||||
</script>
|
||||
|
||||
{@render children?.()}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
export { default as TerraConfig } from './components/terra-config.svelte';
|
||||
export { getTerraConfig, TerraConfigState } from './terra-config.js';
|
||||
export type {
|
||||
TerraConfigProps,
|
||||
TerraConfigPropsWithoutChildren,
|
||||
TerraDatePresentationProps,
|
||||
TerraDateTimePresentationProps,
|
||||
} from './types.ts';
|
||||
export { default as TerraConfig } from './components/terra-config.svelte';
|
||||
export { getTerraConfig, TerraConfigState } from './terra-config.js';
|
||||
export type {
|
||||
TerraConfigProps,
|
||||
TerraConfigPropsWithoutChildren,
|
||||
TerraPresentationProps,
|
||||
TerraDatePresentationProps,
|
||||
TerraDateTimePresentationProps,
|
||||
} from './types.ts';
|
||||
|
||||
@ -1,72 +1,86 @@
|
||||
import { readableActive, type Active, type Getter } from '../../utils';
|
||||
import { DEFAULT_DATE_ORDER, DEFAULT_TIME_FORMAT, timeFormatToHourCycle, type HourCycle } from '$terra/adapters/dates';
|
||||
import { type TerraConfigState, getTerraConfig } from './terra-config.ts';
|
||||
|
||||
/**
|
||||
* Creates a generic prop resolver that follows a standard priority chain:
|
||||
* 1. The getter's prop value (if defined)
|
||||
* 2. The config default value (if no getter prop value is defined)
|
||||
* 3. The fallback value (if no config value found)
|
||||
*/
|
||||
function createPropResolver<T>(
|
||||
configOption: (config: TerraConfigState['opts']) => { current: T | undefined },
|
||||
fallback: T
|
||||
) {
|
||||
return (getProp: Getter<T | undefined>): Active<T> => {
|
||||
const config = getTerraConfig();
|
||||
return readableActive(() => {
|
||||
// 1. return the prop's value, if provided
|
||||
const propValue = getProp();
|
||||
if (propValue !== undefined) return propValue;
|
||||
// 2. return the resolved config option value, if available
|
||||
const option = configOption(config).current;
|
||||
if (option !== undefined) return option;
|
||||
// 3. return the fallback if no other value is found
|
||||
return fallback;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a locale value using the prop, the config default, or a fallback.
|
||||
*
|
||||
* Default value: `"en"`
|
||||
*/
|
||||
export const resolveLocaleProp = createPropResolver((config) => config.defaultLocale, "en");
|
||||
|
||||
/**
|
||||
* Resolves a portal's `to` value using the prop, the config default, or a fallback.
|
||||
*
|
||||
* Default value: `"body"`
|
||||
*/
|
||||
export const resolvePortalToProp = createPropResolver((config) => config.defaultPortalTo, "body");
|
||||
|
||||
|
||||
/**
|
||||
* Resolves a date order using the prop, the config default, or the library fallback.
|
||||
*/
|
||||
export const resolveDateOrderProp = createPropResolver((config) => config.defaultDateOrder, DEFAULT_DATE_ORDER);
|
||||
|
||||
/**
|
||||
* Resolves an hour cycle using explicit time/date props, config defaults, or the library fallback.
|
||||
*/
|
||||
export function resolveHourCycleProp(
|
||||
getTimeFormat: Getter<HourCycle | undefined>,
|
||||
getHourCycle: Getter<HourCycle | undefined>
|
||||
): Active<HourCycle> {
|
||||
const config = getTerraConfig();
|
||||
return readableActive(() => {
|
||||
const timeFormat = getTimeFormat();
|
||||
if (timeFormat !== undefined) return timeFormatToHourCycle(timeFormat);
|
||||
|
||||
const hourCycle = getHourCycle();
|
||||
if (hourCycle !== undefined) return hourCycle;
|
||||
|
||||
const configTimeFormat = config.defaultTimeFormat.current;
|
||||
if (configTimeFormat !== undefined) return timeFormatToHourCycle(configTimeFormat);
|
||||
|
||||
return timeFormatToHourCycle(DEFAULT_TIME_FORMAT);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
import { readableActive, type Active, type Direction, type Getter } from '$terra/utils';
|
||||
import {
|
||||
DEFAULT_DATE_ORDER,
|
||||
DEFAULT_TIME_FORMAT,
|
||||
timeFormatToHourCycle,
|
||||
type HourCycle,
|
||||
} from '$terra/adapters/dates';
|
||||
import { type TerraConfigState, getTerraConfig } from './terra-config.ts';
|
||||
|
||||
/**
|
||||
* Creates a generic prop resolver that follows a standard priority chain:
|
||||
* 1. The getter's prop value (if defined)
|
||||
* 2. The config default value (if no getter prop value is defined)
|
||||
* 3. The fallback value (if no config value found)
|
||||
*/
|
||||
function createPropResolver<T>(
|
||||
configOption: (config: TerraConfigState['opts']) => { current: T | undefined },
|
||||
fallback: T
|
||||
) {
|
||||
return (getProp: Getter<T | undefined>): Active<T> => {
|
||||
const config = getTerraConfig();
|
||||
return readableActive(() => {
|
||||
const propValue = getProp();
|
||||
if (propValue !== undefined) return propValue;
|
||||
|
||||
const option = configOption(config).current;
|
||||
if (option !== undefined) return option;
|
||||
|
||||
return fallback;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a locale value using the prop, the config default, or a fallback.
|
||||
*
|
||||
* Default value: `"en"`
|
||||
*/
|
||||
export const resolveLocaleProp = createPropResolver((config) => config.defaultLocale, 'en');
|
||||
|
||||
/**
|
||||
* Resolves a direction value using the prop, the config default, or a fallback.
|
||||
*
|
||||
* Default value: `"ltr"`
|
||||
*/
|
||||
export const resolveDirectionProp = createPropResolver(
|
||||
(config) => config.defaultDir,
|
||||
'ltr' as Direction
|
||||
);
|
||||
|
||||
/**
|
||||
* Resolves a portal's `to` value using the prop, the config default, or a fallback.
|
||||
*
|
||||
* Default value: `"body"`
|
||||
*/
|
||||
export const resolvePortalToProp = createPropResolver((config) => config.defaultPortalTo, 'body');
|
||||
|
||||
/**
|
||||
* Resolves a date order using the prop, the config default, or the library fallback.
|
||||
*/
|
||||
export const resolveDateOrderProp = createPropResolver(
|
||||
(config) => config.defaultDateOrder,
|
||||
DEFAULT_DATE_ORDER
|
||||
);
|
||||
|
||||
/**
|
||||
* Resolves an hour cycle using explicit time/date props, config defaults, or the library fallback.
|
||||
*/
|
||||
export function resolveHourCycleProp(
|
||||
getTimeFormat: Getter<HourCycle | undefined>,
|
||||
getHourCycle: Getter<HourCycle | undefined>
|
||||
): Active<HourCycle> {
|
||||
const config = getTerraConfig();
|
||||
return readableActive(() => {
|
||||
const timeFormat = getTimeFormat();
|
||||
if (timeFormat !== undefined) return timeFormatToHourCycle(timeFormat);
|
||||
|
||||
const hourCycle = getHourCycle();
|
||||
if (hourCycle !== undefined) return hourCycle;
|
||||
|
||||
const configTimeFormat = config.defaultTimeFormat.current;
|
||||
if (configTimeFormat !== undefined) return timeFormatToHourCycle(configTimeFormat);
|
||||
|
||||
return timeFormatToHourCycle(DEFAULT_TIME_FORMAT);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,49 +1,49 @@
|
||||
import { Context } from 'runed';
|
||||
import { readableActive, type Active, type ActiveProps } from '../../utils';
|
||||
import type { TerraConfigPropsWithoutChildren } from './types';
|
||||
|
||||
type TerraConfigStateProps = ActiveProps<TerraConfigPropsWithoutChildren>;
|
||||
|
||||
export const TerraConfigContext = new Context<TerraConfigState>('TerraConfig');
|
||||
|
||||
export function getTerraConfig() {
|
||||
const fallback = new TerraConfigState(null, {});
|
||||
return TerraConfigContext.getOr(fallback).opts;
|
||||
}
|
||||
|
||||
export function useTerraConfig(opts: TerraConfigStateProps) {
|
||||
return TerraConfigContext.set(new TerraConfigState(TerraConfigContext.getOr(null), opts));
|
||||
}
|
||||
|
||||
export class TerraConfigState {
|
||||
readonly opts: Required<TerraConfigStateProps>;
|
||||
|
||||
constructor(parent: TerraConfigState | null, opts: TerraConfigStateProps) {
|
||||
const resolveConfigOption = createConfigResolver(parent, opts);
|
||||
this.opts = {
|
||||
defaultPortalTo: resolveConfigOption((config) => config.defaultPortalTo),
|
||||
defaultLocale: resolveConfigOption((config) => config.defaultLocale),
|
||||
defaultDateOrder: resolveConfigOption((config) => config.defaultDateOrder),
|
||||
defaultTimeFormat: resolveConfigOption((config) => config.defaultTimeFormat),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigOptionGetter<T> = (config: TerraConfigStateProps) => Active<T> | undefined;
|
||||
type ConfigOptionResolver = <T>(getter: ConfigOptionGetter<T>) => Active<T | undefined>;
|
||||
|
||||
function createConfigResolver(
|
||||
parent: TerraConfigState | null,
|
||||
currentOpts: TerraConfigStateProps
|
||||
): ConfigOptionResolver {
|
||||
return <T>(getter: ConfigOptionGetter<T>) => {
|
||||
const configOption = readableActive(() => {
|
||||
const value = getter(currentOpts)?.current;
|
||||
if (value !== undefined) return value;
|
||||
if (parent === null) return undefined;
|
||||
return getter(parent.opts)?.current;
|
||||
});
|
||||
return configOption;
|
||||
};
|
||||
}
|
||||
|
||||
import { Context } from 'runed';
|
||||
import { readableActive, type Active, type ActiveProps } from '$terra/utils';
|
||||
import type { TerraConfigPropsWithoutChildren } from './types';
|
||||
|
||||
type TerraConfigStateProps = ActiveProps<TerraConfigPropsWithoutChildren>;
|
||||
|
||||
export const TerraConfigContext = new Context<TerraConfigState>('TerraConfig');
|
||||
|
||||
export function getTerraConfig() {
|
||||
const fallback = new TerraConfigState(null, {});
|
||||
return TerraConfigContext.getOr(fallback).opts;
|
||||
}
|
||||
|
||||
export function useTerraConfig(opts: TerraConfigStateProps) {
|
||||
return TerraConfigContext.set(new TerraConfigState(TerraConfigContext.getOr(null), opts));
|
||||
}
|
||||
|
||||
export class TerraConfigState {
|
||||
readonly opts: Required<TerraConfigStateProps>;
|
||||
|
||||
constructor(parent: TerraConfigState | null, opts: TerraConfigStateProps) {
|
||||
const resolveConfigOption = createConfigResolver(parent, opts);
|
||||
this.opts = {
|
||||
defaultPortalTo: resolveConfigOption((config) => config.defaultPortalTo),
|
||||
defaultLocale: resolveConfigOption((config) => config.defaultLocale),
|
||||
defaultDir: resolveConfigOption((config) => config.defaultDir),
|
||||
defaultDateOrder: resolveConfigOption((config) => config.defaultDateOrder),
|
||||
defaultTimeFormat: resolveConfigOption((config) => config.defaultTimeFormat),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigOptionGetter<T> = (config: TerraConfigStateProps) => Active<T> | undefined;
|
||||
type ConfigOptionResolver = <T>(getter: ConfigOptionGetter<T>) => Active<T | undefined>;
|
||||
|
||||
function createConfigResolver(
|
||||
parent: TerraConfigState | null,
|
||||
currentOpts: TerraConfigStateProps
|
||||
): ConfigOptionResolver {
|
||||
return <T>(getter: ConfigOptionGetter<T>) => {
|
||||
const configOption = readableActive(() => {
|
||||
const value = getter(currentOpts)?.current;
|
||||
if (value !== undefined) return value;
|
||||
if (parent === null) return undefined;
|
||||
return getter(parent.opts)?.current;
|
||||
});
|
||||
return configOption;
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,61 +1,76 @@
|
||||
import type { WithChildren } from '../../utils';
|
||||
import type { DateOrder, HourCycle } from '$terra/adapters/dates';
|
||||
import type { PortalTarget } from '../portal/types.ts';
|
||||
|
||||
export type TerraConfigPropsWithoutChildren = {
|
||||
/**
|
||||
* The default portal `to`/target to use for the `Portal` components throughout the app.
|
||||
*/
|
||||
defaultPortalTo?: PortalTarget;
|
||||
|
||||
/**
|
||||
* The default locale to use for the components that support localization.
|
||||
*/
|
||||
defaultLocale?: string;
|
||||
|
||||
/**
|
||||
* The default date part order to use for date-aware components.
|
||||
*/
|
||||
defaultDateOrder?: DateOrder;
|
||||
|
||||
/**
|
||||
* The default time format to use for time-aware components.
|
||||
*/
|
||||
defaultTimeFormat?: HourCycle;
|
||||
};
|
||||
|
||||
export type TerraConfigProps = WithChildren<TerraConfigPropsWithoutChildren>;
|
||||
|
||||
export type TerraDatePresentationProps = {
|
||||
/**
|
||||
* The locale to use for formatting date-aware components.
|
||||
*
|
||||
* @defaultValue 'en'
|
||||
*/
|
||||
locale?: string;
|
||||
|
||||
/**
|
||||
* Preferred order for date segments in date-aware components.
|
||||
*
|
||||
* @defaultValue 'DMY'
|
||||
*/
|
||||
dateOrder?: DateOrder;
|
||||
};
|
||||
|
||||
export type TerraDateTimePresentationProps = TerraDatePresentationProps & {
|
||||
/**
|
||||
* The format to use for displaying time segments.
|
||||
*
|
||||
* @defaultValue the locale's default time format
|
||||
*/
|
||||
hourCycle?: HourCycle;
|
||||
|
||||
/**
|
||||
* Preferred time format for time-aware components.
|
||||
* When provided, it overrides `hourCycle`.
|
||||
*
|
||||
* @defaultValue '24h'
|
||||
*/
|
||||
timeFormat?: HourCycle;
|
||||
};
|
||||
|
||||
import type { Direction, WithChildren } from '$terra/utils';
|
||||
import type { DateOrder, HourCycle } from '$terra/adapters/dates';
|
||||
import type { PortalTarget } from '../portal/types.ts';
|
||||
|
||||
export type TerraConfigPropsWithoutChildren = {
|
||||
/**
|
||||
* The default portal `to`/target to use for the `Portal` components throughout the app.
|
||||
*/
|
||||
defaultPortalTo?: PortalTarget;
|
||||
|
||||
/**
|
||||
* The default locale to use for the components that support localization.
|
||||
*/
|
||||
defaultLocale?: string;
|
||||
|
||||
/**
|
||||
* The default direction to use for direction-aware components.
|
||||
*
|
||||
* @defaultValue 'ltr'
|
||||
*/
|
||||
defaultDir?: Direction;
|
||||
|
||||
/**
|
||||
* The default date part order to use for date-aware components.
|
||||
*/
|
||||
defaultDateOrder?: DateOrder;
|
||||
|
||||
/**
|
||||
* The default time format to use for time-aware components.
|
||||
*/
|
||||
defaultTimeFormat?: HourCycle;
|
||||
};
|
||||
|
||||
export type TerraConfigProps = WithChildren<TerraConfigPropsWithoutChildren>;
|
||||
|
||||
export type TerraPresentationProps = {
|
||||
/**
|
||||
* The locale to use for formatting localized components.
|
||||
*
|
||||
* @defaultValue 'en'
|
||||
*/
|
||||
locale?: string;
|
||||
|
||||
/**
|
||||
* The direction to use for direction-aware components.
|
||||
*
|
||||
* @defaultValue 'ltr'
|
||||
*/
|
||||
dir?: Direction;
|
||||
};
|
||||
|
||||
export type TerraDatePresentationProps = TerraPresentationProps & {
|
||||
/**
|
||||
* Preferred order for date segments in date-aware components.
|
||||
*
|
||||
* @defaultValue 'DMY'
|
||||
*/
|
||||
dateOrder?: DateOrder;
|
||||
};
|
||||
|
||||
export type TerraDateTimePresentationProps = TerraDatePresentationProps & {
|
||||
/**
|
||||
* The format to use for displaying time segments.
|
||||
*
|
||||
* @defaultValue the locale's default time format
|
||||
*/
|
||||
hourCycle?: HourCycle;
|
||||
|
||||
/**
|
||||
* Preferred time format for time-aware components.
|
||||
* When provided, it overrides `hourCycle`.
|
||||
*
|
||||
* @defaultValue '24h'
|
||||
*/
|
||||
timeFormat?: HourCycle;
|
||||
};
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
import { readableActive, type Active, type Direction } from '$terra/utils';
|
||||
import { getTerraConfig } from '../config/terra-config.ts';
|
||||
|
||||
export function getDirection(): Active<Direction> {
|
||||
const config = getTerraConfig();
|
||||
return readableActive(() => config.defaultDir.current ?? 'ltr');
|
||||
}
|
||||
|
||||
export function useDirection(dir?: Active<Direction | undefined>): Active<Direction> {
|
||||
const defaultDirection = getDirection();
|
||||
return readableActive(() => dir?.current ?? defaultDirection.current);
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
export { getDirection, useDirection } from './direction.svelte.ts';
|
||||
@ -0,0 +1 @@
|
||||
export * from './exports.ts';
|
||||
@ -1 +1 @@
|
||||
export { default as Mounted } from "./mounted.svelte";
|
||||
export { default as Mounted } from './mounted.svelte';
|
||||
|
||||
Loading…
Reference in new issue