@incodetech/prizma-core-screens 0.1.3 → 0.2.3

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.
Files changed (80) hide show
  1. package/dist/DemoScreensConfigScreen.d.ts +36 -0
  2. package/dist/DemoScreensConfigScreen.js +30 -0
  3. package/dist/DemoScreensConfigSheetScreen.d.ts +39 -0
  4. package/dist/DemoScreensConfigSheetScreen.js +33 -0
  5. package/dist/DemoScreensEditSheetScreen.d.ts +30 -0
  6. package/dist/DemoScreensEditSheetScreen.js +28 -0
  7. package/dist/DemoScreensEndScreen.d.ts +9 -21
  8. package/dist/DemoScreensEndScreen.js +9 -14
  9. package/dist/DemoScreensLaunchScreen.d.ts +28 -0
  10. package/dist/DemoScreensLaunchScreen.js +26 -0
  11. package/dist/DemoScreensLayout.css +542 -0
  12. package/dist/DemoScreensRedirectScreen.d.ts +50 -0
  13. package/dist/DemoScreensRedirectScreen.js +38 -0
  14. package/dist/DemoScreensResultScreen.d.ts +52 -0
  15. package/dist/DemoScreensResultScreen.js +46 -0
  16. package/dist/DemoScreensRunningScreen.d.ts +22 -0
  17. package/dist/DemoScreensRunningScreen.js +16 -0
  18. package/dist/DemoScreensSettingsScreen.d.ts +37 -0
  19. package/dist/DemoScreensSettingsScreen.js +40 -0
  20. package/dist/DemoScreensSplashScreen.d.ts +27 -0
  21. package/dist/DemoScreensSplashScreen.js +25 -0
  22. package/dist/DemoScreensStartScreen.d.ts +38 -0
  23. package/dist/DemoScreensStartScreen.js +37 -0
  24. package/dist/DemoScreensStrings.d.ts +192 -0
  25. package/dist/DemoScreensStrings.js +11 -11
  26. package/dist/DemoScreensTermsScreen.d.ts +28 -0
  27. package/dist/DemoScreensTermsScreen.js +26 -0
  28. package/dist/EkybAddressDetailsScreen.js +9 -5
  29. package/dist/EkybBusinessDetailsScreen.js +6 -3
  30. package/dist/EkybOwnersScreen.d.ts +4 -0
  31. package/dist/EkybOwnersScreen.js +13 -5
  32. package/dist/EkybStrings.d.ts +12 -1
  33. package/dist/EkycCountryCatalog.d.ts +46 -0
  34. package/dist/EkycCountryCatalog.js +139 -0
  35. package/dist/EkycCountryFormScreen.d.ts +36 -0
  36. package/dist/EkycCountryFormScreen.js +94 -0
  37. package/dist/EkycDetailsScreen.js +11 -4
  38. package/dist/EkycLayout.css +23 -0
  39. package/dist/EkycLicenseScreen.js +7 -3
  40. package/dist/EkycStrings.d.ts +26 -0
  41. package/dist/FaceMatchComparisonScreen.d.ts +4 -2
  42. package/dist/FaceMatchComparisonScreen.js +3 -1
  43. package/dist/FaceMatchLayout.css +53 -3
  44. package/dist/FormScreen.css +74 -0
  45. package/dist/FormScreen.d.ts +17 -0
  46. package/dist/FormScreen.js +12 -1
  47. package/dist/index.d.ts +15 -2
  48. package/dist/index.js +13 -0
  49. package/dist/styles.css +1 -0
  50. package/package.json +62 -1
  51. package/src/DemoScreensConfigScreen.tsx +101 -0
  52. package/src/DemoScreensConfigSheetScreen.tsx +120 -0
  53. package/src/DemoScreensEditSheetScreen.tsx +103 -0
  54. package/src/DemoScreensEndScreen.tsx +14 -38
  55. package/src/DemoScreensLaunchScreen.tsx +96 -0
  56. package/src/DemoScreensLayout.css +542 -0
  57. package/src/DemoScreensRedirectScreen.tsx +190 -0
  58. package/src/DemoScreensResultScreen.tsx +115 -0
  59. package/src/DemoScreensRunningScreen.tsx +45 -0
  60. package/src/DemoScreensSettingsScreen.tsx +101 -0
  61. package/src/DemoScreensSplashScreen.tsx +47 -0
  62. package/src/DemoScreensStartScreen.tsx +105 -0
  63. package/src/DemoScreensStrings.ts +204 -11
  64. package/src/DemoScreensTermsScreen.tsx +82 -0
  65. package/src/EkybAddressDetailsScreen.tsx +7 -1
  66. package/src/EkybBusinessDetailsScreen.tsx +6 -1
  67. package/src/EkybOwnersScreen.tsx +46 -2
  68. package/src/EkybStrings.ts +12 -0
  69. package/src/EkycCountryCatalog.ts +199 -0
  70. package/src/EkycCountryFormScreen.tsx +184 -0
  71. package/src/EkycDetailsScreen.tsx +29 -20
  72. package/src/EkycLayout.css +23 -0
  73. package/src/EkycLicenseScreen.tsx +22 -16
  74. package/src/EkycStrings.ts +25 -0
  75. package/src/FaceMatchComparisonScreen.tsx +11 -3
  76. package/src/FaceMatchLayout.css +53 -3
  77. package/src/FormScreen.css +74 -0
  78. package/src/FormScreen.tsx +27 -1
  79. package/src/index.ts +65 -1
  80. package/src/styles.css +1 -0
