@nanisoft/prism-ui 0.16.1 → 1.0.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/.tsbuildinfo +1 -1
- package/dist/blocks/activity-feed-01/activity-feed-01.d.ts +120 -190
- package/dist/blocks/activity-feed-01/activity-feed-01.d.ts.map +1 -1
- package/dist/blocks/activity-feed-01/activity-feed-01.js +107 -97
- package/dist/blocks/activity-feed-01/index.d.ts +1 -1
- package/dist/blocks/activity-feed-01/index.d.ts.map +1 -1
- package/dist/blocks/auth-form-01/auth-form.d.ts +86 -53
- package/dist/blocks/auth-form-01/auth-form.d.ts.map +1 -1
- package/dist/blocks/auth-form-01/auth-form.js +95 -31
- package/dist/blocks/auth-form-01/index.d.ts +1 -1
- package/dist/blocks/auth-form-01/index.d.ts.map +1 -1
- package/dist/blocks/calendar-01/calendar.d.ts +28 -0
- package/dist/blocks/calendar-01/calendar.d.ts.map +1 -1
- package/dist/blocks/calendar-01/calendar.js +15 -1
- package/dist/blocks/card-index-01/card-index.d.ts +225 -0
- package/dist/blocks/card-index-01/card-index.d.ts.map +1 -0
- package/dist/blocks/card-index-01/card-index.js +154 -0
- package/dist/blocks/card-index-01/index.d.ts +3 -0
- package/dist/blocks/card-index-01/index.d.ts.map +1 -0
- package/dist/blocks/card-index-01/index.js +1 -0
- package/dist/blocks/chart-card-01/chart-card-01.d.ts +23 -21
- package/dist/blocks/chart-card-01/chart-card-01.d.ts.map +1 -1
- package/dist/blocks/chart-card-01/chart-card-01.js +46 -1
- package/dist/blocks/chart-card-01/index.d.ts +1 -1
- package/dist/blocks/chart-card-01/index.d.ts.map +1 -1
- package/dist/blocks/chart-group-01/chart-group-01.d.ts +3 -3
- package/dist/blocks/chart-group-01/chart-group-01.d.ts.map +1 -1
- package/dist/blocks/contact-01/contact.d.ts +30 -82
- package/dist/blocks/contact-01/contact.d.ts.map +1 -1
- package/dist/blocks/contact-01/contact.js +59 -30
- package/dist/blocks/contact-01/index.d.ts +1 -1
- package/dist/blocks/contact-01/index.d.ts.map +1 -1
- package/dist/blocks/dashboard-01/dashboard-01.d.ts +26 -71
- package/dist/blocks/dashboard-01/dashboard-01.d.ts.map +1 -1
- package/dist/blocks/dashboard-01/dashboard-01.js +34 -23
- package/dist/blocks/dashboard-01/index.d.ts +1 -1
- package/dist/blocks/dashboard-01/index.d.ts.map +1 -1
- package/dist/blocks/data-table-01/data-table.d.ts +276 -59
- package/dist/blocks/data-table-01/data-table.d.ts.map +1 -1
- package/dist/blocks/data-table-01/data-table.js +217 -35
- package/dist/blocks/data-table-01/index.d.ts +1 -1
- package/dist/blocks/data-table-01/index.d.ts.map +1 -1
- package/dist/blocks/directory-01/directory-01.d.ts +23 -2
- package/dist/blocks/directory-01/directory-01.d.ts.map +1 -1
- package/dist/blocks/directory-01/directory-01.js +3 -3
- package/dist/blocks/empty-state-01/empty-state.d.ts +60 -21
- package/dist/blocks/empty-state-01/empty-state.d.ts.map +1 -1
- package/dist/blocks/empty-state-01/empty-state.js +52 -17
- package/dist/blocks/index-detail-01/index-detail.d.ts +85 -0
- package/dist/blocks/index-detail-01/index-detail.d.ts.map +1 -0
- package/dist/blocks/index-detail-01/index-detail.js +58 -0
- package/dist/blocks/index-detail-01/index.d.ts +3 -0
- package/dist/blocks/index-detail-01/index.d.ts.map +1 -0
- package/dist/blocks/index-detail-01/index.js +1 -0
- package/dist/blocks/index.d.ts +20 -8
- package/dist/blocks/index.d.ts.map +1 -1
- package/dist/blocks/index.js +7 -0
- package/dist/blocks/issue-detail-01/issue-detail.d.ts +18 -0
- package/dist/blocks/issue-detail-01/issue-detail.d.ts.map +1 -1
- package/dist/blocks/issue-detail-01/issue-detail.js +18 -0
- package/dist/blocks/nothing-chosen-01/index.d.ts +3 -0
- package/dist/blocks/nothing-chosen-01/index.d.ts.map +1 -0
- package/dist/blocks/nothing-chosen-01/index.js +1 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.d.ts +119 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.d.ts.map +1 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.js +79 -0
- package/dist/blocks/project-dashboard-01/index.d.ts +1 -1
- package/dist/blocks/project-dashboard-01/index.d.ts.map +1 -1
- package/dist/blocks/project-dashboard-01/project-dashboard.d.ts +21 -27
- package/dist/blocks/project-dashboard-01/project-dashboard.d.ts.map +1 -1
- package/dist/blocks/project-dashboard-01/project-dashboard.js +45 -5
- package/dist/blocks/provisioning-01/index.d.ts +1 -1
- package/dist/blocks/provisioning-01/index.d.ts.map +1 -1
- package/dist/blocks/provisioning-01/provisioning.d.ts +9 -97
- package/dist/blocks/provisioning-01/provisioning.d.ts.map +1 -1
- package/dist/blocks/provisioning-01/provisioning.js +26 -13
- package/dist/blocks/record-detail-01/index.d.ts +3 -0
- package/dist/blocks/record-detail-01/index.d.ts.map +1 -0
- package/dist/blocks/record-detail-01/index.js +1 -0
- package/dist/blocks/record-detail-01/record-detail.d.ts +239 -0
- package/dist/blocks/record-detail-01/record-detail.d.ts.map +1 -0
- package/dist/blocks/record-detail-01/record-detail.js +150 -0
- package/dist/blocks/record-form-01/index.d.ts +3 -0
- package/dist/blocks/record-form-01/index.d.ts.map +1 -0
- package/dist/blocks/record-form-01/index.js +1 -0
- package/dist/blocks/record-form-01/record-form.d.ts +148 -0
- package/dist/blocks/record-form-01/record-form.d.ts.map +1 -0
- package/dist/blocks/record-form-01/record-form.js +107 -0
- package/dist/blocks/record-grid-01/index.d.ts +3 -0
- package/dist/blocks/record-grid-01/index.d.ts.map +1 -0
- package/dist/blocks/record-grid-01/index.js +1 -0
- package/dist/blocks/record-grid-01/record-grid.d.ts +145 -0
- package/dist/blocks/record-grid-01/record-grid.d.ts.map +1 -0
- package/dist/blocks/record-grid-01/record-grid.js +114 -0
- package/dist/blocks/record-wizard-01/index.d.ts +3 -0
- package/dist/blocks/record-wizard-01/index.d.ts.map +1 -0
- package/dist/blocks/record-wizard-01/index.js +1 -0
- package/dist/blocks/record-wizard-01/record-wizard.d.ts +152 -0
- package/dist/blocks/record-wizard-01/record-wizard.d.ts.map +1 -0
- package/dist/blocks/record-wizard-01/record-wizard.js +144 -0
- package/dist/blocks/settings-members-01/settings-members.d.ts +2 -1
- package/dist/blocks/settings-members-01/settings-members.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/index.d.ts +1 -1
- package/dist/blocks/settings-panel-01/index.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.d.ts +165 -67
- package/dist/blocks/settings-panel-01/settings-panel.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.js +92 -33
- package/dist/blocks/signup-01/index.d.ts +1 -1
- package/dist/blocks/signup-01/index.d.ts.map +1 -1
- package/dist/blocks/signup-01/signup-01.d.ts +10 -84
- package/dist/blocks/signup-01/signup-01.d.ts.map +1 -1
- package/dist/blocks/signup-01/signup-01.js +41 -23
- package/dist/blocks/stats-01/index.d.ts +5 -5
- package/dist/blocks/stats-01/index.d.ts.map +1 -1
- package/dist/blocks/stats-01/index.js +4 -4
- package/dist/blocks/stats-01/stats.d.ts +49 -14
- package/dist/blocks/stats-01/stats.d.ts.map +1 -1
- package/dist/blocks/stats-01/stats.js +65 -15
- package/dist/blocks/trend-01/index.d.ts +1 -1
- package/dist/blocks/trend-01/index.d.ts.map +1 -1
- package/dist/blocks/trend-01/trend.d.ts +26 -73
- package/dist/blocks/trend-01/trend.d.ts.map +1 -1
- package/dist/blocks/trend-01/trend.js +47 -30
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +93 -3
- package/dist/components/ui/metric.d.ts +4 -2
- package/dist/components/ui/metric.d.ts.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/lib/field-render.d.ts +107 -0
- package/dist/lib/field-render.d.ts.map +1 -0
- package/dist/lib/field-render.js +251 -0
- package/dist/lib/spec.d.ts +609 -0
- package/dist/lib/spec.d.ts.map +1 -0
- package/dist/lib/spec.js +1 -0
- package/dist/live/index.d.ts +2 -0
- package/dist/live/index.d.ts.map +1 -1
- package/dist/live/index.js +6 -0
- package/dist/live/message-thread-01/index.d.ts +3 -0
- package/dist/live/message-thread-01/index.d.ts.map +1 -0
- package/dist/live/message-thread-01/index.js +1 -0
- package/dist/live/message-thread-01/message-thread.d.ts +282 -0
- package/dist/live/message-thread-01/message-thread.d.ts.map +1 -0
- package/dist/live/message-thread-01/message-thread.js +221 -0
- package/dist/live/run-stream-01/run-stream.d.ts +2 -1
- package/dist/live/run-stream-01/run-stream.d.ts.map +1 -1
- package/dist/live/run-stream-01/run-stream.js +1 -7
- package/dist/live/status.d.ts +27 -0
- package/dist/live/status.d.ts.map +1 -0
- package/dist/live/status.js +15 -0
- package/dist/live/tool-call-row.d.ts +26 -0
- package/dist/live/tool-call-row.d.ts.map +1 -0
- package/dist/live/tool-call-row.js +60 -0
- package/dist/live/tool-ledger-01/tool-ledger.d.ts.map +1 -1
- package/dist/live/tool-ledger-01/tool-ledger.js +2 -42
- package/dist/pages/contact-page/index.d.ts +7 -7
- package/dist/pages/contact-page/index.d.ts.map +1 -1
- package/dist/pages/contact-page/index.js +17 -5
- package/dist/pages/document-page/document-page.d.ts +146 -0
- package/dist/pages/document-page/document-page.d.ts.map +1 -0
- package/dist/pages/document-page/document-page.js +80 -0
- package/dist/pages/document-page/index.d.ts +3 -0
- package/dist/pages/document-page/index.d.ts.map +1 -0
- package/dist/pages/document-page/index.js +1 -0
- package/dist/pages/index.d.ts +2 -0
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +6 -0
- package/dist/styles.css +35 -3
- package/package.json +7 -2
|
@@ -1,81 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
3
|
-
|
|
4
|
-
* One choice in a `select` field, and nothing else.
|
|
5
|
-
*
|
|
6
|
-
* A native `<option>` holds text and nothing more, which is the whole reason an
|
|
7
|
-
* account form's select is a `NativeSelect` rather than the Base UI one. The two
|
|
8
|
-
* values are a string and its label, so a caller who needs a description beside an
|
|
9
|
-
* option, a count, or anything nested is reaching for a control the platform cannot
|
|
10
|
-
* render, and `Signup01` does not pretend otherwise: it names `NativeSelect`'s own
|
|
11
|
-
* argument in its JSDoc and stops there.
|
|
12
|
-
*/
|
|
13
|
-
export type SignupOption = {
|
|
14
|
-
/** What the form submits for this choice, which is a key and not a sentence. */
|
|
15
|
-
value: string;
|
|
16
|
-
/** The words the reader sees, in the product's own language. */
|
|
17
|
-
label: string;
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* What every account field carries, whichever control it draws.
|
|
21
|
-
*
|
|
22
|
-
* `required` is a boolean rather than an optional prop because there is no third
|
|
23
|
-
* state worth having: a field is one the reader must fill or one they may leave, and
|
|
24
|
-
* a `required?: boolean` leaves a caller who passed nothing unable to say which of
|
|
25
|
-
* the two they meant. A field is a data declaration and nothing else, and the two
|
|
26
|
-
* arms below differ only in which control is drawn and in the one property that
|
|
27
|
-
* control needs and the other has no use for.
|
|
28
|
-
*/
|
|
29
|
-
type SignupFieldBase = {
|
|
30
|
-
/** The caller's key for this field, and the key the value arrives under. */
|
|
31
|
-
id: string;
|
|
32
|
-
/** The field's visible name, and the accessible name its control announces. */
|
|
33
|
-
label: string;
|
|
34
|
-
/** Whether the reader may submit the form without filling it. */
|
|
35
|
-
required: boolean;
|
|
36
|
-
/**
|
|
37
|
-
* A line under the control, announced with it rather than printed under the row.
|
|
38
|
-
*
|
|
39
|
-
* A node, because the honest line in this position is often not one sentence: a
|
|
40
|
-
* caller's hint, a link to the document that says why a field is being asked for,
|
|
41
|
-
* a piece of the page's own copy. It is wired to the control through
|
|
42
|
-
* `aria-describedby`, so a screen reader hears what this field is for as the reader
|
|
43
|
-
* reaches it rather than as a footnote they have to go back for.
|
|
44
|
-
*/
|
|
45
|
-
description?: ReactNode;
|
|
46
|
-
/** What the control shows while it is empty. A hint, not a name. */
|
|
47
|
-
placeholder?: string;
|
|
48
|
-
/** The platform's own autofill hint for this field. */
|
|
49
|
-
autoComplete?: string;
|
|
50
|
-
};
|
|
51
|
-
/**
|
|
52
|
-
* One field of the caller's account form, and the control it draws.
|
|
53
|
-
*
|
|
54
|
-
* **A union and not one type with an optional `options`, and that is the shape of
|
|
55
|
-
* the whole argument in this Block.** An account form's fields are the caller's
|
|
56
|
-
* declaration, not this package's opinion about what a person may be asked, and the
|
|
57
|
-
* one place that shows is the select: a select with no options is a control with
|
|
58
|
-
* nothing in it, which is a question no reader can answer, so `options` is required
|
|
59
|
-
* inside the select arm and forbidden beside the other three. A `options?: string[]`
|
|
60
|
-
* on one type would typecheck the defect and only the diagnostic at run time would
|
|
61
|
-
* catch it, which is one gate too late. The union is the same mechanism
|
|
62
|
-
* `ContactField` uses for the same reason: the shape that requires something declares
|
|
63
|
-
* it, and the shape that forbids it says so in the type rather than in prose.
|
|
64
|
-
*
|
|
65
|
-
* The cost of the union is that a caller who wants to build a field list
|
|
66
|
-
* generically, from a schema, has to narrow before it can type the result, and the
|
|
67
|
-
* answer to that is a narrowing function in the caller's own code rather than a
|
|
68
|
-
* looser type here.
|
|
69
|
-
*/
|
|
70
|
-
export type SignupField = (SignupFieldBase & {
|
|
71
|
-
type: 'select';
|
|
72
|
-
/** The choices, in the order the reader should meet them. */
|
|
73
|
-
options: readonly SignupOption[];
|
|
74
|
-
}) | (SignupFieldBase & {
|
|
75
|
-
type: 'text' | 'email' | 'tel';
|
|
76
|
-
/** A select with no options is a question nobody can answer, so it is not one here. */
|
|
77
|
-
options?: never;
|
|
78
|
-
});
|
|
3
|
+
import type { FieldSpecGroup } from '../../lib/spec.js';
|
|
79
4
|
/**
|
|
80
5
|
* One reading of the secret the reader is typing, in the caller's own words and on
|
|
81
6
|
* the caller's own scale.
|
|
@@ -237,7 +162,7 @@ export type Signup01Props = {
|
|
|
237
162
|
*/
|
|
238
163
|
onSubmit: (value: SignupValue) => void;
|
|
239
164
|
/**
|
|
240
|
-
* The fields,
|
|
165
|
+
* The fields, grouped and ordered, as the shared field specification.
|
|
241
166
|
*
|
|
242
167
|
* **Required, and this list is the whole design rather than a convenience.** An
|
|
243
168
|
* account form with a fixed set of fields is a form that decides what a person may
|
|
@@ -247,13 +172,14 @@ export type Signup01Props = {
|
|
|
247
172
|
* where every entity is scoped to one, a form that cannot ask for a job title
|
|
248
173
|
* guarantees a sales team three weeks of discovery calls, and a form that cannot
|
|
249
174
|
* ask which estate somebody is observing sends a half a dozen accounts onto a
|
|
250
|
-
* queue that belongs to one.
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
175
|
+
* queue that belongs to one. The fields arrive as `FieldSpecGroup`, the same type
|
|
176
|
+
* a record write form takes, so the vocabulary a consumer learns on one screen is
|
|
177
|
+
* the vocabulary here and a control this package does not ship is the `slot` arm.
|
|
178
|
+
* The cost is stated rather than hidden: one column in the order the list gives,
|
|
179
|
+
* so a consumer that wants a two-column form for a subset composes two of these
|
|
180
|
+
* or reaches for a `Page`.
|
|
255
181
|
*/
|
|
256
|
-
|
|
182
|
+
groups: readonly FieldSpecGroup[];
|
|
257
183
|
/** The secret, and the caller's own reading of it. See `Signup01Password`. */
|
|
258
184
|
password: Signup01Password;
|
|
259
185
|
/**
|
|
@@ -400,6 +326,6 @@ export type Signup01Props = {
|
|
|
400
326
|
* function to a prop, so a caller rendering this from a server component gets a form
|
|
401
327
|
* that submits and calls nothing and a reading that never appears.
|
|
402
328
|
*/
|
|
403
|
-
export declare function Signup01({ eyebrow, title, description, onSubmit,
|
|
329
|
+
export declare function Signup01({ eyebrow, title, description, onSubmit, groups, password, terms, submitLabel, submitting, status, signIn, headingLevel, className, }: Signup01Props): import("react").JSX.Element;
|
|
404
330
|
export default Signup01;
|
|
405
331
|
//# sourceMappingURL=signup-01.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/signup-01/signup-01.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"signup-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/signup-01/signup-01.tsx"],"names":[],"mappings":"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"}
|
|
@@ -4,13 +4,13 @@ import { useId, useState } from 'react';
|
|
|
4
4
|
import { Button } from '../../components/ui/button.js';
|
|
5
5
|
import { Card } from '../../components/ui/card.js';
|
|
6
6
|
import { CtaLink } from '../../components/ui/cta-link.js';
|
|
7
|
-
import { Field, FieldDescription, FieldGroup
|
|
8
|
-
import {
|
|
7
|
+
import { Field, FieldDescription, FieldGroup } from '../../components/ui/field.js';
|
|
8
|
+
import { Label } from '../../components/ui/label.js';
|
|
9
9
|
import { LiveRegion } from '../../components/ui/live-region.js';
|
|
10
|
-
import { NativeSelect } from '../../components/ui/native-select.js';
|
|
11
10
|
import { PasswordField } from '../../components/ui/password-field.js';
|
|
12
11
|
import { Progress } from '../../components/ui/progress.js';
|
|
13
12
|
import { Section, SectionHeading } from '../../components/ui/section.js';
|
|
13
|
+
import { CHOICE_KINDS, fieldText, renderControl, SELF_LABELLED, } from '../../lib/field-render.js';
|
|
14
14
|
import { cn } from '../../lib/utils.js';
|
|
15
15
|
/**
|
|
16
16
|
* The empty form, which is an empty record and an empty secret.
|
|
@@ -47,18 +47,21 @@ function blank(value) {
|
|
|
47
47
|
* reader is watching while they choose it.
|
|
48
48
|
*/
|
|
49
49
|
function assertSignup(input) {
|
|
50
|
-
const {
|
|
51
|
-
for (const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
const { groups, password, signIn } = input;
|
|
51
|
+
for (const group of groups) {
|
|
52
|
+
for (const field of group.fields) {
|
|
53
|
+
if (blank(typeof field.label === 'string' ? field.label : undefined)) {
|
|
54
|
+
throw new Error(`Signup01: the field "${field.key}" declares no label, so its control would be announced with no name ` +
|
|
55
|
+
'and two fields on this page would be announced identically. Every control this Block draws is named ' +
|
|
56
|
+
'by its label, so there is no fallback to fall back to.');
|
|
57
|
+
}
|
|
58
|
+
if (CHOICE_KINDS.has(field.kind) &&
|
|
59
|
+
(field.options?.length ?? 0) === 0) {
|
|
60
|
+
throw new Error(`Signup01: the field "${field.key}" is a choice and passes no options, so it would render a control ` +
|
|
61
|
+
'with nothing in it, which is a question no reader can answer and looks answered only because the ' +
|
|
62
|
+
'control is there and focusable. Pass the choices, or declare the field as a text field and check the ' +
|
|
63
|
+
'answer in your own handler.');
|
|
64
|
+
}
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
67
|
if (blank(password.label) || blank(password.revealLabel) || blank(password.hideLabel)) {
|
|
@@ -172,8 +175,8 @@ function assertSignup(input) {
|
|
|
172
175
|
* function to a prop, so a caller rendering this from a server component gets a form
|
|
173
176
|
* that submits and calls nothing and a reading that never appears.
|
|
174
177
|
*/
|
|
175
|
-
export function Signup01({ eyebrow, title, description, onSubmit,
|
|
176
|
-
assertSignup({
|
|
178
|
+
export function Signup01({ eyebrow, title, description, onSubmit, groups, password, terms, submitLabel, submitting = false, status, signIn, headingLevel = 'h2', className, }) {
|
|
179
|
+
assertSignup({ groups, password, signIn });
|
|
177
180
|
const generated = useId();
|
|
178
181
|
const working = submitting || status?.state === 'working';
|
|
179
182
|
const done = status?.state === 'done';
|
|
@@ -193,17 +196,32 @@ export function Signup01({ eyebrow, title, description, onSubmit, fields, passwo
|
|
|
193
196
|
if (done)
|
|
194
197
|
setTyped(EMPTY_SIGNUP);
|
|
195
198
|
}
|
|
196
|
-
const setField = (id) => (value) => setTyped((was) => ({
|
|
199
|
+
const setField = (id) => (value) => setTyped((was) => ({
|
|
200
|
+
...was,
|
|
201
|
+
fields: { ...was.fields, [id]: value === undefined || value === null ? '' : String(value) },
|
|
202
|
+
}));
|
|
197
203
|
const setSecret = (value) => setTyped((was) => ({ ...was, password: value }));
|
|
198
204
|
const handleSubmit = (event) => {
|
|
199
205
|
event.preventDefault();
|
|
200
206
|
onSubmit(typed);
|
|
201
207
|
};
|
|
202
208
|
const reading = password.strength === undefined ? null : password.strength(typed.password);
|
|
203
|
-
return (_jsx(Section, { "data-slot": "signup-01", className: cn(className), children: _jsxs("div", { "data-slot": "signup-01-body", className: "flex flex-col gap-10", children: [_jsx(SectionHeading, { as: headingLevel, align: "left", eyebrow: eyebrow, title: title, description: description }), _jsx(Card, { "data-slot": "signup-01-card", className: "max-w-measure-narrow gap-0 px-6", children: _jsxs("form", { "data-slot": "signup-01-form", onSubmit: handleSubmit, className: "flex w-full flex-col gap-5", children: [_jsxs(FieldGroup, { "data-slot": "signup-01-fields", children: [fields.map((field) => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
209
|
+
return (_jsx(Section, { "data-slot": "signup-01", className: cn(className), children: _jsxs("div", { "data-slot": "signup-01-body", className: "flex flex-col gap-10", children: [_jsx(SectionHeading, { as: headingLevel, align: "left", eyebrow: eyebrow, title: title, description: description }), _jsx(Card, { "data-slot": "signup-01-card", className: "max-w-measure-narrow gap-0 px-6", children: _jsxs("form", { "data-slot": "signup-01-form", onSubmit: handleSubmit, className: "flex w-full flex-col gap-5", children: [_jsxs(FieldGroup, { "data-slot": "signup-01-fields", children: [groups.map((group, groupIndex) => (_jsxs(FieldGroup, { "data-slot": "signup-01-group", className: "flex flex-col gap-5", children: [group.label === undefined ? null : (_jsx("p", { "data-slot": "signup-01-group-label", className: "text-foreground text-sm font-medium", children: group.label })), group.description === undefined ? null : (_jsx("p", { "data-slot": "signup-01-group-description", className: "text-muted-foreground text-sm", children: group.description })), group.fields.map((field, fieldIndex) => {
|
|
210
|
+
const id = `${generated}-${groupIndex}-${fieldIndex}`;
|
|
211
|
+
const labelId = `${id}-label`;
|
|
212
|
+
const helpId = field.help === undefined ? undefined : `${id}-help`;
|
|
213
|
+
const kind = field.kind.toLowerCase();
|
|
214
|
+
const ctx = {
|
|
215
|
+
id,
|
|
216
|
+
labelId,
|
|
217
|
+
describedBy: helpId,
|
|
218
|
+
invalid: false,
|
|
219
|
+
text: fieldText(field),
|
|
220
|
+
values: typed.fields,
|
|
221
|
+
setValue: (key, value) => setField(key)(value),
|
|
222
|
+
controlled: true,
|
|
223
|
+
};
|
|
224
|
+
return (_jsxs(Field, { "data-slot": "signup-01-field", "data-field": field.kind, children: [SELF_LABELLED.has(kind) ? null : (_jsx(Label, { id: labelId, htmlFor: id, required: field.required, disabled: field.disabled, children: field.label })), renderControl(field, ctx), field.help === undefined ? null : (_jsx(FieldDescription, { id: helpId, children: field.help }))] }, field.key));
|
|
225
|
+
})] }, group.id ?? groupIndex))), _jsx(PasswordField, { value: typed.password, onValueChange: setSecret, label: password.label, description: password.description, revealLabel: password.revealLabel, hideLabel: password.hideLabel, autoComplete: "new-password" })] }), reading === null ? null : (_jsxs("div", { "data-slot": "signup-01-strength", className: "flex flex-col gap-1.5", children: [_jsx(Progress, { value: reading.value, max: 100, valueText: reading.label }), _jsx("span", { "data-slot": "signup-01-strength-label", className: "text-muted-foreground text-sm", children: reading.label })] })), terms === undefined ? null : (_jsx("p", { "data-slot": "signup-01-terms", className: "text-muted-foreground text-sm text-pretty", children: terms })), _jsxs("div", { "data-slot": "signup-01-actions", className: "flex flex-col gap-3", children: [_jsx(Button, { "data-slot": "signup-01-submit", type: "submit", disabled: working, className: "w-full", children: submitLabel }), _jsx(LiveRegion, { "data-slot": "signup-01-status", politeness: status?.state === 'error' ? 'assertive' : 'polite', busy: working, className: cn('text-sm', status?.state === 'error' ? 'text-destructive font-medium' : 'text-muted-foreground'), children: status?.message })] })] }) }), signIn === undefined ? null : (_jsx(CtaLink, { "data-slot": "signup-01-sign-in", href: signIn.href, variant: "ghost", size: "sm", className: "self-start", children: signIn.label }))] }) }));
|
|
208
226
|
}
|
|
209
227
|
export default Signup01;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The subpath exports the Block and its props, and the props are exported because
|
|
3
|
-
*
|
|
2
|
+
* The subpath exports the Block and its props, and the props are exported because a
|
|
3
|
+
* caller that builds a `MetricSpec[]` has to be able to name the type it is building.
|
|
4
4
|
* A Block whose prop types are only reachable through inference is a Block whose
|
|
5
|
-
* callers annotate their arrays as `any`, which is the point at which a rename
|
|
6
|
-
*
|
|
5
|
+
* callers annotate their arrays as `any`, which is the point at which a rename stops
|
|
6
|
+
* being caught by the compiler.
|
|
7
7
|
*/
|
|
8
8
|
export { Stats01, default } from './stats.js';
|
|
9
|
-
export type {
|
|
9
|
+
export type { Stats01Props } from './stats.js';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/stats-01/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAC1C,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/stats-01/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The subpath exports the Block and its props, and the props are exported because
|
|
3
|
-
*
|
|
2
|
+
* The subpath exports the Block and its props, and the props are exported because a
|
|
3
|
+
* caller that builds a `MetricSpec[]` has to be able to name the type it is building.
|
|
4
4
|
* A Block whose prop types are only reachable through inference is a Block whose
|
|
5
|
-
* callers annotate their arrays as `any`, which is the point at which a rename
|
|
6
|
-
*
|
|
5
|
+
* callers annotate their arrays as `any`, which is the point at which a rename stops
|
|
6
|
+
* being caught by the compiler.
|
|
7
7
|
*/
|
|
8
8
|
export { Stats01, default } from './stats.js';
|
|
@@ -1,27 +1,62 @@
|
|
|
1
1
|
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/** Period the delta compares against, e.g. "vs last month". */
|
|
8
|
-
hint?: string;
|
|
9
|
-
};
|
|
2
|
+
import type { MetricSpec } from '../../lib/spec.js';
|
|
3
|
+
/**
|
|
4
|
+
* The props a Stats01 takes. Every string and every figure is a prop and the Block
|
|
5
|
+
* ships none.
|
|
6
|
+
*/
|
|
10
7
|
export type Stats01Props = {
|
|
8
|
+
/** Optional label above the section title. See the No-Default-Eyebrow Rule. */
|
|
11
9
|
eyebrow?: string;
|
|
10
|
+
/** The section title. Omit it for a row composed under its own heading. */
|
|
12
11
|
title?: string;
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The figures, in the order a reader should meet them.
|
|
14
|
+
*
|
|
15
|
+
* **The shared `MetricSpec` and not a shape of this Block's own.** `key`,
|
|
16
|
+
* `label` and `value` are required on each figure, `delta` is a number whose sign
|
|
17
|
+
* is the direction, `deltaFormat` is the caller's own words for that change,
|
|
18
|
+
* `hint` carries the period or the caveat, and a `series` and an `href` each travel
|
|
19
|
+
* with the label or the words they cannot be read without. The type is published as
|
|
20
|
+
* `@nanisoft/prism-ui/spec`, so the words a caller learns on a summary are the
|
|
21
|
+
* words on this row.
|
|
22
|
+
*/
|
|
23
|
+
stats: MetricSpec[];
|
|
14
24
|
/** Heading level for the section title. See `HeadingLevel`. */
|
|
15
25
|
headingLevel?: HeadingLevel;
|
|
16
26
|
};
|
|
17
27
|
/**
|
|
18
|
-
* A KPI row
|
|
28
|
+
* A KPI row whose figures are the shared `MetricSpec` drawn by `Metric` and
|
|
29
|
+
* `Sparkline`. Every figure is a prop.
|
|
19
30
|
*
|
|
20
|
-
* This block used to carry a hardcoded set reading "128 blocks shipped" and
|
|
21
|
-
* weekly installs"
|
|
22
|
-
* yet, and because they lived in the component rather than at the call site,
|
|
23
|
-
* consumer who installed the block shipped fabricated metrics into their own
|
|
31
|
+
* **This block used to carry a hardcoded set reading "128 blocks shipped" and
|
|
32
|
+
* "9.4k weekly installs".** Those were invented figures about a library nobody
|
|
33
|
+
* installs yet, and because they lived in the component rather than at the call site,
|
|
34
|
+
* a consumer who installed the block shipped fabricated metrics into their own
|
|
24
35
|
* dashboard. A stat block with invented stats in it is worse than no stat block.
|
|
36
|
+
*
|
|
37
|
+
* **It declared its own statistic type and guessed the unit, and that guess is the
|
|
38
|
+
* reason it is in this migration.** Its `delta` was documented as a percentage change
|
|
39
|
+
* and the Block appended a percent sign to the number, which is the one guess
|
|
40
|
+
* `Metric` refuses to make, because a delta of `0.12` is twelve percent, twelve cents
|
|
41
|
+
* or twelve milliseconds. It also carried no series, so the trend a metric summary
|
|
42
|
+
* names had nowhere to go. After this migration a delta with no formatter prints the
|
|
43
|
+
* number the caller passed and a delta with a formatter prints the caller's words,
|
|
44
|
+
* and the same consumer stops seeing a formatted change on one screen and a bare
|
|
45
|
+
* number on another.
|
|
46
|
+
*
|
|
47
|
+
* **The Block keeps its own name, kind and arrangement.** It is the same job as a row
|
|
48
|
+
* of figures with less of it declared, and a consumer who wants figures above
|
|
49
|
+
* supporting detail composes the dashboard Block beside them rather than either
|
|
50
|
+
* becoming the other. Only the statistic shape moves, because a shape is shared and a
|
|
51
|
+
* screen is not.
|
|
52
|
+
*
|
|
53
|
+
* **It still aggregates nothing and judges nothing.** It does not sum, count,
|
|
54
|
+
* average, bucket, rank or divide, it owns no threshold, no target, no severity and
|
|
55
|
+
* no freshness, and it draws no period switcher, no comparison toggle and no export.
|
|
56
|
+
* The period is the caller's words, carried by the `hint` and the delta formatter,
|
|
57
|
+
* and no period member exists.
|
|
58
|
+
*
|
|
59
|
+
* It is a server Component: no hook, no state and no client code.
|
|
25
60
|
*/
|
|
26
61
|
export declare function Stats01({ eyebrow, title, stats, headingLevel }: Stats01Props): import("react").JSX.Element;
|
|
27
62
|
export default Stats01;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../src/blocks/stats-01/stats.tsx"],"names":[],"mappings":"AAGA,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAExF,
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../src/blocks/stats-01/stats.tsx"],"names":[],"mappings":"AAGA,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAExF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhD;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;;;;OAUG;IACH,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,YAAY,CAAA;CAC5B,CAAA;AAiCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAmB,EAAE,EAAE,YAAY,+BAqDnF;AAED,eAAe,OAAO,CAAA"}
|
|
@@ -1,27 +1,77 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ArrowDownRight, ArrowUpRight, Minus } from 'lucide-react';
|
|
3
2
|
import { Card, CardContent } from '../../components/ui/card.js';
|
|
3
|
+
import { CtaLink } from '../../components/ui/cta-link.js';
|
|
4
|
+
import { Metric } from '../../components/ui/metric.js';
|
|
4
5
|
import { Section, SectionHeading } from '../../components/ui/section.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import { Sparkline } from '../../components/ui/sparkline.js';
|
|
7
|
+
/**
|
|
8
|
+
* The refusals, as checks, so a tile that would render a link nobody can name is a
|
|
9
|
+
* diagnostic in a console rather than a rendered link.
|
|
10
|
+
*
|
|
11
|
+
* **The type already holds both pairs, and these checks exist for the caller the
|
|
12
|
+
* type never saw.** `MetricSpec` requires a destination's words wherever it has a
|
|
13
|
+
* destination and a series name wherever it has a series, so a typed caller cannot
|
|
14
|
+
* reach either diagnostic. A JavaScript caller can, and the cost of reaching one is
|
|
15
|
+
* a figure that draws a link with no words or a shape no reader can announce, so the
|
|
16
|
+
* same rule is stated once more where a runtime value can be read: the pair rule is
|
|
17
|
+
* the one `Dashboard01` makes.
|
|
18
|
+
*/
|
|
19
|
+
function assertMetrics(metrics) {
|
|
20
|
+
for (const metric of metrics) {
|
|
21
|
+
if ((metric.href === undefined) !== (metric.hrefLabel === undefined)) {
|
|
22
|
+
throw new Error(`Stats01: the statistic "${metric.key}" declares one of href and hrefLabel without the other, so the ` +
|
|
23
|
+
'tile would carry a link with no words on it, or a name with no link beside it. Pass the words that say ' +
|
|
24
|
+
'what following it does, or omit the href.');
|
|
25
|
+
}
|
|
26
|
+
if ((metric.series === undefined) !== (metric.seriesLabel === undefined)) {
|
|
27
|
+
throw new Error(`Stats01: the statistic "${metric.key}" declares one of series and seriesLabel without the other, so the ` +
|
|
28
|
+
'figure would be a picture with no name, which a screen reader cannot announce and two shapes in one row ' +
|
|
29
|
+
'cannot be told apart. Pass the series name in the words the product uses, or omit the series.');
|
|
30
|
+
}
|
|
8
31
|
}
|
|
9
|
-
const up = value > 0;
|
|
10
|
-
const Icon = up ? ArrowUpRight : ArrowDownRight;
|
|
11
|
-
return (_jsxs("span", { className: up
|
|
12
|
-
? 'text-success flex items-center gap-1 text-xs'
|
|
13
|
-
: 'text-destructive flex items-center gap-1 text-xs', children: [_jsx(Icon, { className: "size-3" }), Math.abs(value), "%"] }));
|
|
14
32
|
}
|
|
15
33
|
/**
|
|
16
|
-
* A KPI row
|
|
34
|
+
* A KPI row whose figures are the shared `MetricSpec` drawn by `Metric` and
|
|
35
|
+
* `Sparkline`. Every figure is a prop.
|
|
17
36
|
*
|
|
18
|
-
* This block used to carry a hardcoded set reading "128 blocks shipped" and
|
|
19
|
-
* weekly installs"
|
|
20
|
-
* yet, and because they lived in the component rather than at the call site,
|
|
21
|
-
* consumer who installed the block shipped fabricated metrics into their own
|
|
37
|
+
* **This block used to carry a hardcoded set reading "128 blocks shipped" and
|
|
38
|
+
* "9.4k weekly installs".** Those were invented figures about a library nobody
|
|
39
|
+
* installs yet, and because they lived in the component rather than at the call site,
|
|
40
|
+
* a consumer who installed the block shipped fabricated metrics into their own
|
|
22
41
|
* dashboard. A stat block with invented stats in it is worse than no stat block.
|
|
42
|
+
*
|
|
43
|
+
* **It declared its own statistic type and guessed the unit, and that guess is the
|
|
44
|
+
* reason it is in this migration.** Its `delta` was documented as a percentage change
|
|
45
|
+
* and the Block appended a percent sign to the number, which is the one guess
|
|
46
|
+
* `Metric` refuses to make, because a delta of `0.12` is twelve percent, twelve cents
|
|
47
|
+
* or twelve milliseconds. It also carried no series, so the trend a metric summary
|
|
48
|
+
* names had nowhere to go. After this migration a delta with no formatter prints the
|
|
49
|
+
* number the caller passed and a delta with a formatter prints the caller's words,
|
|
50
|
+
* and the same consumer stops seeing a formatted change on one screen and a bare
|
|
51
|
+
* number on another.
|
|
52
|
+
*
|
|
53
|
+
* **The Block keeps its own name, kind and arrangement.** It is the same job as a row
|
|
54
|
+
* of figures with less of it declared, and a consumer who wants figures above
|
|
55
|
+
* supporting detail composes the dashboard Block beside them rather than either
|
|
56
|
+
* becoming the other. Only the statistic shape moves, because a shape is shared and a
|
|
57
|
+
* screen is not.
|
|
58
|
+
*
|
|
59
|
+
* **It still aggregates nothing and judges nothing.** It does not sum, count,
|
|
60
|
+
* average, bucket, rank or divide, it owns no threshold, no target, no severity and
|
|
61
|
+
* no freshness, and it draws no period switcher, no comparison toggle and no export.
|
|
62
|
+
* The period is the caller's words, carried by the `hint` and the delta formatter,
|
|
63
|
+
* and no period member exists.
|
|
64
|
+
*
|
|
65
|
+
* It is a server Component: no hook, no state and no client code.
|
|
23
66
|
*/
|
|
24
67
|
export function Stats01({ eyebrow, title, stats, headingLevel = 'h2' }) {
|
|
25
|
-
|
|
68
|
+
assertMetrics(stats);
|
|
69
|
+
return (_jsxs(Section, { children: [title ? (_jsx(SectionHeading, { as: headingLevel, eyebrow: eyebrow, title: title, align: "left", className: "mb-10" })) : null, _jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: stats.map((metric) => {
|
|
70
|
+
// The delta's words are the caller's own node, and `Metric` takes a
|
|
71
|
+
// formatter, so the node is placed through one. A delta with no words is
|
|
72
|
+
// left to `Metric`, which prints the number the caller passed.
|
|
73
|
+
const deltaWords = metric.deltaFormat;
|
|
74
|
+
return (_jsx(Card, { "data-slot": "stats-01-stat", "data-metric": metric.key, className: "gap-2 py-5", children: _jsxs(CardContent, { className: "flex flex-col gap-3", children: [_jsx(Metric, { value: metric.value, label: metric.label, delta: metric.delta, deltaFormat: deltaWords === undefined ? undefined : () => deltaWords, hint: metric.hint }), metric.series === undefined ? null : (_jsx("div", { "data-slot": "stats-01-sparkline", className: "flex", children: _jsx(Sparkline, { values: metric.series, label: metric.seriesLabel, className: "ms-auto" }) })), metric.href === undefined ? null : (_jsx(CtaLink, { href: metric.href, variant: "ghost", size: "sm", children: metric.hrefLabel }))] }) }, metric.key));
|
|
75
|
+
}) })] }));
|
|
26
76
|
}
|
|
27
77
|
export default Stats01;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/trend-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/trend-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -1,64 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
3
|
-
|
|
4
|
-
* One row of a trend list: a name, what it is at now, how it moved, and a shape
|
|
5
|
-
* beside it.
|
|
6
|
-
*
|
|
7
|
-
* Every field except `id` and `label` is optional because a trend list is
|
|
8
|
-
* heterogeneous by nature. Some of the things a list tracks have a series behind
|
|
9
|
-
* them and some have only a reading, some moved and some did not, and a shape
|
|
10
|
-
* that made every field required would be a list where two thirds of every row is
|
|
11
|
-
* an empty cell drawn as a placeholder.
|
|
12
|
-
*/
|
|
13
|
-
export type Trend01Item = {
|
|
14
|
-
/** A stable key for the row. */
|
|
15
|
-
id: string;
|
|
16
|
-
/**
|
|
17
|
-
* The name of the thing being tracked, as a reader would name it in
|
|
18
|
-
* conversation. It is the label the metric carries, and it is the last thing
|
|
19
|
-
* read on the row because the figure and the direction come first.
|
|
20
|
-
*/
|
|
21
|
-
label: string;
|
|
22
|
-
/**
|
|
23
|
-
* The current reading, already formatted by the caller.
|
|
24
|
-
*
|
|
25
|
-
* A `ReactNode` and not a `number`, for the reason `MetricProps.value` gives:
|
|
26
|
-
* a caller's figure is often a value it formatted itself, with its own grouped
|
|
27
|
-
* thousands, its own currency and its own unit, and a `number` here would push
|
|
28
|
-
* that formatting onto every call site twice. Prism takes the node and formats
|
|
29
|
-
* nothing.
|
|
30
|
-
*/
|
|
31
|
-
value: ReactNode;
|
|
32
|
-
/**
|
|
33
|
-
* The change against the previous reading, as a number whose sign is the
|
|
34
|
-
* direction. See `MetricDelta` on the Component.
|
|
35
|
-
*/
|
|
36
|
-
delta?: number;
|
|
37
|
-
/**
|
|
38
|
-
* The words for that change, given the number. Required whenever `delta` is
|
|
39
|
-
* set, and the Block throws without it. See the Component JSDoc for the full
|
|
40
|
-
* argument.
|
|
41
|
-
*/
|
|
42
|
-
deltaLabel?: (delta: number) => string;
|
|
43
|
-
/**
|
|
44
|
-
* The readings behind the current one, oldest first, in the caller's own units.
|
|
45
|
-
*
|
|
46
|
-
* Raw magnitudes rather than percentages of a maximum, which is the contract
|
|
47
|
-
* `Sparkline` states: a caller that pre-normalises has two chances to get the
|
|
48
|
-
* shape wrong, and a series drawn against an axis nobody can see is a picture
|
|
49
|
-
* of somebody's arithmetic.
|
|
50
|
-
*/
|
|
51
|
-
series?: number[];
|
|
52
|
-
/**
|
|
53
|
-
* The name of that series, and the only thing a screen reader reads from the
|
|
54
|
-
* shape. Required whenever `series` is set, and the Block throws without it.
|
|
55
|
-
*/
|
|
56
|
-
seriesLabel?: string;
|
|
57
|
-
/** Where the row goes. Rendered as a native anchor, so the destination is real. */
|
|
58
|
-
href?: string;
|
|
59
|
-
/** The words on that link. Required whenever `href` is set. */
|
|
60
|
-
hrefLabel?: string;
|
|
61
|
-
};
|
|
3
|
+
import type { MetricSpec } from '../../lib/spec.js';
|
|
62
4
|
/**
|
|
63
5
|
* The props a Trend01 takes.
|
|
64
6
|
*
|
|
@@ -78,8 +20,13 @@ export type Trend01Props = {
|
|
|
78
20
|
/**
|
|
79
21
|
* The rows, in the order a reader should meet them. See the Component JSDoc
|
|
80
22
|
* for why the Block does not sort them.
|
|
23
|
+
*
|
|
24
|
+
* Each row is the shared `MetricSpec`: a stable `key` that is never the words of
|
|
25
|
+
* the label, a required `label` and `value`, a `delta` whose sign is the
|
|
26
|
+
* direction, an optional `deltaFormat` for the caller's own words, an optional
|
|
27
|
+
* `hint`, and an optional `series` and `href` with the names they travel with.
|
|
81
28
|
*/
|
|
82
|
-
items: readonly
|
|
29
|
+
items: readonly MetricSpec[];
|
|
83
30
|
/**
|
|
84
31
|
* How many rows to render. Defaults to the whole set.
|
|
85
32
|
*
|
|
@@ -133,19 +80,25 @@ export type Trend01Props = {
|
|
|
133
80
|
* implementation would be the one without the table, because the table is the part
|
|
134
81
|
* nobody draws by hand and it is the part that matters.
|
|
135
82
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
83
|
+
* **The rows are the shared `MetricSpec`, so a row here and a figure on a summary
|
|
84
|
+
* are one shape rather than two that disagree.** Each row carries a stable `key`
|
|
85
|
+
* that is never the words of the label, a required `label` and `value`, an optional
|
|
86
|
+
* `delta` whose sign is the direction, an optional `deltaFormat` carrying the
|
|
87
|
+
* caller's own words for the change, an optional `hint` for the period or the
|
|
88
|
+
* caveat, and an optional `series` and `href` each travelling with the name it
|
|
89
|
+
* cannot be read without. This closes the absence that put this Block in the
|
|
90
|
+
* migration: a reading now has a place for a series and for a destination, so the
|
|
91
|
+
* trend a metric summary names has somewhere to go when the two sit on one screen.
|
|
92
|
+
*
|
|
93
|
+
* **The words for the change stay the caller's, and the Block mints no period and
|
|
94
|
+
* no arithmetic.** `deltaFormat` is the caller's own node rather than a formatter
|
|
95
|
+
* this Block could write, because "up 12 percent", "+12%" and "12 higher than last
|
|
96
|
+
* week" are three sentences about one number and no single fallback is right for
|
|
97
|
+
* all three. A delta with a formatter prints the caller's words and one without
|
|
98
|
+
* prints the number the caller passed, which is `Metric`'s own contract. There is
|
|
99
|
+
* no period member, no derived rate of change and no period-over-period arithmetic
|
|
100
|
+
* over the series the caller passed, because each of those is a second arithmetic
|
|
101
|
+
* this package would be doing over a population it never fetched.
|
|
149
102
|
*
|
|
150
103
|
* **`seriesLabel` is required whenever any row carries a `series`, for the reason
|
|
151
104
|
* the `Sparkline` JSDoc gives in full: a figure with no name is not announced
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trend.d.ts","sourceRoot":"","sources":["../../../src/blocks/trend-01/trend.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"trend.d.ts","sourceRoot":"","sources":["../../../src/blocks/trend-01/trend.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAExF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAkChD;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iFAAiF;IACjF,KAAK,EAAE,SAAS,CAAA;IAChB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;;;;OAQG;IACH,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IAC5B;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,KAAK,EACL,WAAW,EACX,KAAK,EACL,KAAK,EACL,KAAK,EACL,YAAmB,EACnB,SAAS,GACV,EAAE,YAAY,sCA2Fd;AAED,eAAe,OAAO,CAAA"}
|