@studiolxd/brand 49.11.0 → 49.12.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/CHANGELOG.md +118 -0
- package/dist/_shared/tokens.js +160 -0
- package/dist/_types/messages/BrandMessages.d.ts +13 -1
- package/dist/_types/messages/index.d.ts +1 -1
- package/dist/_types/molecules/ClockWidget/ClockWidget.d.ts +116 -0
- package/dist/_types/molecules/ClockWidget/clockDuration.d.ts +23 -0
- package/dist/_types/molecules/Heatmap/Heatmap.d.ts +101 -0
- package/dist/_types/molecules/Heatmap/heatmapScale.d.ts +39 -0
- package/dist/_types/molecules/RecurrenceField/RecurrenceField.d.ts +93 -0
- package/dist/_types/molecules/RecurrenceField/recurrenceRule.d.ts +62 -0
- package/dist/_types/molecules/Timeline/Timeline.d.ts +77 -0
- package/dist/_types/organisms/OrgChart/OrgChart.d.ts +104 -0
- package/dist/_types/organisms/PlanningGrid/PlanningGrid.d.ts +120 -0
- package/dist/_types/organisms/PlanningGrid/planningHours.d.ts +10 -0
- package/dist/brand.css +396 -1
- package/dist/clock-widget.css +1 -0
- package/dist/clock-widget.js +126 -0
- package/dist/heatmap.css +1 -0
- package/dist/heatmap.js +104 -0
- package/dist/org-chart.css +1 -0
- package/dist/org-chart.js +122 -0
- package/dist/planning-grid.css +1 -0
- package/dist/planning-grid.js +167 -0
- package/dist/recurrence-field.css +1 -0
- package/dist/recurrence-field.js +185 -0
- package/dist/timeline.css +1 -0
- package/dist/timeline.js +57 -0
- package/dist/tokens.css +414 -1
- package/package.json +31 -1
- package/src/tokens/index.css +6 -0
- package/src/tokens/molecules/clock-widget.css +36 -0
- package/src/tokens/molecules/clock-widget.ts +7 -0
- package/src/tokens/molecules/heatmap.css +78 -0
- package/src/tokens/molecules/heatmap.ts +7 -0
- package/src/tokens/molecules/org-chart.css +59 -0
- package/src/tokens/molecules/org-chart.ts +7 -0
- package/src/tokens/molecules/planning-grid.css +58 -0
- package/src/tokens/molecules/planning-grid.ts +7 -0
- package/src/tokens/molecules/recurrence-field.css +21 -0
- package/src/tokens/molecules/recurrence-field.ts +7 -0
- package/src/tokens/molecules/timeline.css +36 -0
- package/src/tokens/molecules/timeline.ts +7 -0
- package/src/tokens/scss/_index.legacy.scss +6 -0
- package/src/tokens/scss/_index.scss +6 -0
- package/src/tokens/scss/molecules/_clock-widget.scss +22 -0
- package/src/tokens/scss/molecules/_heatmap.scss +48 -0
- package/src/tokens/scss/molecules/_org-chart.scss +39 -0
- package/src/tokens/scss/molecules/_planning-grid.scss +36 -0
- package/src/tokens/scss/molecules/_recurrence-field.scss +11 -0
- package/src/tokens/scss/molecules/_timeline.scss +22 -0
- package/src/tokens/surface-invert.css +56 -0
- package/src/tokens/surface-light.css +56 -0
- package/src/tokens/surface-public.css +7 -0
- package/src/tokens/tokens.json +160 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type RecurrenceFrequency, type RecurrenceValue } from './recurrenceRule';
|
|
3
|
+
import './RecurrenceField.css';
|
|
4
|
+
export type { RecurrenceValue, RecurrenceFrequency, RecurrenceWeekday, RecurrenceEnd, } from './recurrenceRule';
|
|
5
|
+
/**
|
|
6
|
+
* Los textos del editor de recurrencia. Todo es **cromo**: las etiquetas de
|
|
7
|
+
* los cuatro campos y los nombres de las opciones.
|
|
8
|
+
*
|
|
9
|
+
* Los **días de la semana no están aquí**: salen del `locale` con `Intl`, como
|
|
10
|
+
* manda el sistema para meses, días y formatos de fecha.
|
|
11
|
+
*/
|
|
12
|
+
export interface RecurrenceFieldMessages {
|
|
13
|
+
/** Título del grupo de campos. */
|
|
14
|
+
legend: string;
|
|
15
|
+
/** Etiqueta del campo de frecuencia. */
|
|
16
|
+
frequency: string;
|
|
17
|
+
/** La opción de no repetir. */
|
|
18
|
+
never: string;
|
|
19
|
+
/** Cada día. */
|
|
20
|
+
daily: string;
|
|
21
|
+
/** Cada semana. */
|
|
22
|
+
weekly: string;
|
|
23
|
+
/** Cada mes. */
|
|
24
|
+
monthly: string;
|
|
25
|
+
/** Cada año. */
|
|
26
|
+
yearly: string;
|
|
27
|
+
/** Etiqueta del intervalo, que depende de la frecuencia: «Cada cuántas semanas». */
|
|
28
|
+
interval: (frequency: RecurrenceFrequency) => string;
|
|
29
|
+
/** Etiqueta del grupo de días de la semana. */
|
|
30
|
+
weekdays: string;
|
|
31
|
+
/** Etiqueta del campo de final. */
|
|
32
|
+
end: string;
|
|
33
|
+
/** El final que no llega. */
|
|
34
|
+
endNever: string;
|
|
35
|
+
/** El final por fecha. */
|
|
36
|
+
endUntil: string;
|
|
37
|
+
/** El final por número de veces. */
|
|
38
|
+
endCount: string;
|
|
39
|
+
/** Etiqueta del campo de fecha final. */
|
|
40
|
+
until: string;
|
|
41
|
+
/** Etiqueta del campo de número de repeticiones. */
|
|
42
|
+
count: string;
|
|
43
|
+
}
|
|
44
|
+
export interface RecurrenceFieldProps {
|
|
45
|
+
/**
|
|
46
|
+
* La repetición, **controlada**. `null` es «no se repite», que no es lo
|
|
47
|
+
* mismo que una repetición con los campos a cero.
|
|
48
|
+
*/
|
|
49
|
+
value: RecurrenceValue | null;
|
|
50
|
+
/** Se llama con la repetición entera, o con `null` al dejar de repetirse. */
|
|
51
|
+
onValueChange: (value: RecurrenceValue | null) => void;
|
|
52
|
+
/** `id` base de los campos. Sin él, se genera con `useId`. */
|
|
53
|
+
id?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Título del grupo. Con él, el editor se envuelve en un `Fieldset`; **sin
|
|
56
|
+
* él, no** —el editor va suelto dentro del formulario que lo monta—. Para el
|
|
57
|
+
* texto del catálogo, pásale `recurrenceField.legend`.
|
|
58
|
+
*/
|
|
59
|
+
legend?: ReactNode;
|
|
60
|
+
/** Deshabilita el editor entero. */
|
|
61
|
+
disabled?: boolean;
|
|
62
|
+
/** Talla de los campos. Sin ella, la del contexto de formulario. */
|
|
63
|
+
size?: 'sm' | 'md' | 'lg';
|
|
64
|
+
/** Locale de los nombres de los días y del campo de fecha. Default `'es-ES'`. */
|
|
65
|
+
locale?: string;
|
|
66
|
+
/** Por dónde empieza la semana en los días. Default `'monday'`. */
|
|
67
|
+
weekStartsOn?: 'monday' | 'sunday';
|
|
68
|
+
/** Tope inferior de la fecha de final. */
|
|
69
|
+
minDate?: Date;
|
|
70
|
+
/** Tope superior de la fecha de final. */
|
|
71
|
+
maxDate?: Date;
|
|
72
|
+
/** Se añade DESPUÉS de las clases propias. */
|
|
73
|
+
className?: string;
|
|
74
|
+
/** Etiqueta del campo de frecuencia. Sin ella, sale de `recurrenceField.frequency`. */
|
|
75
|
+
frequencyLabel?: string;
|
|
76
|
+
/** Etiqueta del grupo de días. Sin ella, sale de `recurrenceField.weekdays`. */
|
|
77
|
+
weekdaysLabel?: string;
|
|
78
|
+
/** Etiqueta del campo de final. Sin ella, sale de `recurrenceField.end`. */
|
|
79
|
+
endLabel?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* El editor de recurrencia de un evento: cada cuánto se repite, qué días y
|
|
83
|
+
* hasta cuándo. Produce y lee una regla `RRULE` con
|
|
84
|
+
* `buildRecurrenceRule` / `parseRecurrenceRule`.
|
|
85
|
+
*
|
|
86
|
+
* **Enseña solo lo que hace falta.** Sin frecuencia no hay nada más que
|
|
87
|
+
* decidir, y los días de la semana solo aparecen en la frecuencia semanal: un
|
|
88
|
+
* formulario que enseña siempre los siete campos obliga a leerlos siempre.
|
|
89
|
+
*
|
|
90
|
+
* Es **controlado**, como el resto de campos del sistema: recibe `value` y
|
|
91
|
+
* llama a `onValueChange` con el valor entero. `null` es «no se repite».
|
|
92
|
+
*/
|
|
93
|
+
export declare function RecurrenceField({ value, onValueChange, id: idProp, legend, disabled, size, locale, weekStartsOn, minDate, maxDate, className, frequencyLabel, weekdaysLabel, endLabel, }: RecurrenceFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El subconjunto de `RRULE` (RFC 5545) que el editor expone, en las dos
|
|
3
|
+
* direcciones: de la forma que edita una persona a la cadena que guarda el
|
|
4
|
+
* servidor, y al revés.
|
|
5
|
+
*
|
|
6
|
+
* **Es un trozo de gramática, no una librería de calendario.** No expande
|
|
7
|
+
* ocurrencias, no sabe de husos y no valida contra un calendario: eso lo hace
|
|
8
|
+
* quien guarda la regla, del lado del servidor, con la librería que use. Aquí
|
|
9
|
+
* solo se escribe y se lee la cadena, que es lo que un campo de formulario
|
|
10
|
+
* necesita — y por eso el sistema de diseño no se trae ninguna dependencia
|
|
11
|
+
* para esto.
|
|
12
|
+
*
|
|
13
|
+
* Lo que se cubre: `FREQ`, `INTERVAL`, `BYDAY` (en la frecuencia semanal) y el
|
|
14
|
+
* final, que puede ser ninguno, `UNTIL` o `COUNT`. Lo que no se cubre
|
|
15
|
+
* —`BYMONTHDAY`, `BYSETPOS`, `WKST`, excepciones— se conserva al leer y se
|
|
16
|
+
* vuelve a escribir tal cual, para que editar la repetición de un evento no
|
|
17
|
+
* destruya lo que el editor no sabe enseñar.
|
|
18
|
+
*/
|
|
19
|
+
/** Cada cuánto se repite. */
|
|
20
|
+
export type RecurrenceFrequency = 'daily' | 'weekly' | 'monthly' | 'yearly';
|
|
21
|
+
/** Un día de la semana, con el código de dos letras de la especificación. */
|
|
22
|
+
export type RecurrenceWeekday = 'MO' | 'TU' | 'WE' | 'TH' | 'FR' | 'SA' | 'SU';
|
|
23
|
+
/** Los siete días, de lunes a domingo. */
|
|
24
|
+
export declare const RECURRENCE_WEEKDAYS: RecurrenceWeekday[];
|
|
25
|
+
/** Cuándo deja de repetirse. */
|
|
26
|
+
export type RecurrenceEnd = {
|
|
27
|
+
type: 'never';
|
|
28
|
+
} | {
|
|
29
|
+
type: 'until';
|
|
30
|
+
date: Date | null;
|
|
31
|
+
} | {
|
|
32
|
+
type: 'count';
|
|
33
|
+
count: number;
|
|
34
|
+
};
|
|
35
|
+
/** La repetición, tal y como la edita una persona. */
|
|
36
|
+
export interface RecurrenceValue {
|
|
37
|
+
frequency: RecurrenceFrequency;
|
|
38
|
+
/** Cada cuántos periodos. 1 o más. */
|
|
39
|
+
interval: number;
|
|
40
|
+
/** Días elegidos, solo en la frecuencia semanal. */
|
|
41
|
+
weekdays: RecurrenceWeekday[];
|
|
42
|
+
end: RecurrenceEnd;
|
|
43
|
+
/**
|
|
44
|
+
* Las partes de la regla que el editor no enseña (`BYMONTHDAY`, `WKST`…),
|
|
45
|
+
* en el orden en que venían. Se conservan al leer y se vuelven a escribir:
|
|
46
|
+
* editar la repetición no destruye lo que no se ve.
|
|
47
|
+
*/
|
|
48
|
+
rest?: string[];
|
|
49
|
+
}
|
|
50
|
+
/** La repetición de partida al activarla: cada semana, sin fin. */
|
|
51
|
+
export declare const DEFAULT_RECURRENCE: RecurrenceValue;
|
|
52
|
+
/**
|
|
53
|
+
* La cadena `RRULE` de una repetición. `null` —«no se repite»— da la cadena
|
|
54
|
+
* vacía, que es lo que se guarda cuando no hay regla.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildRecurrenceRule(value: RecurrenceValue | null): string;
|
|
57
|
+
/**
|
|
58
|
+
* La repetición que describe una cadena `RRULE`. Una cadena vacía, ausente o
|
|
59
|
+
* sin `FREQ` reconocible es **«no se repite»** (`null`): una regla sin
|
|
60
|
+
* frecuencia no es una regla.
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseRecurrenceRule(rule: string | null | undefined): RecurrenceValue | null;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
2
|
+
import { type StepMarkerTone } from '../../atoms/StepMarker/StepMarker';
|
|
3
|
+
import type { IconName } from '../../atoms/Icon/Icon';
|
|
4
|
+
import './Timeline.css';
|
|
5
|
+
/**
|
|
6
|
+
* Los dos textos del historial, y los dos son **cromo**: cómo se llama la
|
|
7
|
+
* lista y cómo se dice, para quien no ve la marca rellena, que un hito es el
|
|
8
|
+
* estado vigente. Ni las fechas ni los nombres pasan por aquí — son contenido,
|
|
9
|
+
* y los escribe quien pasa los hitos.
|
|
10
|
+
*/
|
|
11
|
+
export interface TimelineMessages {
|
|
12
|
+
/** Nombre accesible del historial cuando la pantalla no le da uno propio. */
|
|
13
|
+
label: string;
|
|
14
|
+
/** Lo que se lee tras el título del hito vigente: «estado actual». */
|
|
15
|
+
current: string;
|
|
16
|
+
}
|
|
17
|
+
/** El tono de la marca: los mismos ocho que `StepMarker`, sin traducción. */
|
|
18
|
+
export type TimelineTone = StepMarkerTone;
|
|
19
|
+
/** Un hito del historial: un cambio de estado, con su fecha y quien lo firmó. */
|
|
20
|
+
export interface TimelineItem {
|
|
21
|
+
id: string;
|
|
22
|
+
/** El estado al que se pasó: «En revisión», «Contratado». */
|
|
23
|
+
title: ReactNode;
|
|
24
|
+
/**
|
|
25
|
+
* Cuándo pasó, **ya escrito** por quien lo pasa («14 de marzo, 09:12»). El
|
|
26
|
+
* historial no formatea fechas: eso es `Intl` y sabe de idiomas y de husos,
|
|
27
|
+
* cosas que un sistema de diseño no tiene por qué saber.
|
|
28
|
+
*/
|
|
29
|
+
date?: ReactNode;
|
|
30
|
+
/** Quién lo hizo. */
|
|
31
|
+
author?: ReactNode;
|
|
32
|
+
/** La nota que se dejó al cambiar de estado. */
|
|
33
|
+
note?: ReactNode;
|
|
34
|
+
/** Tono de la marca. Default `neutral`. */
|
|
35
|
+
tone?: TimelineTone;
|
|
36
|
+
/** Icono dentro de la marca, en vez del punto. */
|
|
37
|
+
icon?: IconName;
|
|
38
|
+
/**
|
|
39
|
+
* El estado vigente. Pinta la marca rellena de su tono y añade el texto de
|
|
40
|
+
* `timeline.current` para quien no la ve. Como mucho uno: si vienen varios,
|
|
41
|
+
* el historial pinta todos (no es él quien decide qué expediente es
|
|
42
|
+
* coherente), pero quien lo usa marca el último.
|
|
43
|
+
*/
|
|
44
|
+
current?: boolean;
|
|
45
|
+
/** Acciones del hito (un enlace al documento, un botón de deshacer). */
|
|
46
|
+
actions?: ReactNode;
|
|
47
|
+
}
|
|
48
|
+
export interface TimelineProps extends Omit<ComponentPropsWithoutRef<'ol'>, 'children'> {
|
|
49
|
+
/** Los hitos, en el orden en que se pintan. */
|
|
50
|
+
items: TimelineItem[];
|
|
51
|
+
/**
|
|
52
|
+
* Nombre accesible del historial. **Sin default**: sin él, sale de
|
|
53
|
+
* `timeline.label` del `BrandMessagesProvider`.
|
|
54
|
+
*/
|
|
55
|
+
label?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Lo que se lee tras el título del hito vigente. **Sin default**: sin él,
|
|
58
|
+
* sale de `timeline.current`. Solo se lee cuando hay un hito `current`.
|
|
59
|
+
*/
|
|
60
|
+
currentLabel?: string;
|
|
61
|
+
/** Talla de la marca. Default `sm`: el historial es cromo, no un asistente. */
|
|
62
|
+
markerSize?: 'sm' | 'md';
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* El historial vertical de un expediente: un hito por cambio de estado, con su
|
|
66
|
+
* fecha, quien lo firmó y la nota que dejó. Es lo que lleva la ficha de un
|
|
67
|
+
* ticket y la de una candidatura.
|
|
68
|
+
*
|
|
69
|
+
* **No ordena ni interpreta.** Pinta los hitos en el orden en que llegan —el
|
|
70
|
+
* más reciente arriba o abajo, lo decide quien lo usa— y no deduce cuál es el
|
|
71
|
+
* estado vigente: se marca con `current`.
|
|
72
|
+
*
|
|
73
|
+
* La lista es una `<ol>`, así que el orden y el número de hitos se anuncian
|
|
74
|
+
* solos; el carril y el color de la marca no son la única señal, porque el
|
|
75
|
+
* estado va escrito en el título de cada hito.
|
|
76
|
+
*/
|
|
77
|
+
export declare const Timeline: import("react").ForwardRefExoticComponent<TimelineProps & import("react").RefAttributes<HTMLOListElement>>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
2
|
+
import './OrgChart.css';
|
|
3
|
+
/**
|
|
4
|
+
* Los textos del organigrama. Todo es **cromo**: cómo se llama el lienzo, cómo
|
|
5
|
+
* se nombran los dos grupos de personas de una tarjeta, cómo se dice que no
|
|
6
|
+
* hay nadie en uno de ellos y los cuatro nombres de los controles. Los nombres
|
|
7
|
+
* de departamentos y personas son contenido y los escribe quien los pasa.
|
|
8
|
+
*/
|
|
9
|
+
export interface OrgChartMessages {
|
|
10
|
+
/** Nombre accesible del lienzo. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Rótulo del grupo de responsables de una tarjeta. */
|
|
13
|
+
managers: string;
|
|
14
|
+
/** Rótulo del grupo de personas de una tarjeta. */
|
|
15
|
+
members: string;
|
|
16
|
+
/** Cómo se dice que un departamento no tiene responsable. */
|
|
17
|
+
noManagers: string;
|
|
18
|
+
/** Cómo se dice que un departamento no tiene equipo. */
|
|
19
|
+
noMembers: string;
|
|
20
|
+
/** Nombre accesible del botón que pliega un departamento. */
|
|
21
|
+
collapse: (name: string) => string;
|
|
22
|
+
/** Nombre accesible del botón que lo despliega. */
|
|
23
|
+
expand: (name: string) => string;
|
|
24
|
+
/** Nombre accesible del botón de acercar. */
|
|
25
|
+
zoomIn: string;
|
|
26
|
+
/** Nombre accesible del botón de alejar. */
|
|
27
|
+
zoomOut: string;
|
|
28
|
+
/** Nombre accesible del botón que devuelve el zoom a su sitio. */
|
|
29
|
+
zoomReset: string;
|
|
30
|
+
}
|
|
31
|
+
/** Una persona dentro de un departamento. */
|
|
32
|
+
export interface OrgChartPerson {
|
|
33
|
+
id: string;
|
|
34
|
+
/** El nombre, en texto plano. */
|
|
35
|
+
name: string;
|
|
36
|
+
/** Cómo se pinta el nombre. Sin ella, el propio `name`. Admite un enlace a la ficha. */
|
|
37
|
+
label?: ReactNode;
|
|
38
|
+
/** El puesto, bajo el nombre. */
|
|
39
|
+
role?: ReactNode;
|
|
40
|
+
}
|
|
41
|
+
/** Un departamento: su gente y los departamentos que cuelgan de él. */
|
|
42
|
+
export interface OrgChartNode {
|
|
43
|
+
id: string;
|
|
44
|
+
/** El nombre, **en texto plano**: con él se nombra el botón de plegado. */
|
|
45
|
+
name: string;
|
|
46
|
+
/** Cómo se pinta el nombre. Sin ella, el propio `name`. */
|
|
47
|
+
label?: ReactNode;
|
|
48
|
+
/** Quien responde del departamento. */
|
|
49
|
+
managers?: OrgChartPerson[];
|
|
50
|
+
/** El resto del equipo. */
|
|
51
|
+
members?: OrgChartPerson[];
|
|
52
|
+
/** Los departamentos que cuelgan de este. */
|
|
53
|
+
children?: OrgChartNode[];
|
|
54
|
+
}
|
|
55
|
+
export interface OrgChartProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
56
|
+
/** Las raíces del árbol. Normalmente una, pero admite varias. */
|
|
57
|
+
nodes: OrgChartNode[];
|
|
58
|
+
/** Los departamentos plegados (controlado). */
|
|
59
|
+
collapsed?: string[];
|
|
60
|
+
/** Los departamentos plegados al montar (no controlado). */
|
|
61
|
+
defaultCollapsed?: string[];
|
|
62
|
+
/** Se llama con la lista entera de plegados. */
|
|
63
|
+
onCollapsedChange?: (collapsed: string[]) => void;
|
|
64
|
+
/** El zoom (controlado). 1 es el tamaño natural. */
|
|
65
|
+
zoom?: number;
|
|
66
|
+
/** El zoom al montar (no controlado). Default 1. */
|
|
67
|
+
defaultZoom?: number;
|
|
68
|
+
/** Se llama con el zoom nuevo. */
|
|
69
|
+
onZoomChange?: (zoom: number) => void;
|
|
70
|
+
/** Zoom mínimo. Default 0.5. */
|
|
71
|
+
minZoom?: number;
|
|
72
|
+
/** Zoom máximo. Default 1.5. */
|
|
73
|
+
maxZoom?: number;
|
|
74
|
+
/** Cuánto cambia el zoom en cada paso. Default 0.1. */
|
|
75
|
+
zoomStep?: number;
|
|
76
|
+
/** Pinta los tres botones de zoom. Default `true`. */
|
|
77
|
+
showZoom?: boolean;
|
|
78
|
+
/** Lo que va al final de la barra de controles, junto a los botones de zoom. */
|
|
79
|
+
toolbar?: ReactNode;
|
|
80
|
+
/** Enseña los grupos de personas dentro de cada tarjeta. Default `true`. */
|
|
81
|
+
showPeople?: boolean;
|
|
82
|
+
/** Nombre accesible del lienzo. Sin él, sale de `orgChart.label`. */
|
|
83
|
+
label?: string;
|
|
84
|
+
/** Rótulo del grupo de responsables. Sin él, sale de `orgChart.managers`. */
|
|
85
|
+
managersLabel?: string;
|
|
86
|
+
/** Rótulo del grupo de equipo. Sin él, sale de `orgChart.members`. */
|
|
87
|
+
membersLabel?: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* El organigrama de departamentos: quién responde de cada uno, quién está
|
|
91
|
+
* dentro y de quién cuelga. Plegable, con zoom y con desplazamiento.
|
|
92
|
+
*
|
|
93
|
+
* **Es un árbol de listas anidadas, no un lienzo de dibujo.** Las líneas las
|
|
94
|
+
* pintan pseudoelementos, así que el orden del documento y el del árbol son el
|
|
95
|
+
* mismo: un lector de pantalla recorre la jerarquía de verdad, no una
|
|
96
|
+
* alternativa en texto escrita aparte. El zoom usa `zoom` y no `transform`
|
|
97
|
+
* porque `zoom` cambia la maqueta: el lienzo sigue sabiendo cuánto mide lo que
|
|
98
|
+
* tiene dentro, y el desplazamiento sigue llegando al final.
|
|
99
|
+
*
|
|
100
|
+
* **No coloca nada**: no hay motor de disposición, ni aristas, ni posiciones.
|
|
101
|
+
* Un árbol se dibuja centrando cada nodo sobre sus hijos, que es lo que hace
|
|
102
|
+
* el CSS solo.
|
|
103
|
+
*/
|
|
104
|
+
export declare const OrgChart: import("react").ForwardRefExoticComponent<OrgChartProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
2
|
+
import './PlanningGrid.css';
|
|
3
|
+
/**
|
|
4
|
+
* Los textos de la rejilla. Todo es **cromo**: cómo se llama la tabla, cómo se
|
|
5
|
+
* nombra el campo de cada cruce y cómo se llaman las tres filas del pie. Los
|
|
6
|
+
* nombres de filas y columnas son contenido y los escribe quien los pasa; las
|
|
7
|
+
* horas son formato y salen de `locale`.
|
|
8
|
+
*/
|
|
9
|
+
export interface PlanningGridMessages {
|
|
10
|
+
/** Nombre accesible de la rejilla cuando la pantalla no le da uno propio. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Nombre accesible del campo de un cruce, a partir de los dos ejes. */
|
|
13
|
+
cellLabel: (row: string, column: string) => string;
|
|
14
|
+
/** Etiqueta de la columna de totales de fila. */
|
|
15
|
+
rowTotal: string;
|
|
16
|
+
/** Etiqueta de la fila de totales de columna. */
|
|
17
|
+
columnTotal: string;
|
|
18
|
+
/** Etiqueta de la fila de horas disponibles. */
|
|
19
|
+
capacity: string;
|
|
20
|
+
/** Etiqueta de la fila de resto (disponible − asignado). */
|
|
21
|
+
remaining: string;
|
|
22
|
+
/** Lo que se lee tras un resto negativo: hay más horas asignadas que disponibles. */
|
|
23
|
+
over: string;
|
|
24
|
+
}
|
|
25
|
+
/** Una fila: un proyecto, una persona. Lo que se planifica. */
|
|
26
|
+
export interface PlanningGridRow {
|
|
27
|
+
id: string;
|
|
28
|
+
/** El nombre, **en texto plano**: con él se nombra el campo de cada cruce. */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Cómo se pinta el nombre. Sin ella, el propio `name`. Admite un enlace. */
|
|
31
|
+
label?: ReactNode;
|
|
32
|
+
/** La fila entera no se edita. */
|
|
33
|
+
readOnly?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** Una columna: una semana, un día, una persona. Contra qué se planifica. */
|
|
36
|
+
export interface PlanningGridColumn {
|
|
37
|
+
key: string;
|
|
38
|
+
/** El nombre, **en texto plano**: con él se nombra el campo de cada cruce. */
|
|
39
|
+
name: string;
|
|
40
|
+
/** Cómo se pinta el nombre. Sin ella, el propio `name`. */
|
|
41
|
+
label?: ReactNode;
|
|
42
|
+
/** Segunda línea del encabezado: el rango de fechas de la semana, la fecha del día. */
|
|
43
|
+
sublabel?: ReactNode;
|
|
44
|
+
/** Horas disponibles de la columna. Sin ella, la columna no tiene tope. */
|
|
45
|
+
capacity?: number;
|
|
46
|
+
/** La columna entera no se edita: una semana pasada, un día no laborable. */
|
|
47
|
+
readOnly?: boolean;
|
|
48
|
+
/** La columna en curso: se recuadra. */
|
|
49
|
+
current?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** Un cruce con horas. Lo que no venga en la lista vale cero. */
|
|
52
|
+
export interface PlanningGridCell {
|
|
53
|
+
rowId: string;
|
|
54
|
+
columnKey: string;
|
|
55
|
+
/** Las horas. `null` es lo mismo que cero a efectos de suma, y se pinta vacío. */
|
|
56
|
+
value: number | null;
|
|
57
|
+
/** Este cruce en concreto no se edita. */
|
|
58
|
+
readOnly?: boolean;
|
|
59
|
+
/** El cambio está en vuelo: la celda se atenúa. */
|
|
60
|
+
pending?: boolean;
|
|
61
|
+
/** Lo que falló al guardar este cruce. Pone el campo en error y se anuncia. */
|
|
62
|
+
error?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface PlanningGridProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
65
|
+
rows: PlanningGridRow[];
|
|
66
|
+
columns: PlanningGridColumn[];
|
|
67
|
+
/** Los cruces con horas. Los que falten valen cero. */
|
|
68
|
+
cells: PlanningGridCell[];
|
|
69
|
+
/**
|
|
70
|
+
* Se llama al **confirmar** un cruce —al salir del campo o con Intro—, no en
|
|
71
|
+
* cada tecla: la rejilla guarda contra un servidor, y una petición por
|
|
72
|
+
* pulsación no es una rejilla, es una tormenta.
|
|
73
|
+
*/
|
|
74
|
+
onCellChange?: (rowId: string, columnKey: string, value: number) => void;
|
|
75
|
+
/** La rejilla entera se mira, no se edita. Sin `onCellChange` ya lo está. */
|
|
76
|
+
readOnly?: boolean;
|
|
77
|
+
/** Encabezado de la columna de filas («Proyecto», «Persona»). */
|
|
78
|
+
rowHeader?: ReactNode;
|
|
79
|
+
/** Columna de totales por fila, al final. Default `true`. */
|
|
80
|
+
showRowTotals?: boolean;
|
|
81
|
+
/** Fila de totales por columna, al pie. Default `true`. */
|
|
82
|
+
showColumnTotals?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Fila de horas disponibles al pie. Default `false`, y solo sale si alguna
|
|
85
|
+
* columna declara `capacity`: repetir el tope en cada columna solo hace
|
|
86
|
+
* falta cuando cambia de una a otra.
|
|
87
|
+
*/
|
|
88
|
+
showCapacity?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Fila de resto (disponible − asignado) al pie. Default `true`, y solo sale
|
|
91
|
+
* si alguna columna declara `capacity`.
|
|
92
|
+
*/
|
|
93
|
+
showRemaining?: boolean;
|
|
94
|
+
/** Tope de horas de un cruce. Default 999. */
|
|
95
|
+
max?: number;
|
|
96
|
+
/** Locale del formato de horas. Default `'es-ES'`. */
|
|
97
|
+
locale?: string;
|
|
98
|
+
/** Cómo se escriben unas horas. Default: la cifra con `Intl` y una `h`. */
|
|
99
|
+
formatHours?: (hours: number) => string;
|
|
100
|
+
/** Talla de los campos. Default `sm`: son celdas de una rejilla. */
|
|
101
|
+
size?: 'sm' | 'md' | 'lg';
|
|
102
|
+
/** Nombre accesible de la rejilla. Sin él, sale de `planningGrid.label`. */
|
|
103
|
+
label?: string;
|
|
104
|
+
/** Nombre accesible del campo de un cruce. Sin él, sale de `planningGrid.cellLabel`. */
|
|
105
|
+
cellLabel?: (row: string, column: string) => string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* La rejilla editable de horas: fila × columna —persona por semana, proyecto
|
|
109
|
+
* por día—, con los totales de cada eje y la disponibilidad de cada columna.
|
|
110
|
+
*
|
|
111
|
+
* **No guarda ni valida contra nada.** Llama a `onCellChange` al confirmar un
|
|
112
|
+
* cruce y espera que le vuelvan a pasar las celdas; el estado en vuelo y el
|
|
113
|
+
* error de guardado viajan **en la propia celda** (`pending`, `error`), que es
|
|
114
|
+
* lo que permite que dos cruces estén en vuelo a la vez sin que la rejilla
|
|
115
|
+
* tenga que llevar la cuenta.
|
|
116
|
+
*
|
|
117
|
+
* Los totales sí los suma ella: son la suma de lo que tiene delante, y no hay
|
|
118
|
+
* dos formas de hacerla.
|
|
119
|
+
*/
|
|
120
|
+
export declare const PlanningGrid: import("react").ForwardRefExoticComponent<PlanningGridProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leer unas horas escritas a mano, aparte del componente para poder probarlo
|
|
3
|
+
* sin montar nada.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Las horas que dice un texto. Admite **coma y punto** decimal —en castellano
|
|
7
|
+
* se escribe «7,5»—, entiende el campo vacío como cero y devuelve `null`
|
|
8
|
+
* cuando lo escrito no son unas horas: un negativo tampoco lo es.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parsePlanningHours(text: string): number | null;
|