@@ -0,0 +1,190 @@
1
+ import React, { forwardRef, type ReactNode } from 'react';
2
+ import { PrizmaButton } from '@incodetech/prizma-core-web/button';
3
+ import { PrizmaTitle } from '@incodetech/prizma-core-web/title';
4
+ import { PrizmaDisclaimer } from '@incodetech/prizma-core-web/disclaimer';
5
+ import { PrizmaTag } from '@incodetech/prizma-core-web/tag';
6
+ import { PrizmaSnackbar } from '@incodetech/prizma-core-web/snackbar';
7
+ import { PrizmaCombined, type PrizmaCountry } from '@incodetech/prizma-core-web/combined';
8
+ import { PrizmaScreenShell, type PrizmaScreenShellProps } from './ScreenShell';
9
+ import { PrizmaScreenNav } from './ScreenNav';
10
+ import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
11
+ import type { DemoScreensStrings } from './DemoScreensStrings';
12
+ import './ScreenLayout.css';
13
+ import './DemoScreensLayout.css';
14
+
15
+ /**
16
+ * Where the redirect page is in its life:
17
+ * - `qr` the QR tab (or the QR-only page)
18
+ * - `sms` the SMS tab, number not typed yet
19
+ * - `smsFilled` the SMS tab, number typed, Send live
20
+ * - `sent` "Link sent!", resend counting down
21
+ * - `resend` "Link sent!", resend available
22
+ * - `resent` "Link sent!" with the "resent" snackbar
23
+ * - `tooMany` "Too many attempts"
24
+ */
25
+ export type DemoScreensRedirectStage = 'qr' | 'sms' | 'smsFilled' | 'sent' | 'resend' | 'resent' | 'tooMany';
26
+
27
+ export interface DemoScreensRedirectScreenProps
28
+ extends Omit<PrizmaScreenShellProps, 'dark' | 'title' | 'children'> {
29
+ /** Family copy — this screen reads the `redirect` slice. */
30
+ strings: DemoScreensStrings;
31
+ stage?: DemoScreensRedirectStage;
32
+ /** Offer the SMS tab. Off: the `QR Only` section (4272:11388). Default on. */
33
+ sms?: boolean;
34
+ /** Offer "or · Continue on desktop" under the leg. Default on. */
35
+ desktop?: boolean;
36
+ /**
37
+ * The QR image — a host slot, like every photo on a mock. The screens
38
+ * package draws no QR of its own (it has no encoder and no URL to encode);
39
+ * the hub passes a real one for its sample link.
40
+ */
41
+ qr?: ReactNode;
42
+ screenId?: string;
43
+ overrides?: PrizmaScreenOverrides;
44
+ onAction?: PrizmaScreenActionHandler;
45
+ }
46
+
47
+ /**
48
+ * Demo screens · DESKTOP — the redirect page, `Redirect` (4272:11166) in
49
+ * `Demo App - Latest Design`: a desktop browser opened the link, and the page
50
+ * offers to move the session to a phone. One card centred in the window: the
51
+ * logo nav, a title block, two tabs (Scan QR · Send SMS), the leg's own
52
+ * content, then "or" and a secondary "Continue on desktop", and the tag.
53
+ *
54
+ * Authored at the card's width — 800 on the frame, the content column 752 —
55
+ * so it is the one screen in the family that is not a 390 column; Core Lab
56
+ * lays it on the desktop panel with `adapt.column`. Archetype F's band and
57
+ * footer supply the CTA rhythm; the card, the tabs and the QR well are the
58
+ * page's own (DEVIATIONS #GEN-15 for the tabs — Core has no Tabs yet).
59
+ */
60
+ export const DemoScreensRedirectScreen = forwardRef<HTMLDivElement, DemoScreensRedirectScreenProps>(
61
+ (
62
+ {
63
+ strings,
64
+ stage = 'qr',
65
+ sms = true,
66
+ desktop = true,
67
+ qr,
68
+ screenId = `demo-screens-redirect-${stage}${sms ? '' : '-qr-only'}${desktop ? '' : '-no-desktop'}`,
69
+ overrides,
70
+ onAction,
71
+ className,
72
+ ...shell
73
+ },
74
+ ref,
75
+ ) => {
76
+ const r = strings.redirect;
77
+ const onSms = stage !== 'qr';
78
+ const country: PrizmaCountry | undefined = [
79
+ { code: 'RS', name: 'Serbia', dialCode: '+381', flag: '' },
80
+ ].find(c => c.code === r.countryCode);
81
+ const subtitle = sms ? r.subtitle : desktop ? r.subtitleQrDesktop : r.subtitleQrOnly;
82
+ const verdict = stage === 'sent' || stage === 'resend' || stage === 'resent' || stage === 'tooMany';
83
+
84
+ return (
85
+ <PrizmaScreenShell
86
+ ref={ref}
87
+ data-screen-id={screenId}
88
+ className={['demo-screens-redirect', className ?? ''].filter(Boolean).join(' ')}
89
+ {...shell}
90
+ >
91
+ {stage === 'resent' ? (
92
+ <div className="demo-screens-toast demo-screens-toast--desktop">
93
+ <PrizmaSnackbar state="success">{r.sent.resentSnackbar}</PrizmaSnackbar>
94
+ </div>
95
+ ) : null}
96
+ <div className="demo-screens-redirect__card">
97
+ <PrizmaScreenNav logo={overrides?.logo} navLabel={strings.navLabel} />
98
+ <div className="prizma-screen-layout__content demo-screens-redirect__content">
99
+ <PrizmaTitle variant="centered" title={r.title} showSubtitle subtitle={subtitle} />
100
+ {sms ? (
101
+ <div className="demo-screens-tabs" role="tablist">
102
+ <button type="button" role="tab" aria-selected={!onSms} className={['demo-screens-tabs__tab', !onSms ? 'demo-screens-tabs__tab--active' : ''].join(' ')} onClick={() => onAction?.({ type: 'secondary', screenId })}>
103
+ {r.tabs.qr}
104
+ </button>
105
+ <button type="button" role="tab" aria-selected={onSms} className={['demo-screens-tabs__tab', onSms ? 'demo-screens-tabs__tab--active' : ''].join(' ')} onClick={() => onAction?.({ type: 'tertiary', screenId })}>
106
+ {r.tabs.sms}
107
+ </button>
108
+ </div>
109
+ ) : null}
110
+
111
+ {stage === 'qr' ? (
112
+ <div className="demo-screens-redirect__leg">
113
+ {sms ? <p className="demo-screens-redirect__hint">{r.qrHint}</p> : null}
114
+ <div className="demo-screens-redirect__qr" aria-label={r.tabs.qr}>{qr}</div>
115
+ </div>
116
+ ) : null}
117
+
118
+ {stage === 'sms' || stage === 'smsFilled' ? (
119
+ <div className="demo-screens-redirect__leg demo-screens-redirect__leg--form">
120
+ <p className="demo-screens-redirect__hint">{r.smsHint}</p>
121
+ {/* `defaultValue`, not `readOnly`: Core paints a read-only field
122
+ on the disabled surface (owner definition 2026-07-12), and
123
+ the frame shows a live, empty field — Elena caught the grey
124
+ selector on 2026-09-24. */}
125
+ <PrizmaCombined
126
+ key={stage}
127
+ aria-label={r.phoneLabel}
128
+ placeholder={r.phonePlaceholder}
129
+ defaultValue={stage === 'smsFilled' ? r.phoneValue : ''}
130
+ defaultCountry={country}
131
+ showRequiredMarker={false}
132
+ />
133
+ <PrizmaButton variant="primary" size="L" disabled={stage !== 'smsFilled'} onClick={() => onAction?.({ type: 'cta', screenId })}>
134
+ {r.sendLabel}
135
+ </PrizmaButton>
136
+ </div>
137
+ ) : null}
138
+
139
+ {verdict ? (
140
+ <div className="demo-screens-redirect__leg demo-screens-redirect__leg--verdict">
141
+ {stage === 'tooMany' ? (
142
+ <PrizmaTitle variant="centered" title={r.tooMany.title} showSubtitle subtitle={r.tooMany.subtitle} icon={{ type: 'status', status: 'error' }} />
143
+ ) : (
144
+ <PrizmaTitle
145
+ variant="centered"
146
+ title={r.sent.title}
147
+ showSubtitle
148
+ subtitle={`${r.sent.toPrefix} ${country?.dialCode ?? ''} ${r.phoneValue}`.trim()}
149
+ icon={{ type: 'status', status: 'success' }}
150
+ />
151
+ )}
152
+ {stage === 'tooMany' ? (
153
+ <PrizmaButton variant="tertiary-link" size="L" onClick={() => onAction?.({ type: 'tertiary', screenId })}>
154
+ {r.tooMany.changeLabel}
155
+ </PrizmaButton>
156
+ ) : stage === 'sent' || stage === 'resent' ? (
157
+ <PrizmaButton variant="tertiary-link" size="L" disabled>
158
+ {r.sent.resendIn}
159
+ </PrizmaButton>
160
+ ) : (
161
+ <PrizmaButton variant="tertiary-link" size="L" onClick={() => onAction?.({ type: 'cta', screenId })}>
162
+ {r.sent.resend}
163
+ </PrizmaButton>
164
+ )}
165
+ </div>
166
+ ) : null}
167
+ </div>
168
+ <div className="prizma-screen-layout__band demo-screens-redirect__band">
169
+ {desktop && !verdict ? (
170
+ <>
171
+ <PrizmaDisclaimer text={r.orLabel} />
172
+ <div className="prizma-screen-layout__band__ctas">
173
+ <PrizmaButton variant="secondary" size="L" onClick={() => onAction?.({ type: 'secondary', screenId })}>
174
+ {r.desktopLabel}
175
+ </PrizmaButton>
176
+ </div>
177
+ </>
178
+ ) : null}
179
+ <div className="prizma-screen-layout__band__tag">
180
+ <PrizmaTag label={strings.verifiedLabel} />
181
+ </div>
182
+ </div>
183
+ </div>
184
+ </PrizmaScreenShell>
185
+ );
186
+ },
187
+ );
188
+ DemoScreensRedirectScreen.displayName = 'DemoScreensRedirectScreen';
189
+
190
+ export default DemoScreensRedirectScreen;
@@ -0,0 +1,115 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { PrizmaResultScreen, type PrizmaResultScreenProps } from './ResultScreen';
3
+ import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
4
+ import type { DemoScreensStrings } from './DemoScreensStrings';
5
+ import './ScreenLayout.css';
6
+
7
+ /**
8
+ * The four ways a demo session ends. Named after what the BACKEND says, not
9
+ * after the colour the screen shows, so a host maps its session status straight
10
+ * onto this prop:
11
+ *
12
+ * - `approved` the session passed → green mark, no CTA
13
+ * - `rejected` it did not pass verification → red mark, restart CTA
14
+ * - `inReview` a human still has to decide → no mark, no CTA
15
+ * - `notCompleted` the flow broke before a verdict → red mark, restart CTA
16
+ *
17
+ * Marks and CTAs follow the Demo App dev's spec (2026-09-24, via Elena): a
18
+ * passed session has nowhere to go, so no Restart; a rejection and a broken
19
+ * flow both offer one; a pending review shows neither. The production frames
20
+ * differ in two places (Restart on approved; no mark on not completed) and
21
+ * are superseded by that spec — DEVIATIONS #GEN-14.
22
+ */
23
+ export type DemoScreensResultStatus = 'approved' | 'rejected' | 'inReview' | 'notCompleted';
24
+
25
+ export interface DemoScreensResultScreenProps
26
+ extends Omit<
27
+ PrizmaResultScreenProps,
28
+ 'status' | 'title' | 'subtitle' | 'glyph' | 'ctaLabel' | 'verifiedLabel' | 'navLabel'
29
+ > {
30
+ /** Family copy — this screen reads `result`, `end` and `notCompleted`. */
31
+ strings: DemoScreensStrings;
32
+ status: DemoScreensResultStatus;
33
+ /**
34
+ * Draw the restart CTA. Defaults per status (the verdicts have it, the two
35
+ * end screens do not); a host that wants a way out of an in-review screen,
36
+ * or none out of a rejection, says so here.
37
+ */
38
+ showRestart?: boolean;
39
+ screenId?: string;
40
+ overrides?: PrizmaScreenOverrides;
41
+ onAction?: PrizmaScreenActionHandler;
42
+ }
43
+
44
+ const MARK: Record<DemoScreensResultStatus, PrizmaResultScreenProps['status']> = {
45
+ approved: 'success',
46
+ rejected: 'error',
47
+ // No mark while a human decides: a tick or a cross here would claim a
48
+ // verdict the session never reached.
49
+ inReview: 'none',
50
+ // A broken flow reads as an error, not as a pending result (dev spec).
51
+ notCompleted: 'error',
52
+ };
53
+
54
+ const RESTART_BY_DEFAULT: Record<DemoScreensResultStatus, boolean> = {
55
+ approved: false,
56
+ rejected: true,
57
+ inReview: false,
58
+ notCompleted: true,
59
+ };
60
+
61
+ /**
62
+ * Demo screens — the end of a session, on the Result archetype
63
+ * (GUIDELINES §6-F, `PrizmaResultScreen`).
64
+ *
65
+ * Figma:
66
+ * `approved` `demo_7` (2010:5745) "Thank you!" · green mark
67
+ * `rejected` `demo_8` (2010:5808) "Verification failed" · red mark · Restart
68
+ * `inReview` `63:399` "Verification complete!" · bare title
69
+ * `notCompleted` "DemoApp: End Screens" › `Processnotcompleted` · red mark · Restart
70
+ *
71
+ * One component for the four because they are one screen with one axis: the
72
+ * host learns the session status and passes it through. `DemoScreensEndScreen`
73
+ * stays exported as the `inReview` case under its original name.
74
+ */
75
+ export const DemoScreensResultScreen = forwardRef<HTMLDivElement, DemoScreensResultScreenProps>(
76
+ (
77
+ {
78
+ strings,
79
+ status,
80
+ showRestart = RESTART_BY_DEFAULT[status],
81
+ screenId = `demo-screens-result-${status}`,
82
+ overrides,
83
+ onAction,
84
+ ...shell
85
+ },
86
+ ref,
87
+ ) => {
88
+ const copy =
89
+ status === 'approved'
90
+ ? strings.result.approved
91
+ : status === 'rejected'
92
+ ? strings.result.rejected
93
+ : status === 'inReview'
94
+ ? strings.end
95
+ : strings.notCompleted;
96
+ return (
97
+ <PrizmaResultScreen
98
+ ref={ref}
99
+ status={MARK[status]}
100
+ title={copy.title}
101
+ subtitle={copy.subtitle}
102
+ ctaLabel={showRestart ? strings.result.restartLabel : undefined}
103
+ verifiedLabel={strings.verifiedLabel}
104
+ navLabel={strings.navLabel}
105
+ screenId={screenId}
106
+ overrides={overrides}
107
+ onAction={onAction}
108
+ {...shell}
109
+ />
110
+ );
111
+ },
112
+ );
113
+ DemoScreensResultScreen.displayName = 'DemoScreensResultScreen';
114
+
115
+ export default DemoScreensResultScreen;
@@ -0,0 +1,45 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { PrizmaProcessingScreen, type PrizmaProcessingScreenProps } from './ProcessingScreen';
3
+ import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
4
+ import type { DemoScreensStrings } from './DemoScreensStrings';
5
+ import './ScreenLayout.css';
6
+
7
+ export interface DemoScreensRunningScreenProps
8
+ extends Omit<
9
+ PrizmaProcessingScreenProps,
10
+ 'status' | 'title' | 'subtitle' | 'verifiedLabel' | 'navLabel'
11
+ > {
12
+ /** Family copy — this screen reads the `running` slice. */
13
+ strings: DemoScreensStrings;
14
+ screenId?: string;
15
+ overrides?: PrizmaScreenOverrides;
16
+ onAction?: PrizmaScreenActionHandler;
17
+ }
18
+
19
+ /**
20
+ * Demo screens — "Hold on a sec…", `demo_6` (2010:6180): the wait while the
21
+ * session is scored, between the last module and the verdict.
22
+ *
23
+ * Archetype C (`PrizmaProcessingScreen`, GUIDELINES §6-C) in its pure form:
24
+ * spinner, title, one line under it, the tag. No CTA — there is nothing for
25
+ * the prospect to do but wait, and the frame draws no button.
26
+ */
27
+ export const DemoScreensRunningScreen = forwardRef<HTMLDivElement, DemoScreensRunningScreenProps>(
28
+ ({ strings, screenId = 'demo-screens-running', overrides, onAction, ...shell }, ref) => (
29
+ <PrizmaProcessingScreen
30
+ ref={ref}
31
+ status="loading"
32
+ title={strings.running.title}
33
+ subtitle={strings.running.subtitle}
34
+ verifiedLabel={strings.verifiedLabel}
35
+ navLabel={strings.navLabel}
36
+ screenId={screenId}
37
+ overrides={overrides}
38
+ onAction={onAction}
39
+ {...shell}
40
+ />
41
+ ),
42
+ );
43
+ DemoScreensRunningScreen.displayName = 'DemoScreensRunningScreen';
44
+
45
+ export default DemoScreensRunningScreen;
@@ -0,0 +1,101 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { PrizmaButton } from '@incodetech/prizma-core-web/button';
3
+ import { PrizmaTitle } from '@incodetech/prizma-core-web/title';
4
+ import { PrizmaNavigation } from '@incodetech/prizma-core-web/navigation';
5
+ import { PrizmaDropdown, type PrizmaDropdownOption } from '@incodetech/prizma-core-web/dropdown';
6
+ import { PrizmaCountryFlag } from '@incodetech/prizma-core-web/combined';
7
+ import { PrizmaScreenShell, type PrizmaScreenShellProps } from './ScreenShell';
8
+ import type { PrizmaScreenActionHandler } from './types';
9
+ import type { DemoScreensStrings } from './DemoScreensStrings';
10
+ import './ScreenLayout.css';
11
+ import './DemoScreensLayout.css';
12
+
13
+ export interface DemoScreensSettingsScreenProps
14
+ extends Omit<PrizmaScreenShellProps, 'dark' | 'title' | 'children'> {
15
+ /** Family copy — this screen reads the `settings` slice. */
16
+ strings: DemoScreensStrings;
17
+ /** The selected language, by `DemoScreensLanguage.value`. Defaults to the
18
+ * first one listed. */
19
+ value?: string;
20
+ /** Documentation-only: render the language menu open (`demo_4`). */
21
+ menuOpen?: boolean;
22
+ screenId?: string;
23
+ onAction?: PrizmaScreenActionHandler;
24
+ }
25
+
26
+ /**
27
+ * Demo screens — the settings sheet, `demo_3` (2010:6002) closed and `demo_4`
28
+ * (2010:6060) with the language menu open, in `Demo App - Latest Design`.
29
+ *
30
+ * Core's Navigation with the logo off and the close X on, a left-aligned H2,
31
+ * one searchable Dropdown, one CTA. No verified tag — the frame draws none on
32
+ * a settings surface.
33
+ *
34
+ * Archetype E (`PrizmaFormScreen`) is the near miss: it insists on the logo nav
35
+ * (or its reserved row) and has no close affordance, and the frame has neither
36
+ * a logo nor a back chevron. So the shell is composed here with the same
37
+ * layout classes E uses; the Spacing/32 between the title and the field is the
38
+ * one measure the file adds (`DemoScreensLayout.css`).
39
+ *
40
+ * Flags: the option rows draw the canonical raster where the bank has one
41
+ * (`US`, `RS`); rows without one draw the label alone rather than an emoji
42
+ * (DEVIATIONS #GEN-8 — Mexico and Spain are missing from the bank).
43
+ */
44
+ export const DemoScreensSettingsScreen = forwardRef<HTMLDivElement, DemoScreensSettingsScreenProps>(
45
+ (
46
+ { strings, value, menuOpen = false, screenId = 'demo-screens-settings', onAction, ...shell },
47
+ ref,
48
+ ) => {
49
+ const { settings } = strings;
50
+ const options: PrizmaDropdownOption[] = settings.languages.map(l => ({ value: l.value, label: l.label }));
51
+ const flagFor = (option: PrizmaDropdownOption) => {
52
+ const lang = settings.languages.find(l => l.value === option.value);
53
+ return lang?.countryCode ? (
54
+ <PrizmaCountryFlag
55
+ country={{ code: lang.countryCode, name: lang.label, dialCode: '', flag: '' }}
56
+ />
57
+ ) : null;
58
+ };
59
+ return (
60
+ <PrizmaScreenShell ref={ref} data-screen-id={screenId} {...shell}>
61
+ <div className="demo-screens-settings__nav">
62
+ <PrizmaNavigation
63
+ showIncodeLogo={false}
64
+ showRightIcon
65
+ closeLabel={settings.closeLabel}
66
+ onRightIconClick={() => onAction?.({ type: 'help', screenId })}
67
+ aria-label={strings.navLabel}
68
+ />
69
+ </div>
70
+ <div className="prizma-screen-layout__content">
71
+ <PrizmaTitle variant="left" title={settings.title} />
72
+ <div className="demo-screens-settings__fields">
73
+ <PrizmaDropdown
74
+ label={settings.languageLabel}
75
+ options={options}
76
+ defaultValue={value ?? settings.languages[0]?.value ?? null}
77
+ showRequiredMarker={false}
78
+ searchable
79
+ searchPlaceholder={settings.searchPlaceholder}
80
+ clearSearchLabel={settings.clearSearchLabel}
81
+ renderLeading={flagFor}
82
+ forceOpen={menuOpen}
83
+ />
84
+ </div>
85
+ </div>
86
+ {/* Archetype F's CTA band in its `--bare` form — no tag on a settings
87
+ sheet, so the button sits Spacing/24 off the bottom. */}
88
+ <div className="prizma-screen-layout__band prizma-screen-layout__band--bare demo-screens-band">
89
+ <div className="prizma-screen-layout__band__ctas">
90
+ <PrizmaButton variant="primary" size="L" onClick={() => onAction?.({ type: 'cta', screenId })}>
91
+ {settings.ctaLabel}
92
+ </PrizmaButton>
93
+ </div>
94
+ </div>
95
+ </PrizmaScreenShell>
96
+ );
97
+ },
98
+ );
99
+ DemoScreensSettingsScreen.displayName = 'DemoScreensSettingsScreen';
100
+
101
+ export default DemoScreensSettingsScreen;
@@ -0,0 +1,47 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { PrizmaLogo } from '@incodetech/prizma-core-web/logo';
3
+ import { PrizmaScreenShell, type PrizmaScreenShellProps } from './ScreenShell';
4
+ import type { DemoScreensStrings } from './DemoScreensStrings';
5
+ import './ScreenLayout.css';
6
+ import './DemoScreensLayout.css';
7
+
8
+ export interface DemoScreensSplashScreenProps
9
+ extends Omit<PrizmaScreenShellProps, 'dark' | 'title' | 'children'> {
10
+ /** Family copy — this screen reads `appName`. */
11
+ strings: DemoScreensStrings;
12
+ screenId?: string;
13
+ }
14
+
15
+ /**
16
+ * Demo screens — the splash, `demo_1` (2010:5868) in `Demo App - Latest Design`.
17
+ *
18
+ * The brand moment before the front door: the whole stage is Surface/Brand/500,
19
+ * the wordmark and the app name sit white in the middle. No nav, no CTA, no
20
+ * verified tag — the frame draws none, and there is nothing to verify yet.
21
+ *
22
+ * Not an archetype (GUIDELINES §6 has no "stage" template), so the shell gets
23
+ * one modifier class and the wordmark block is the same one the start screen
24
+ * draws. The colour goes through the shell's background and Core's two opt-in
25
+ * hooks (`--prizma-logo-fill`, Text/Body/0 static); no hex, no new palette.
26
+ *
27
+ * The desktop frame (2010:6362) is this same stage filling a 1440 browser
28
+ * window; the column does not centre, the colour does.
29
+ */
30
+ export const DemoScreensSplashScreen = forwardRef<HTMLDivElement, DemoScreensSplashScreenProps>(
31
+ ({ strings, screenId = 'demo-screens-splash', className, ...shell }, ref) => (
32
+ <PrizmaScreenShell
33
+ ref={ref}
34
+ data-screen-id={screenId}
35
+ className={['demo-screens-splash', className ?? ''].filter(Boolean).join(' ')}
36
+ {...shell}
37
+ >
38
+ <div className="demo-screens-wordmark">
39
+ <PrizmaLogo variant="incode" />
40
+ <p className="demo-screens-wordmark__caption">{strings.appName}</p>
41
+ </div>
42
+ </PrizmaScreenShell>
43
+ ),
44
+ );
45
+ DemoScreensSplashScreen.displayName = 'DemoScreensSplashScreen';
46
+
47
+ export default DemoScreensSplashScreen;
@@ -0,0 +1,105 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { PrizmaButton } from '@incodetech/prizma-core-web/button';
3
+ import { PrizmaLogo } from '@incodetech/prizma-core-web/logo';
4
+ import { PrizmaCountryFlag } from '@incodetech/prizma-core-web/combined';
5
+ import { PrizmaScreenShell, type PrizmaScreenShellProps } from './ScreenShell';
6
+ import type { PrizmaScreenActionHandler } from './types';
7
+ import type { DemoScreensStrings } from './DemoScreensStrings';
8
+ import './ScreenLayout.css';
9
+ import './DemoScreensLayout.css';
10
+
11
+ export interface DemoScreensStartScreenProps
12
+ extends Omit<PrizmaScreenShellProps, 'dark' | 'title' | 'children'> {
13
+ /** Family copy — this screen reads `appName`, `start` and `settings.languages`. */
14
+ strings: DemoScreensStrings;
15
+ /** The CTA is pressed and the session is being created (`2082:8743`). */
16
+ loading?: boolean;
17
+ screenId?: string;
18
+ onAction?: PrizmaScreenActionHandler;
19
+ }
20
+
21
+ /**
22
+ * Demo screens — the front door, `demo_2` (2010:5925) in `Demo App - Latest
23
+ * Design`, and its loading twin `2082:8743`.
24
+ *
25
+ * Header row: the current language (label over value, the flag from the
26
+ * canonical bank) on the left and the menu affordance on the right. Middle: the
27
+ * wordmark block the splash also draws. Bottom: one primary CTA. No verified
28
+ * tag — the frame draws none; nothing has been verified yet.
29
+ *
30
+ * Not an archetype: §6 has no template with a two-slot header and a centred
31
+ * wordmark, so the row is composed here from the nav row's own measures
32
+ * (`DemoScreensLayout.css`). The menu glyph is drawn locally — the frame's
33
+ * three-dot "Info menu" is not in the canonical bank yet, and the owner asked
34
+ * for the dots rather than a stand-in (Elena, 2026-09-24). It sits in
35
+ * Navigation's own icon button so size, colour and focus are Core's; the
36
+ * geometry is the only new thing, named as such in DEVIATIONS #GEN-10.
37
+ *
38
+ * `onAction`: `cta` when the CTA is pressed, `help` for the menu (the
39
+ * settings sheet is what it opens in the app).
40
+ */
41
+ /** The three-dot menu glyph, 24 box, currentColor — pending its bank entry. */
42
+ export const MenuDotsGlyph: React.FC = () => (
43
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
44
+ <circle cx="5" cy="12" r="2" fill="currentColor" />
45
+ <circle cx="12" cy="12" r="2" fill="currentColor" />
46
+ <circle cx="19" cy="12" r="2" fill="currentColor" />
47
+ </svg>
48
+ );
49
+
50
+ export const DemoScreensStartScreen = forwardRef<HTMLDivElement, DemoScreensStartScreenProps>(
51
+ ({ strings, loading = false, screenId = 'demo-screens-start', onAction, ...shell }, ref) => {
52
+ const current = strings.settings.languages.find(l => l.label === strings.start.languageValue);
53
+ return (
54
+ <PrizmaScreenShell ref={ref} data-screen-id={screenId} {...shell}>
55
+ <header className="demo-screens-start__header" aria-label={strings.navLabel}>
56
+ <div className="demo-screens-start__language">
57
+ <span className="demo-screens-start__language-label">{strings.start.languageLabel}</span>
58
+ <span className="demo-screens-start__language-value">
59
+ {current?.countryCode ? (
60
+ <PrizmaCountryFlag
61
+ country={{ code: current.countryCode, name: current.label, dialCode: '', flag: '' }}
62
+ />
63
+ ) : null}
64
+ {strings.start.languageValue}
65
+ </span>
66
+ </div>
67
+ <button
68
+ type="button"
69
+ className="prizma-nav__icon demo-screens-start__menu"
70
+ aria-label={strings.start.menuLabel}
71
+ onClick={() => onAction?.({ type: 'help', screenId })}
72
+ >
73
+ <MenuDotsGlyph />
74
+ </button>
75
+ </header>
76
+ <div className="prizma-screen-layout__content">
77
+ <div className="demo-screens-start__middle">
78
+ <div className="demo-screens-wordmark">
79
+ <PrizmaLogo variant="incode" />
80
+ <p className="demo-screens-wordmark__caption">{strings.appName}</p>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ {/* Archetype F's CTA band in its `--bare` form: this screen carries no
85
+ verified tag, so the button sits Spacing/24 off the bottom, where
86
+ the other modules' tag-less CTAs sit, not up at the tag's height. */}
87
+ <div className="prizma-screen-layout__band prizma-screen-layout__band--bare demo-screens-band">
88
+ <div className="prizma-screen-layout__band__ctas">
89
+ <PrizmaButton
90
+ variant="primary"
91
+ size="L"
92
+ loading={loading}
93
+ onClick={() => onAction?.({ type: 'cta', screenId })}
94
+ >
95
+ {strings.start.ctaLabel}
96
+ </PrizmaButton>
97
+ </div>
98
+ </div>
99
+ </PrizmaScreenShell>
100
+ );
101
+ },
102
+ );
103
+ DemoScreensStartScreen.displayName = 'DemoScreensStartScreen';
104
+
105
+ export default DemoScreensStartScreen;