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