@owlmeans/client-panel 0.1.18-rc.57 → 0.1.18-rc.59
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/README.md +5 -5
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/client-panel/SKILL.md +46 -19
- package/build/auth/i18n.js +9 -9
- package/build/auth/i18n.js.map +1 -1
- package/build/auth/login/index.d.ts +2 -0
- package/build/auth/login/index.d.ts.map +1 -1
- package/build/auth/login/index.js +2 -0
- package/build/auth/login/index.js.map +1 -1
- package/build/auth/login/model.d.ts +2 -2
- package/build/auth/login/model.d.ts.map +1 -1
- package/build/auth/login/model.js +11 -8
- package/build/auth/login/model.js.map +1 -1
- package/build/auth/login/provider/consts.local.d.ts +3 -0
- package/build/auth/login/provider/consts.local.d.ts.map +1 -0
- package/build/auth/login/provider/consts.local.js +3 -0
- package/build/auth/login/provider/consts.local.js.map +1 -0
- package/build/auth/login/provider-config.d.ts +37 -0
- package/build/auth/login/provider-config.d.ts.map +1 -0
- package/build/auth/login/provider-config.js +2 -0
- package/build/auth/login/provider-config.js.map +1 -0
- package/build/auth/login/provider.d.ts +5 -0
- package/build/auth/login/provider.d.ts.map +1 -0
- package/build/auth/login/provider.js +55 -0
- package/build/auth/login/provider.js.map +1 -0
- package/build/auth/login/types.d.ts +48 -1
- package/build/auth/login/types.d.ts.map +1 -1
- package/build/components/nav/helper.d.ts +1 -9
- package/build/components/nav/helper.d.ts.map +1 -1
- package/build/components/nav/helper.js +0 -12
- package/build/components/nav/helper.js.map +1 -1
- package/build/components/nav/index.d.ts +1 -0
- package/build/components/nav/index.d.ts.map +1 -1
- package/build/components/nav/index.js +1 -0
- package/build/components/nav/index.js.map +1 -1
- package/build/components/nav/label.d.ts +4 -0
- package/build/components/nav/label.d.ts.map +1 -0
- package/build/components/nav/label.js +12 -0
- package/build/components/nav/label.js.map +1 -0
- package/build/components/nav/types.d.ts +12 -0
- package/build/components/nav/types.d.ts.map +1 -1
- package/build/i18n.js +2 -2
- package/build/i18n.js.map +1 -1
- package/package.json +14 -14
- package/src/auth/i18n.ts +9 -9
- package/src/auth/login/index.ts +2 -0
- package/src/auth/login/model.ts +11 -11
- package/src/auth/login/provider/consts.local.ts +2 -0
- package/src/auth/login/provider-config.ts +38 -0
- package/src/auth/login/provider.ts +65 -0
- package/src/auth/login/types.ts +52 -1
- package/src/components/nav/helper.ts +1 -19
- package/src/components/nav/index.ts +1 -0
- package/src/components/nav/label.ts +20 -0
- package/src/components/nav/types.ts +13 -0
- package/src/i18n.ts +2 -2
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ headless navigation model.
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
bun add @owlmeans/client-panel@^0.1.18-rc.
|
|
19
|
+
bun add @owlmeans/client-panel@^0.1.18-rc.59
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Usage
|
|
@@ -130,12 +130,12 @@ Returns `sections` (with `hidden` filtered out), `current` (the active screen's
|
|
|
130
130
|
keyboard-operable, openable in a new tab). It returns `undefined` for a path carrying route
|
|
131
131
|
parameters.
|
|
132
132
|
|
|
133
|
-
### `resolveNavLabel(translate, label, key, alias)`
|
|
133
|
+
### `navLabelHelper.resolveNavLabel(translate, label, key, alias)`
|
|
134
134
|
|
|
135
135
|
Resolves a label as literal `label` → `translate(key, defaultNavLabel(alias))` → the humanized
|
|
136
|
-
alias. `translate` always reaches a component as a **prop**, defaulting to `defaultNavTranslate`
|
|
136
|
+
alias. `translate` always reaches a component as a **prop**, defaulting to `navLabelHelper.defaultNavTranslate`
|
|
137
137
|
(which returns the fallback) — a menu must never read an i18n context implicitly, because an app
|
|
138
|
-
mounted without an i18n provider throws inside render and blanks the page. `defaultNavLabel`
|
|
138
|
+
mounted without an i18n provider throws inside render and blanks the page. `navLabelHelper.defaultNavLabel`
|
|
139
139
|
humanizes the last alias segment
|
|
140
140
|
(`my-app:web:user-list` → `User list`). Default key families are `nav.<section>` and
|
|
141
141
|
`modules.<alias>`.
|
|
@@ -153,7 +153,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
153
153
|
your project's skill store (`.agents/skills/`):
|
|
154
154
|
|
|
155
155
|
```sh
|
|
156
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
156
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.51
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/client-panel",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-10-
|
|
4
|
+
"version": "0.1.18-rc.59",
|
|
5
|
+
"generatedAt": "2026-10-06T11:45:57.680Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/client-panel
|
|
9
9
|
|
|
10
10
|
**Layer:** Client
|
|
11
|
-
**Install:** `"@owlmeans/client-panel": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/client-panel": "^0.1.18-rc.59"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
## Key Exports
|
|
14
14
|
|
|
@@ -34,15 +34,17 @@ controller; apps may override it through their own `buttons` resource.
|
|
|
34
34
|
| `usePanelLayout()` / `useLayoutTitle(name?, alias?)` / `prepareLayoutTitle(title)` | The entrypoint the current layout is rendering, and its translated title |
|
|
35
35
|
| `BlockScaling` | `Full` / `Half` / `Wide` — the sizing vocabulary both platform families render |
|
|
36
36
|
| `usePanelNav(config)` | Headless navigation model behind a two-layer menu |
|
|
37
|
-
| `resolveNavLabel`,
|
|
37
|
+
| `navLabelHelper` → `.resolveNavLabel`, `.defaultNavLabel`, `.defaultNavTranslate` | Label resolution helpers |
|
|
38
38
|
| `PanelNavConfig`, `PanelNavSection`, `PanelNavItem`, `PanelNavLink`, `PanelNavModel`, `NavTranslate` | Navigation types |
|
|
39
39
|
| `FormProps`, `FormRef`, `FormOnSubmit`, `TFormContext`, `FormFieldProps`, `FormActionProps`, `FormActionRenderArgs`, `InputControllerProps`, `TPanelContext`, `StatusOptions` | The model shapes a renderer is written against |
|
|
40
40
|
|
|
41
41
|
## Subpath Exports
|
|
42
42
|
|
|
43
|
-
- `./auth` — `useLoginMethods` and the login model types
|
|
44
|
-
`
|
|
45
|
-
|
|
43
|
+
- `./auth` — `useLoginMethods`, `loginProviderHelper` and the login model types
|
|
44
|
+
(`LoginMethodsModel`, `LoginTermsModel`, `LoginCreditModel`, `LoginProviderConfig`,
|
|
45
|
+
`LoginProviderModel`, `LoginProviderPlacement`, `UseLoginMethodsOptions`). Importing it also
|
|
46
|
+
registers this package's own seven-language `client-panel-auth` bundle and the
|
|
47
|
+
`LoginScreenConfig.provider` augmentation of `@owlmeans/config` by side effect.
|
|
46
48
|
- `./auth/plugins` — the shapes an auth UI plugin is written against: `Ed22519BasicAuthUIPluginForm`
|
|
47
49
|
and its `Ed22519BasicAuthUIPluginFormSchema`.
|
|
48
50
|
|
|
@@ -55,13 +57,37 @@ same split the form and navigation models already use. See `login-methods`.
|
|
|
55
57
|
`@owlmeans/client-auth/login`) rather than a flat `urls: { terms, privacy, cookies? }` — that field
|
|
56
58
|
is gone; nothing in the repo read it outside this model and its one renderer, so it was dropped
|
|
57
59
|
rather than kept alongside the replacement. It also carries `revisedAt?` and `version`. A renderer
|
|
58
|
-
turns `documents`/`notices` into markup with `termsSentence`
|
|
59
|
-
by re-deriving link/label pairs itself.
|
|
60
|
-
|
|
61
|
-
`LoginTermsModel.deferred` mirrors `termsDeferred(ctx)`: true once a registered,
|
|
62
|
-
confirms the terms instead of this screen. `LoginMethodsModel.blocked` folds it in
|
|
63
|
-
(`required && !deferred && !accepted`) — a renderer never blocks on `required`/`accepted` alone,
|
|
64
|
-
|
|
60
|
+
turns `documents`/`notices` into markup with `loginTermsHelper.termsSentence`
|
|
61
|
+
(`@owlmeans/client-auth/login`), never by re-deriving link/label pairs itself.
|
|
62
|
+
|
|
63
|
+
`LoginTermsModel.deferred` mirrors `loginTermsHelper.termsDeferred(ctx)`: true once a registered,
|
|
64
|
+
bound `LoginStep` confirms the terms instead of this screen. `LoginMethodsModel.blocked` folds it in
|
|
65
|
+
(`required && !deferred && !accepted`) — a renderer never blocks on `required`/`accepted` alone, or
|
|
66
|
+
it would re-block a screen whose confirmation moved elsewhere.
|
|
67
|
+
|
|
68
|
+
### The provider disclosure — `LoginMethodsModel.provider`
|
|
69
|
+
|
|
70
|
+
A sign-in screen that a third party runs for an application (a generated target signing people in
|
|
71
|
+
through the platform's IAM) names both parties: who signs the person in, and on behalf of which
|
|
72
|
+
product. The rules live here, headless, so every renderer discloses the same thing:
|
|
73
|
+
|
|
74
|
+
- **Config** — `cfg.security.auth.login.provider?: LoginProviderConfig`
|
|
75
|
+
(`{ name, operator?, info?, placement?: 'top' | 'inline' }`), declared onto `@owlmeans/config`'s
|
|
76
|
+
`LoginScreenConfig` by module augmentation in `auth/login/provider-config.ts` (the
|
|
77
|
+
`client-auth/login/terms-config.ts` pattern), never by editing `@owlmeans/config`. The `./auth`
|
|
78
|
+
barrel re-exports it, so any import of `@owlmeans/client-panel/auth` loads the field; a program
|
|
79
|
+
that builds the literal without one adds `import type {} from '@owlmeans/client-panel/auth'`.
|
|
80
|
+
- **Model** — `loginProviderHelper.resolve(cfg, brand)` → `LoginProviderModel { name, operator,
|
|
81
|
+
product, info: string | null, placement }` or `null`. Null unless the provider `name` AND the
|
|
82
|
+
product (`cfg.brand.name`) are set — a relationship with one party missing is never shown. Blank
|
|
83
|
+
strings are unset (build-time env arrives as `''`); `operator` defaults to `name`; `placement` to
|
|
84
|
+
`'inline'` (anything but `'top'` is inline); `info` survives only as an absolute `http(s)` URL or
|
|
85
|
+
a root-relative path (`//host` and `/\` are refused). `useLoginMethods().provider` carries it.
|
|
86
|
+
- **Copy** — `loginProviderHelper.fill(template, model)` puts `{{provider}}`, `{{operator}}` and
|
|
87
|
+
`{{product}}` into an ALREADY-TRANSLATED sentence in one pass; names never travel as i18next
|
|
88
|
+
params. The keys (`login.provider.*`) and the markup belong to `web-panel`.
|
|
89
|
+
- The disclosure is independent of the credit line: `credit.poweredBy: false` never hides who
|
|
90
|
+
signs a person in.
|
|
65
91
|
|
|
66
92
|
## Usage
|
|
67
93
|
|
|
@@ -122,13 +148,14 @@ alias, or null), `active` (its section), `showSide`, `isSectionActive` / `isItem
|
|
|
122
148
|
- **`showSide` owns the one-screen rule.** It is false when the active section holds a single
|
|
123
149
|
screen — a second level offering the page you are already on is noise. A renderer asks the model;
|
|
124
150
|
it does not count items itself.
|
|
125
|
-
- **Label resolution never touches i18n implicitly.** `NavTranslate` is a
|
|
126
|
-
string` **prop**, defaulting to `defaultNavTranslate`,
|
|
127
|
-
without an i18n provider (`renderApp` from
|
|
128
|
-
reaches for the panel i18n context: the hook
|
|
129
|
-
`react-i18next` returns without an instance, and a
|
|
130
|
-
|
|
131
|
-
`translate
|
|
151
|
+
- **Label resolution never touches i18n implicitly.** `NavTranslate` is a
|
|
152
|
+
`(key, defaultValue) => string` **prop**, defaulting to `navLabelHelper.defaultNavTranslate`,
|
|
153
|
+
which returns the fallback. An app mounted without an i18n provider (`renderApp` from
|
|
154
|
+
`@owlmeans/web-client` mounts none) crashes if a menu reaches for the panel i18n context: the hook
|
|
155
|
+
dereferences `i18n.options` on the empty object `react-i18next` returns without an instance, and a
|
|
156
|
+
throw inside render blanks the whole app.
|
|
157
|
+
`navLabelHelper.resolveNavLabel(translate, label, key, alias)` applies the order — literal `label`
|
|
158
|
+
→ `translate(key, humanized)` → `.defaultNavLabel(alias)`, which humanizes the last alias segment
|
|
132
159
|
(`my-app:web:user-list` → `User list`). Default key families: `nav.<name>` for sections,
|
|
133
160
|
`modules.<alias>` for items and footer links.
|
|
134
161
|
- **Resolving the current screen needs two sources.** The router's `location.state.alias` is
|
package/build/auth/i18n.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i18nHelper } from '@owlmeans/i18n';
|
|
2
2
|
import en from './i18n/en.json' with { type: 'json' };
|
|
3
3
|
import pl from './i18n/pl.json' with { type: 'json' };
|
|
4
4
|
import ru from './i18n/ru.json' with { type: 'json' };
|
|
@@ -7,12 +7,12 @@ import uk from './i18n/uk.json' with { type: 'json' };
|
|
|
7
7
|
import es from './i18n/es.json' with { type: 'json' };
|
|
8
8
|
import de from './i18n/de.json' with { type: 'json' };
|
|
9
9
|
import fr from './i18n/fr.json' with { type: 'json' };
|
|
10
|
-
addI18nLib('en', 'client-panel-auth', en);
|
|
11
|
-
addI18nLib('pl', 'client-panel-auth', pl);
|
|
12
|
-
addI18nLib('ru', 'client-panel-auth', ru);
|
|
13
|
-
addI18nLib('be', 'client-panel-auth', be);
|
|
14
|
-
addI18nLib('uk', 'client-panel-auth', uk);
|
|
15
|
-
addI18nLib('es', 'client-panel-auth', es);
|
|
16
|
-
addI18nLib('de', 'client-panel-auth', de);
|
|
17
|
-
addI18nLib('fr', 'client-panel-auth', fr);
|
|
10
|
+
i18nHelper.addI18nLib('en', 'client-panel-auth', en);
|
|
11
|
+
i18nHelper.addI18nLib('pl', 'client-panel-auth', pl);
|
|
12
|
+
i18nHelper.addI18nLib('ru', 'client-panel-auth', ru);
|
|
13
|
+
i18nHelper.addI18nLib('be', 'client-panel-auth', be);
|
|
14
|
+
i18nHelper.addI18nLib('uk', 'client-panel-auth', uk);
|
|
15
|
+
i18nHelper.addI18nLib('es', 'client-panel-auth', es);
|
|
16
|
+
i18nHelper.addI18nLib('de', 'client-panel-auth', de);
|
|
17
|
+
i18nHelper.addI18nLib('fr', 'client-panel-auth', fr);
|
|
18
18
|
//# sourceMappingURL=i18n.js.map
|
package/build/auth/i18n.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/auth/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAErD,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/auth/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACrD,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAErD,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;AACpD,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;AACpD,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;AACpD,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;AACpD,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;AACpD,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;AACpD,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;AACpD,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA"}
|
|
@@ -3,8 +3,8 @@ import type { LoginMethodsModel, UseLoginMethodsOptions } from './types.js';
|
|
|
3
3
|
* Everything a sign-in screen needs, with no opinion about how it looks.
|
|
4
4
|
*
|
|
5
5
|
* Headless and cross-platform, like `usePanelNav`: the rules about what may be offered, what must
|
|
6
|
-
* be confirmed first
|
|
7
|
-
* rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
6
|
+
* be confirmed first, what the credit line says and who is disclosed as signing the person in are
|
|
7
|
+
* the same on every platform, and only the rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
8
8
|
*/
|
|
9
9
|
export declare const useLoginMethods: (opts?: UseLoginMethodsOptions) => LoginMethodsModel;
|
|
10
10
|
//# sourceMappingURL=model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAG3E;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,UAAW,sBAAsB,KAAG,iBAsF/D,CAAA"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState } from 'react';
|
|
2
2
|
import { useContext, useNavigate } from '@owlmeans/client';
|
|
3
|
-
import {
|
|
3
|
+
import { resolveCredit, LoginOutcome, LOGIN_SERVICE, loginMethodsHelper, loginTermsHelper } from '@owlmeans/client-auth/login';
|
|
4
|
+
import { loginProviderHelper } from './provider.js';
|
|
4
5
|
/**
|
|
5
6
|
* Everything a sign-in screen needs, with no opinion about how it looks.
|
|
6
7
|
*
|
|
7
8
|
* Headless and cross-platform, like `usePanelNav`: the rules about what may be offered, what must
|
|
8
|
-
* be confirmed first
|
|
9
|
-
* rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
9
|
+
* be confirmed first, what the credit line says and who is disclosed as signing the person in are
|
|
10
|
+
* the same on every platform, and only the rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
10
11
|
*/
|
|
11
12
|
export const useLoginMethods = (opts) => {
|
|
12
13
|
const context = useContext();
|
|
@@ -14,9 +15,10 @@ export const useLoginMethods = (opts) => {
|
|
|
14
15
|
const login = context.service(LOGIN_SERVICE);
|
|
15
16
|
const cfg = opts?.config ?? context.cfg.security?.auth?.login;
|
|
16
17
|
const brand = context.cfg.brand;
|
|
17
|
-
const resolved = useMemo(() => resolveTerms(opts?.terms ?? cfg?.terms), [opts?.terms, cfg]);
|
|
18
|
+
const resolved = useMemo(() => loginTermsHelper.resolveTerms(opts?.terms ?? cfg?.terms), [opts?.terms, cfg]);
|
|
18
19
|
const credit = useMemo(() => resolveCredit(cfg?.credit, brand, context.cfg.service), [cfg, brand, context]);
|
|
19
|
-
const
|
|
20
|
+
const provider = useMemo(() => loginProviderHelper.resolve(cfg?.provider, brand), [cfg, brand]);
|
|
21
|
+
const [accepted, setAccepted] = useState(() => loginTermsHelper.termsAccepted(resolved));
|
|
20
22
|
const [attempted, setAttempted] = useState(false);
|
|
21
23
|
const [busy, setBusy] = useState(null);
|
|
22
24
|
const [outcome, setOutcome] = useState(null);
|
|
@@ -29,7 +31,7 @@ export const useLoginMethods = (opts) => {
|
|
|
29
31
|
const methods = typeof opts?.methods === 'function'
|
|
30
32
|
? opts.methods(all)
|
|
31
33
|
: opts?.methods ?? all;
|
|
32
|
-
const deferred = termsDeferred(context);
|
|
34
|
+
const deferred = loginTermsHelper.termsDeferred(context);
|
|
33
35
|
const blocked = resolved != null && resolved.required && !deferred && !accepted;
|
|
34
36
|
const select = useCallback((method) => {
|
|
35
37
|
if (blocked) {
|
|
@@ -58,11 +60,11 @@ export const useLoginMethods = (opts) => {
|
|
|
58
60
|
const accept = useCallback((value) => {
|
|
59
61
|
setAccepted(value);
|
|
60
62
|
setAttempted(false);
|
|
61
|
-
acceptTerms(resolved, value);
|
|
63
|
+
loginTermsHelper.acceptTerms(resolved, value);
|
|
62
64
|
}, [resolved]);
|
|
63
65
|
return {
|
|
64
66
|
methods,
|
|
65
|
-
primary: primaryLoginMethod(methods),
|
|
67
|
+
primary: loginMethodsHelper.primaryLoginMethod(methods),
|
|
66
68
|
terms: {
|
|
67
69
|
required: resolved?.required ?? false,
|
|
68
70
|
accepted: resolved == null ? true : accepted,
|
|
@@ -75,6 +77,7 @@ export const useLoginMethods = (opts) => {
|
|
|
75
77
|
accept,
|
|
76
78
|
},
|
|
77
79
|
credit,
|
|
80
|
+
provider,
|
|
78
81
|
blocked,
|
|
79
82
|
busy,
|
|
80
83
|
outcome,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACtD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../../../../src/auth/login/provider/consts.local.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,eAAO,MAAM,oBAAoB,QAAyC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../../../../src/auth/login/provider/consts.local.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,sCAAsC,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { LoginProviderConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* `LoginScreenConfig.provider`, added by TypeScript module augmentation rather than by editing
|
|
4
|
+
* `@owlmeans/config` itself.
|
|
5
|
+
*
|
|
6
|
+
* `@owlmeans/config` has roughly 85 dependents, and every one of them sits in the same release
|
|
7
|
+
* closure — a config bump ripples through every package that pins a range against it, whether or
|
|
8
|
+
* not it touches sign-in. The field below is read by exactly one consumer family (the sign-in
|
|
9
|
+
* screen's headless model here and its renderers), so it is declared here instead, merged onto the
|
|
10
|
+
* base interface by TypeScript's own declaration merging — the pattern
|
|
11
|
+
* `@owlmeans/client-auth/login`'s `terms-config.ts` set.
|
|
12
|
+
*
|
|
13
|
+
* This file must be part of whatever a consumer's own compilation actually loads, or the merge
|
|
14
|
+
* never happens — an ambient augmentation applies only once the compiler has read the file that
|
|
15
|
+
* declares it. `@owlmeans/client-panel/auth`'s barrel re-exports this module for exactly that
|
|
16
|
+
* reason, so ANY import from `@owlmeans/client-panel/auth` pulls this augmentation into that
|
|
17
|
+
* program. An app that builds its `security.auth.login` literal in a file that imports nothing from
|
|
18
|
+
* it (a config file importing only `@owlmeans/config`) still benefits as long as some other file in
|
|
19
|
+
* the same `tsc` program does; if none does, add `import type {} from '@owlmeans/client-panel/auth'`
|
|
20
|
+
* to the file building the literal — an import with an empty type list, kept only to pull this
|
|
21
|
+
* file into that program's graph.
|
|
22
|
+
*
|
|
23
|
+
* `export {}` below is required, not decorative: without at least one top-level import/export this
|
|
24
|
+
* file is a SCRIPT to TypeScript, not a MODULE, and `declare module '@owlmeans/config'` inside a
|
|
25
|
+
* script augments the global scope instead of merging onto the named module's exports.
|
|
26
|
+
*/
|
|
27
|
+
declare module '@owlmeans/config' {
|
|
28
|
+
interface LoginScreenConfig {
|
|
29
|
+
/**
|
|
30
|
+
* Who signs a person in on this application's behalf — an identity service the application
|
|
31
|
+
* does not itself run. Absent, the screen discloses nothing and renders exactly as before.
|
|
32
|
+
*/
|
|
33
|
+
provider?: LoginProviderConfig;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=provider-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../../../src/auth/login/provider-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,QAAQ,kBAAkB,CAAC;IAChC,UAAU,iBAAiB;QACzB;;;WAGG;QACH,QAAQ,CAAC,EAAE,mBAAmB,CAAA;KAC/B;CACF;AAED,OAAO,EAAG,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-config.js","sourceRoot":"","sources":["../../../src/auth/login/provider-config.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/auth/login/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAuB,mBAAmB,EAAsB,MAAM,YAAY,CAAA;AAE9F,OAAO,sBAAsB,CAAA;AAE7B,eAAO,MAAM,yBAAyB,QAAO,mBAyD5C,CAAA;AAED,eAAO,MAAM,mBAAmB,qBAA8B,CAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { PROVIDER_PLACEHOLDER } from './provider/consts.local.js';
|
|
2
|
+
import './provider-config.js';
|
|
3
|
+
export const createLoginProviderHelper = () => {
|
|
4
|
+
/**
|
|
5
|
+
* A configured value that was actually SET — `''` is how a build-time default spells "unset"
|
|
6
|
+
* (see `resolveCredit` in `@owlmeans/client-auth/login`, which follows the same rule).
|
|
7
|
+
*/
|
|
8
|
+
const set = (value) => typeof value === 'string' && value.trim() !== '' ? value.trim() : null;
|
|
9
|
+
/**
|
|
10
|
+
* A link a person may be sent to: an absolute http(s) URL, or a path on this origin.
|
|
11
|
+
*
|
|
12
|
+
* `//host` is protocol-relative — another origin wearing a path's clothes — and `/\` is read as
|
|
13
|
+
* the same by browsers, so both are refused along with every other scheme (`javascript:` above
|
|
14
|
+
* all, since this lands in an `href`).
|
|
15
|
+
*/
|
|
16
|
+
const link = (value) => {
|
|
17
|
+
const href = set(value);
|
|
18
|
+
if (href == null) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (href.startsWith('/')) {
|
|
22
|
+
return /^\/[/\\]/.test(href) ? null : href;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(href);
|
|
26
|
+
return url.protocol === 'https:' || url.protocol === 'http:' ? href : null;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const resolve = (cfg, brand) => {
|
|
33
|
+
const name = set(cfg?.name);
|
|
34
|
+
const product = set(brand?.name);
|
|
35
|
+
if (name == null || product == null) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
name,
|
|
40
|
+
operator: set(cfg?.operator) ?? name,
|
|
41
|
+
product,
|
|
42
|
+
info: link(cfg?.info),
|
|
43
|
+
placement: cfg?.placement === 'top' ? 'top' : 'inline',
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const fill = (template, model) => {
|
|
47
|
+
const values = {
|
|
48
|
+
provider: model.name, operator: model.operator, product: model.product,
|
|
49
|
+
};
|
|
50
|
+
return template.replace(PROVIDER_PLACEHOLDER, (match, key) => values[key] ?? match);
|
|
51
|
+
};
|
|
52
|
+
return { resolve, fill };
|
|
53
|
+
};
|
|
54
|
+
export const loginProviderHelper = createLoginProviderHelper();
|
|
55
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/auth/login/provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACjE,OAAO,sBAAsB,CAAA;AAE7B,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAwB,EAAE;IACjE;;;OAGG;IACH,MAAM,GAAG,GAAG,CAAC,KAAqB,EAAiB,EAAE,CACnD,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAExE;;;;;;OAMG;IACH,MAAM,IAAI,GAAG,CAAC,KAAqB,EAAiB,EAAE;QACpD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAA;QACvB,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5C,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;YAEzB,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC,CAAA;IAED,MAAM,OAAO,GAAG,CAAC,GAAyB,EAAE,KAAqB,EAA6B,EAAE;QAC9F,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAChC,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;YACpC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO;YACL,IAAI;YACJ,QAAQ,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,IAAI;YACpC,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC;YACrB,SAAS,EAAE,GAAG,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;SACvD,CAAA;IACH,CAAC,CAAA;IAED,MAAM,IAAI,GAAG,CAAC,QAAgB,EAAE,KAAyB,EAAU,EAAE;QACnE,MAAM,MAAM,GAA2B;YACrC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO;SACvE,CAAA;QAED,OAAO,QAAQ,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAA;IAC7F,CAAC,CAAA;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,yBAAyB,EAAE,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LoginMethod, LoginOutcome, ResolvedTermsDocument } from '@owlmeans/client-auth/login';
|
|
2
|
-
import type { LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config';
|
|
2
|
+
import type { BrandSettings, LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config';
|
|
3
3
|
export interface UseLoginMethodsOptions {
|
|
4
4
|
/** Overrides the configuration the context carries. */
|
|
5
5
|
config?: LoginScreenConfig;
|
|
@@ -33,12 +33,59 @@ export interface LoginCreditModel {
|
|
|
33
33
|
poweredBy: boolean;
|
|
34
34
|
line: string | null;
|
|
35
35
|
}
|
|
36
|
+
/** Where the provider disclosure sits: a strip above the card, or a line inside it. */
|
|
37
|
+
export type LoginProviderPlacement = 'top' | 'inline';
|
|
38
|
+
/**
|
|
39
|
+
* Who actually signs a person in, when that is not the application on whose screen they are.
|
|
40
|
+
*
|
|
41
|
+
* Read from `cfg.security.auth.login.provider` (declared onto `LoginScreenConfig` by
|
|
42
|
+
* `./provider-config.ts`). Blank strings are unset, exactly as they are for the credit line: this
|
|
43
|
+
* config is filled from build-time environment variables, and an undelivered one arrives as `''`.
|
|
44
|
+
*/
|
|
45
|
+
export interface LoginProviderConfig {
|
|
46
|
+
/** The sign-in service as a person reads it — "OwlMeans IAM". */
|
|
47
|
+
name: string;
|
|
48
|
+
/** Who runs it. Defaults to `name`. */
|
|
49
|
+
operator?: string;
|
|
50
|
+
/** "More information" — an absolute http(s) URL or a root-relative path; anything else is dropped. */
|
|
51
|
+
info?: string;
|
|
52
|
+
/** Default `'inline'`. */
|
|
53
|
+
placement?: LoginProviderPlacement;
|
|
54
|
+
}
|
|
55
|
+
export interface LoginProviderModel {
|
|
56
|
+
name: string;
|
|
57
|
+
operator: string;
|
|
58
|
+
/** The application the person is signing in to — `cfg.brand.name`. */
|
|
59
|
+
product: string;
|
|
60
|
+
info: string | null;
|
|
61
|
+
placement: LoginProviderPlacement;
|
|
62
|
+
}
|
|
63
|
+
export interface LoginProviderHelper {
|
|
64
|
+
/**
|
|
65
|
+
* The disclosure a sign-in screen renders, or `null` when there is nothing to disclose.
|
|
66
|
+
*
|
|
67
|
+
* Null unless both the provider's `name` and the product (`brand.name`) are set — a sentence
|
|
68
|
+
* relating two parties cannot be written with one of them missing, and a screen that showed half
|
|
69
|
+
* of it would be worse than one that shows none.
|
|
70
|
+
*/
|
|
71
|
+
resolve: (cfg?: LoginProviderConfig, brand?: BrandSettings) => LoginProviderModel | null;
|
|
72
|
+
/**
|
|
73
|
+
* Put the names into a sentence AFTER it was translated.
|
|
74
|
+
*
|
|
75
|
+
* `{{provider}}`, `{{operator}}` and `{{product}}`, replaced in one pass — a name that itself
|
|
76
|
+
* contains a placeholder is never expanded a second time. The names never travel as i18next
|
|
77
|
+
* interpolation parameters, so no translation backend gets the chance to escape or format them.
|
|
78
|
+
*/
|
|
79
|
+
fill: (template: string, model: LoginProviderModel) => string;
|
|
80
|
+
}
|
|
36
81
|
export interface LoginMethodsModel {
|
|
37
82
|
methods: LoginMethod[];
|
|
38
83
|
/** The one a screen highlights and focuses. It is never started automatically. */
|
|
39
84
|
primary: LoginMethod | null;
|
|
40
85
|
terms: LoginTermsModel;
|
|
41
86
|
credit: LoginCreditModel;
|
|
87
|
+
/** Who signs the person in, on behalf of which product — `null` when the app configured nobody. */
|
|
88
|
+
provider: LoginProviderModel | null;
|
|
42
89
|
/** True while the terms have not been confirmed and confirming them is required. */
|
|
43
90
|
blocked: boolean;
|
|
44
91
|
/** Id of the method currently starting. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/auth/login/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnG,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/auth/login/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnG,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE1F,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,KAAK,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAA;IAChC,qDAAqD;IACrD,OAAO,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC,CAAA;CACtE;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAA;IAClB,8FAA8F;IAC9F,SAAS,EAAE,qBAAqB,EAAE,CAAA;IAClC,6FAA6F;IAC7F,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,uFAAuF;AACvF,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,QAAQ,CAAA;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sGAAsG;IACtG,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0BAA0B;IAC1B,SAAS,CAAC,EAAE,sBAAsB,CAAA;CACnC;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,SAAS,EAAE,sBAAsB,CAAA;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,mBAAmB,EAAE,KAAK,CAAC,EAAE,aAAa,KAAK,kBAAkB,GAAG,IAAI,CAAA;IACxF;;;;;;OAMG;IACH,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,MAAM,CAAA;CAC9D;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,kFAAkF;IAClF,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B,KAAK,EAAE,eAAe,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;IACxB,mGAAmG;IACnG,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACnC,oFAAoF;IACpF,OAAO,EAAE,OAAO,CAAA;IAChB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;CACtC"}
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
/** Default resolver: no i18n, the caller's fallback wins. See {@link NavTranslate}. */
|
|
3
|
-
export declare const defaultNavTranslate: NavTranslate;
|
|
4
|
-
/**
|
|
5
|
-
* Turn an alias into something readable — `my-app:web:user-list` becomes `User list`.
|
|
6
|
-
* The last segment is the meaningful one; the prefixes address the app, not the screen.
|
|
7
|
-
*/
|
|
8
|
-
export declare const defaultNavLabel: (alias: string) => string;
|
|
9
|
-
export declare const resolveNavLabel: (translate: NavTranslate, label: string | undefined, key: string, alias?: string) => string;
|
|
1
|
+
import type { PanelNavConfig, PanelNavModel } from './types.js';
|
|
10
2
|
/**
|
|
11
3
|
* The navigation model behind both menus.
|
|
12
4
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../src/components/nav/helper.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../src/components/nav/helper.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAgB,aAAa,EAAmB,MAAM,YAAY,CAAA;AAQ9F;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,WAAY,cAAc,KAAG,aA6FpD,CAAA"}
|
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { useContext, useNavigate } from '@owlmeans/client';
|
|
3
|
-
/** Default resolver: no i18n, the caller's fallback wins. See {@link NavTranslate}. */
|
|
4
|
-
export const defaultNavTranslate = (_key, defaultValue) => defaultValue;
|
|
5
|
-
/**
|
|
6
|
-
* Turn an alias into something readable — `my-app:web:user-list` becomes `User list`.
|
|
7
|
-
* The last segment is the meaningful one; the prefixes address the app, not the screen.
|
|
8
|
-
*/
|
|
9
|
-
export const defaultNavLabel = (alias) => {
|
|
10
|
-
const segment = alias.split(/[:.]/).filter(part => part !== '').pop() ?? alias;
|
|
11
|
-
const words = segment.replace(/[-_]+/g, ' ').trim();
|
|
12
|
-
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
13
|
-
};
|
|
14
|
-
export const resolveNavLabel = (translate, label, key, alias) => label ?? translate(key, defaultNavLabel(alias ?? key));
|
|
15
3
|
const visibleItems = (section) => section.items.filter(item => item.hidden !== true);
|
|
16
4
|
const normalizePath = (path) => path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path;
|
|
17
5
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../../../src/components/nav/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAI/B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAI1D,
|
|
1
|
+
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../../../src/components/nav/helper.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/nav/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAE/B,cAAc,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/nav/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAE/B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/nav/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/nav/index.ts"],"names":[],"mappings":"AAEA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../src/components/nav/label.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,YAAY,CAAA;AAE9D,eAAO,MAAM,oBAAoB,QAAO,cAevC,CAAA;AAED,eAAO,MAAM,cAAc,gBAAyB,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const createNavLabelHelper = () => {
|
|
2
|
+
const defaultNavTranslate = (_key, defaultValue) => defaultValue;
|
|
3
|
+
const defaultNavLabel = (alias) => {
|
|
4
|
+
const segment = alias.split(/[:.]/).filter(part => part !== '').pop() ?? alias;
|
|
5
|
+
const words = segment.replace(/[-_]+/g, ' ').trim();
|
|
6
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
7
|
+
};
|
|
8
|
+
const resolveNavLabel = (translate, label, key, alias) => label ?? translate(key, defaultNavLabel(alias ?? key));
|
|
9
|
+
return { defaultNavTranslate, defaultNavLabel, resolveNavLabel };
|
|
10
|
+
};
|
|
11
|
+
export const navLabelHelper = createNavLabelHelper();
|
|
12
|
+
//# sourceMappingURL=label.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label.js","sourceRoot":"","sources":["../../../src/components/nav/label.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAmB,EAAE;IACvD,MAAM,mBAAmB,GAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,CAAC,YAAY,CAAA;IAE9E,MAAM,eAAe,GAAG,CAAC,KAAa,EAAU,EAAE;QAChD,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,CAAA;QAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;QAEnD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACvD,CAAC,CAAA;IAED,MAAM,eAAe,GAAG,CACtB,SAAuB,EAAE,KAAyB,EAAE,GAAW,EAAE,KAAc,EACvE,EAAE,CAAC,KAAK,IAAI,SAAS,CAAC,GAAG,EAAE,eAAe,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAA;IAEnE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,CAAA;AAClE,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,oBAAoB,EAAE,CAAA"}
|
|
@@ -73,4 +73,16 @@ export interface PanelNavModel {
|
|
|
73
73
|
*/
|
|
74
74
|
hrefOf: (target: PanelNavItem | PanelNavSection) => string | undefined;
|
|
75
75
|
}
|
|
76
|
+
/** How a navigation entry gets its label. */
|
|
77
|
+
export interface NavLabelHelper {
|
|
78
|
+
/** Default resolver: no i18n, the caller's fallback wins. See {@link NavTranslate}. */
|
|
79
|
+
defaultNavTranslate: NavTranslate;
|
|
80
|
+
/**
|
|
81
|
+
* Turn an alias into something readable — `my-app:web:user-list` becomes `User list`.
|
|
82
|
+
* The last segment is the meaningful one; the prefixes address the app, not the screen.
|
|
83
|
+
*/
|
|
84
|
+
defaultNavLabel: (alias: string) => string;
|
|
85
|
+
/** The literal label when given, else the translation of `key` falling back to the humanized alias. */
|
|
86
|
+
resolveNavLabel: (translate: NavTranslate, label: string | undefined, key: string, alias?: string) => string;
|
|
87
|
+
}
|
|
76
88
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/nav/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,4GAA4G;IAC5G,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAA;CAC5B;AAED,uFAAuF;AACvF,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yBAAyB;IACzB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,yEAAyE;IACzE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,eAAe,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAA;IACtD,YAAY,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAA;IAC7C,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,IAAI,CAAA;IACnD,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,MAAM,IAAI,CAAA;IAC1C;;;;;;;OAOG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,YAAY,GAAG,eAAe,KAAK,MAAM,GAAG,SAAS,CAAA;CACvE"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/nav/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,4GAA4G;IAC5G,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAA;CAC5B;AAED,uFAAuF;AACvF,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yBAAyB;IACzB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,yEAAyE;IACzE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,eAAe,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAA;IACtD,YAAY,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAA;IAC7C,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,IAAI,CAAA;IACnD,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,MAAM,IAAI,CAAA;IAC1C;;;;;;;OAOG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,YAAY,GAAG,eAAe,KAAK,MAAM,GAAG,SAAS,CAAA;CACvE;AAED,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,uFAAuF;IACvF,mBAAmB,EAAE,YAAY,CAAA;IACjC;;;OAGG;IACH,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1C,uGAAuG;IACvG,eAAe,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAC7G"}
|
package/build/i18n.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i18nHelper } from '@owlmeans/i18n';
|
|
2
2
|
import { buttons } from './consts.local.js';
|
|
3
3
|
for (const [language, values] of Object.entries(buttons)) {
|
|
4
|
-
addI18nLib(language, 'client-panel', values);
|
|
4
|
+
i18nHelper.addI18nLib(language, 'client-panel', values);
|
|
5
5
|
}
|
|
6
6
|
//# sourceMappingURL=i18n.js.map
|
package/build/i18n.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE3C,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;IACzD,UAAU,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE3C,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;IACzD,UAAU,CAAC,UAAU,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,CAAC,CAAA;AACzD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/client-panel",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.59",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -36,19 +36,19 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@hookform/resolvers": "^5.9.1",
|
|
39
|
-
"@owlmeans/client": "^0.1.18-rc.
|
|
40
|
-
"@owlmeans/client-i18n": "^0.1.18-rc.
|
|
41
|
-
"@owlmeans/client-entrypoint": "^0.1.18-rc.
|
|
42
|
-
"@owlmeans/client-route": "^0.1.18-rc.
|
|
43
|
-
"@owlmeans/error": "^0.1.18-rc.
|
|
44
|
-
"@owlmeans/entrypoint": "^0.1.18-rc.
|
|
39
|
+
"@owlmeans/client": "^0.1.18-rc.52",
|
|
40
|
+
"@owlmeans/client-i18n": "^0.1.18-rc.51",
|
|
41
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.48",
|
|
42
|
+
"@owlmeans/client-route": "^0.1.18-rc.49",
|
|
43
|
+
"@owlmeans/error": "^0.1.18-rc.38",
|
|
44
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.41"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
48
|
-
"@owlmeans/client-auth": "^0.1.18-rc.
|
|
49
|
-
"@owlmeans/config": "^0.1.18-rc.
|
|
50
|
-
"@owlmeans/i18n": "^0.1.18-rc.
|
|
51
|
-
"@owlmeans/router": "^0.1.18-rc.
|
|
47
|
+
"@owlmeans/auth": "^0.1.18-rc.40",
|
|
48
|
+
"@owlmeans/client-auth": "^0.1.18-rc.59",
|
|
49
|
+
"@owlmeans/config": "^0.1.18-rc.44",
|
|
50
|
+
"@owlmeans/i18n": "^0.1.18-rc.39",
|
|
51
|
+
"@owlmeans/router": "^0.1.18-rc.33",
|
|
52
52
|
"ajv": "*",
|
|
53
53
|
"react": "*",
|
|
54
54
|
"react-hook-form": "*"
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@owlmeans/dep-config": "workspace:*",
|
|
66
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
67
|
-
"@owlmeans/client-auth": "^0.1.18-rc.
|
|
66
|
+
"@owlmeans/auth": "^0.1.18-rc.40",
|
|
67
|
+
"@owlmeans/client-auth": "^0.1.18-rc.59",
|
|
68
68
|
"@types/react": "^19.3.0",
|
|
69
69
|
"nodemon": "^3.1.14",
|
|
70
70
|
"react-hook-form": "^7.89.0",
|
package/src/auth/i18n.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i18nHelper } from '@owlmeans/i18n'
|
|
2
2
|
|
|
3
3
|
import en from './i18n/en.json' with { type: 'json' }
|
|
4
4
|
import pl from './i18n/pl.json' with { type: 'json' }
|
|
@@ -9,11 +9,11 @@ import es from './i18n/es.json' with { type: 'json' }
|
|
|
9
9
|
import de from './i18n/de.json' with { type: 'json' }
|
|
10
10
|
import fr from './i18n/fr.json' with { type: 'json' }
|
|
11
11
|
|
|
12
|
-
addI18nLib('en', 'client-panel-auth', en)
|
|
13
|
-
addI18nLib('pl', 'client-panel-auth', pl)
|
|
14
|
-
addI18nLib('ru', 'client-panel-auth', ru)
|
|
15
|
-
addI18nLib('be', 'client-panel-auth', be)
|
|
16
|
-
addI18nLib('uk', 'client-panel-auth', uk)
|
|
17
|
-
addI18nLib('es', 'client-panel-auth', es)
|
|
18
|
-
addI18nLib('de', 'client-panel-auth', de)
|
|
19
|
-
addI18nLib('fr', 'client-panel-auth', fr)
|
|
12
|
+
i18nHelper.addI18nLib('en', 'client-panel-auth', en)
|
|
13
|
+
i18nHelper.addI18nLib('pl', 'client-panel-auth', pl)
|
|
14
|
+
i18nHelper.addI18nLib('ru', 'client-panel-auth', ru)
|
|
15
|
+
i18nHelper.addI18nLib('be', 'client-panel-auth', be)
|
|
16
|
+
i18nHelper.addI18nLib('uk', 'client-panel-auth', uk)
|
|
17
|
+
i18nHelper.addI18nLib('es', 'client-panel-auth', es)
|
|
18
|
+
i18nHelper.addI18nLib('de', 'client-panel-auth', de)
|
|
19
|
+
i18nHelper.addI18nLib('fr', 'client-panel-auth', fr)
|
package/src/auth/login/index.ts
CHANGED
package/src/auth/login/model.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState } from 'react'
|
|
2
2
|
import { useContext, useNavigate } from '@owlmeans/client'
|
|
3
3
|
import type { CommonConfig } from '@owlmeans/config'
|
|
4
|
-
import {
|
|
5
|
-
acceptTerms, primaryLoginMethod, resolveCredit, resolveTerms, termsAccepted, termsDeferred,
|
|
6
|
-
LoginOutcome, LOGIN_SERVICE,
|
|
7
|
-
} from '@owlmeans/client-auth/login'
|
|
4
|
+
import { resolveCredit, LoginOutcome, LOGIN_SERVICE, loginMethodsHelper, loginTermsHelper } from '@owlmeans/client-auth/login'
|
|
8
5
|
import type { LoginContext, LoginMethod, LoginService } from '@owlmeans/client-auth/login'
|
|
9
6
|
import type { LoginMethodsModel, UseLoginMethodsOptions } from './types.js'
|
|
7
|
+
import { loginProviderHelper } from './provider.js'
|
|
10
8
|
|
|
11
9
|
/**
|
|
12
10
|
* Everything a sign-in screen needs, with no opinion about how it looks.
|
|
13
11
|
*
|
|
14
12
|
* Headless and cross-platform, like `usePanelNav`: the rules about what may be offered, what must
|
|
15
|
-
* be confirmed first
|
|
16
|
-
* rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
13
|
+
* be confirmed first, what the credit line says and who is disclosed as signing the person in are
|
|
14
|
+
* the same on every platform, and only the rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
17
15
|
*/
|
|
18
16
|
export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsModel => {
|
|
19
17
|
const context = useContext() as unknown as LoginContext
|
|
@@ -23,12 +21,13 @@ export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsMode
|
|
|
23
21
|
const cfg = opts?.config ?? (context.cfg as CommonConfig).security?.auth?.login
|
|
24
22
|
const brand = (context.cfg as CommonConfig).brand
|
|
25
23
|
|
|
26
|
-
const resolved = useMemo(() => resolveTerms(opts?.terms ?? cfg?.terms), [opts?.terms, cfg])
|
|
24
|
+
const resolved = useMemo(() => loginTermsHelper.resolveTerms(opts?.terms ?? cfg?.terms), [opts?.terms, cfg])
|
|
27
25
|
const credit = useMemo(
|
|
28
26
|
() => resolveCredit(cfg?.credit, brand, context.cfg.service), [cfg, brand, context]
|
|
29
27
|
)
|
|
28
|
+
const provider = useMemo(() => loginProviderHelper.resolve(cfg?.provider, brand), [cfg, brand])
|
|
30
29
|
|
|
31
|
-
const [accepted, setAccepted] = useState(() => termsAccepted(resolved))
|
|
30
|
+
const [accepted, setAccepted] = useState(() => loginTermsHelper.termsAccepted(resolved))
|
|
32
31
|
const [attempted, setAttempted] = useState(false)
|
|
33
32
|
const [busy, setBusy] = useState<string | null>(null)
|
|
34
33
|
const [outcome, setOutcome] = useState<LoginOutcome | null>(null)
|
|
@@ -43,7 +42,7 @@ export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsMode
|
|
|
43
42
|
? opts.methods(all)
|
|
44
43
|
: opts?.methods ?? all
|
|
45
44
|
|
|
46
|
-
const deferred = termsDeferred(context)
|
|
45
|
+
const deferred = loginTermsHelper.termsDeferred(context)
|
|
47
46
|
const blocked = resolved != null && resolved.required && !deferred && !accepted
|
|
48
47
|
|
|
49
48
|
const select = useCallback((method: LoginMethod) => {
|
|
@@ -75,12 +74,12 @@ export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsMode
|
|
|
75
74
|
const accept = useCallback((value: boolean) => {
|
|
76
75
|
setAccepted(value)
|
|
77
76
|
setAttempted(false)
|
|
78
|
-
acceptTerms(resolved, value)
|
|
77
|
+
loginTermsHelper.acceptTerms(resolved, value)
|
|
79
78
|
}, [resolved])
|
|
80
79
|
|
|
81
80
|
return {
|
|
82
81
|
methods,
|
|
83
|
-
primary: primaryLoginMethod(methods),
|
|
82
|
+
primary: loginMethodsHelper.primaryLoginMethod(methods),
|
|
84
83
|
terms: {
|
|
85
84
|
required: resolved?.required ?? false,
|
|
86
85
|
accepted: resolved == null ? true : accepted,
|
|
@@ -93,6 +92,7 @@ export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsMode
|
|
|
93
92
|
accept,
|
|
94
93
|
},
|
|
95
94
|
credit,
|
|
95
|
+
provider,
|
|
96
96
|
blocked,
|
|
97
97
|
busy,
|
|
98
98
|
outcome,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { LoginProviderConfig } from './types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `LoginScreenConfig.provider`, added by TypeScript module augmentation rather than by editing
|
|
5
|
+
* `@owlmeans/config` itself.
|
|
6
|
+
*
|
|
7
|
+
* `@owlmeans/config` has roughly 85 dependents, and every one of them sits in the same release
|
|
8
|
+
* closure — a config bump ripples through every package that pins a range against it, whether or
|
|
9
|
+
* not it touches sign-in. The field below is read by exactly one consumer family (the sign-in
|
|
10
|
+
* screen's headless model here and its renderers), so it is declared here instead, merged onto the
|
|
11
|
+
* base interface by TypeScript's own declaration merging — the pattern
|
|
12
|
+
* `@owlmeans/client-auth/login`'s `terms-config.ts` set.
|
|
13
|
+
*
|
|
14
|
+
* This file must be part of whatever a consumer's own compilation actually loads, or the merge
|
|
15
|
+
* never happens — an ambient augmentation applies only once the compiler has read the file that
|
|
16
|
+
* declares it. `@owlmeans/client-panel/auth`'s barrel re-exports this module for exactly that
|
|
17
|
+
* reason, so ANY import from `@owlmeans/client-panel/auth` pulls this augmentation into that
|
|
18
|
+
* program. An app that builds its `security.auth.login` literal in a file that imports nothing from
|
|
19
|
+
* it (a config file importing only `@owlmeans/config`) still benefits as long as some other file in
|
|
20
|
+
* the same `tsc` program does; if none does, add `import type {} from '@owlmeans/client-panel/auth'`
|
|
21
|
+
* to the file building the literal — an import with an empty type list, kept only to pull this
|
|
22
|
+
* file into that program's graph.
|
|
23
|
+
*
|
|
24
|
+
* `export {}` below is required, not decorative: without at least one top-level import/export this
|
|
25
|
+
* file is a SCRIPT to TypeScript, not a MODULE, and `declare module '@owlmeans/config'` inside a
|
|
26
|
+
* script augments the global scope instead of merging onto the named module's exports.
|
|
27
|
+
*/
|
|
28
|
+
declare module '@owlmeans/config' {
|
|
29
|
+
interface LoginScreenConfig {
|
|
30
|
+
/**
|
|
31
|
+
* Who signs a person in on this application's behalf — an identity service the application
|
|
32
|
+
* does not itself run. Absent, the screen discloses nothing and renders exactly as before.
|
|
33
|
+
*/
|
|
34
|
+
provider?: LoginProviderConfig
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { BrandSettings } from '@owlmeans/config'
|
|
2
|
+
import type { LoginProviderConfig, LoginProviderHelper, LoginProviderModel } from './types.js'
|
|
3
|
+
import { PROVIDER_PLACEHOLDER } from './provider/consts.local.js'
|
|
4
|
+
import './provider-config.js'
|
|
5
|
+
|
|
6
|
+
export const createLoginProviderHelper = (): LoginProviderHelper => {
|
|
7
|
+
/**
|
|
8
|
+
* A configured value that was actually SET — `''` is how a build-time default spells "unset"
|
|
9
|
+
* (see `resolveCredit` in `@owlmeans/client-auth/login`, which follows the same rule).
|
|
10
|
+
*/
|
|
11
|
+
const set = (value?: string | null): string | null =>
|
|
12
|
+
typeof value === 'string' && value.trim() !== '' ? value.trim() : null
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A link a person may be sent to: an absolute http(s) URL, or a path on this origin.
|
|
16
|
+
*
|
|
17
|
+
* `//host` is protocol-relative — another origin wearing a path's clothes — and `/\` is read as
|
|
18
|
+
* the same by browsers, so both are refused along with every other scheme (`javascript:` above
|
|
19
|
+
* all, since this lands in an `href`).
|
|
20
|
+
*/
|
|
21
|
+
const link = (value?: string | null): string | null => {
|
|
22
|
+
const href = set(value)
|
|
23
|
+
if (href == null) {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
if (href.startsWith('/')) {
|
|
27
|
+
return /^\/[/\\]/.test(href) ? null : href
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const url = new URL(href)
|
|
31
|
+
|
|
32
|
+
return url.protocol === 'https:' || url.protocol === 'http:' ? href : null
|
|
33
|
+
} catch {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const resolve = (cfg?: LoginProviderConfig, brand?: BrandSettings): LoginProviderModel | null => {
|
|
39
|
+
const name = set(cfg?.name)
|
|
40
|
+
const product = set(brand?.name)
|
|
41
|
+
if (name == null || product == null) {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
operator: set(cfg?.operator) ?? name,
|
|
48
|
+
product,
|
|
49
|
+
info: link(cfg?.info),
|
|
50
|
+
placement: cfg?.placement === 'top' ? 'top' : 'inline',
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const fill = (template: string, model: LoginProviderModel): string => {
|
|
55
|
+
const values: Record<string, string> = {
|
|
56
|
+
provider: model.name, operator: model.operator, product: model.product,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return template.replace(PROVIDER_PLACEHOLDER, (match, key: string) => values[key] ?? match)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { resolve, fill }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const loginProviderHelper = createLoginProviderHelper()
|
package/src/auth/login/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LoginMethod, LoginOutcome, ResolvedTermsDocument } from '@owlmeans/client-auth/login'
|
|
2
|
-
import type { LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config'
|
|
2
|
+
import type { BrandSettings, LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config'
|
|
3
3
|
|
|
4
4
|
export interface UseLoginMethodsOptions {
|
|
5
5
|
/** Overrides the configuration the context carries. */
|
|
@@ -37,12 +37,63 @@ export interface LoginCreditModel {
|
|
|
37
37
|
line: string | null
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/** Where the provider disclosure sits: a strip above the card, or a line inside it. */
|
|
41
|
+
export type LoginProviderPlacement = 'top' | 'inline'
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Who actually signs a person in, when that is not the application on whose screen they are.
|
|
45
|
+
*
|
|
46
|
+
* Read from `cfg.security.auth.login.provider` (declared onto `LoginScreenConfig` by
|
|
47
|
+
* `./provider-config.ts`). Blank strings are unset, exactly as they are for the credit line: this
|
|
48
|
+
* config is filled from build-time environment variables, and an undelivered one arrives as `''`.
|
|
49
|
+
*/
|
|
50
|
+
export interface LoginProviderConfig {
|
|
51
|
+
/** The sign-in service as a person reads it — "OwlMeans IAM". */
|
|
52
|
+
name: string
|
|
53
|
+
/** Who runs it. Defaults to `name`. */
|
|
54
|
+
operator?: string
|
|
55
|
+
/** "More information" — an absolute http(s) URL or a root-relative path; anything else is dropped. */
|
|
56
|
+
info?: string
|
|
57
|
+
/** Default `'inline'`. */
|
|
58
|
+
placement?: LoginProviderPlacement
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface LoginProviderModel {
|
|
62
|
+
name: string
|
|
63
|
+
operator: string
|
|
64
|
+
/** The application the person is signing in to — `cfg.brand.name`. */
|
|
65
|
+
product: string
|
|
66
|
+
info: string | null
|
|
67
|
+
placement: LoginProviderPlacement
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface LoginProviderHelper {
|
|
71
|
+
/**
|
|
72
|
+
* The disclosure a sign-in screen renders, or `null` when there is nothing to disclose.
|
|
73
|
+
*
|
|
74
|
+
* Null unless both the provider's `name` and the product (`brand.name`) are set — a sentence
|
|
75
|
+
* relating two parties cannot be written with one of them missing, and a screen that showed half
|
|
76
|
+
* of it would be worse than one that shows none.
|
|
77
|
+
*/
|
|
78
|
+
resolve: (cfg?: LoginProviderConfig, brand?: BrandSettings) => LoginProviderModel | null
|
|
79
|
+
/**
|
|
80
|
+
* Put the names into a sentence AFTER it was translated.
|
|
81
|
+
*
|
|
82
|
+
* `{{provider}}`, `{{operator}}` and `{{product}}`, replaced in one pass — a name that itself
|
|
83
|
+
* contains a placeholder is never expanded a second time. The names never travel as i18next
|
|
84
|
+
* interpolation parameters, so no translation backend gets the chance to escape or format them.
|
|
85
|
+
*/
|
|
86
|
+
fill: (template: string, model: LoginProviderModel) => string
|
|
87
|
+
}
|
|
88
|
+
|
|
40
89
|
export interface LoginMethodsModel {
|
|
41
90
|
methods: LoginMethod[]
|
|
42
91
|
/** The one a screen highlights and focuses. It is never started automatically. */
|
|
43
92
|
primary: LoginMethod | null
|
|
44
93
|
terms: LoginTermsModel
|
|
45
94
|
credit: LoginCreditModel
|
|
95
|
+
/** Who signs the person in, on behalf of which product — `null` when the app configured nobody. */
|
|
96
|
+
provider: LoginProviderModel | null
|
|
46
97
|
/** True while the terms have not been confirmed and confirming them is required. */
|
|
47
98
|
blocked: boolean
|
|
48
99
|
/** Id of the method currently starting. */
|
|
@@ -4,25 +4,7 @@ import type { ClientEntrypoint } from '@owlmeans/client-entrypoint'
|
|
|
4
4
|
import type { Location } from '@owlmeans/router'
|
|
5
5
|
import { useContext, useNavigate } from '@owlmeans/client'
|
|
6
6
|
|
|
7
|
-
import type {
|
|
8
|
-
|
|
9
|
-
/** Default resolver: no i18n, the caller's fallback wins. See {@link NavTranslate}. */
|
|
10
|
-
export const defaultNavTranslate: NavTranslate = (_key, defaultValue) => defaultValue
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Turn an alias into something readable — `my-app:web:user-list` becomes `User list`.
|
|
14
|
-
* The last segment is the meaningful one; the prefixes address the app, not the screen.
|
|
15
|
-
*/
|
|
16
|
-
export const defaultNavLabel = (alias: string): string => {
|
|
17
|
-
const segment = alias.split(/[:.]/).filter(part => part !== '').pop() ?? alias
|
|
18
|
-
const words = segment.replace(/[-_]+/g, ' ').trim()
|
|
19
|
-
|
|
20
|
-
return words.charAt(0).toUpperCase() + words.slice(1)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const resolveNavLabel = (
|
|
24
|
-
translate: NavTranslate, label: string | undefined, key: string, alias?: string
|
|
25
|
-
): string => label ?? translate(key, defaultNavLabel(alias ?? key))
|
|
7
|
+
import type { PanelNavConfig, PanelNavItem, PanelNavModel, PanelNavSection } from './types.js'
|
|
26
8
|
|
|
27
9
|
const visibleItems = (section: PanelNavSection): PanelNavItem[] =>
|
|
28
10
|
section.items.filter(item => item.hidden !== true)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NavLabelHelper, NavTranslate } from './types.js'
|
|
2
|
+
|
|
3
|
+
export const createNavLabelHelper = (): NavLabelHelper => {
|
|
4
|
+
const defaultNavTranslate: NavTranslate = (_key, defaultValue) => defaultValue
|
|
5
|
+
|
|
6
|
+
const defaultNavLabel = (alias: string): string => {
|
|
7
|
+
const segment = alias.split(/[:.]/).filter(part => part !== '').pop() ?? alias
|
|
8
|
+
const words = segment.replace(/[-_]+/g, ' ').trim()
|
|
9
|
+
|
|
10
|
+
return words.charAt(0).toUpperCase() + words.slice(1)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const resolveNavLabel = (
|
|
14
|
+
translate: NavTranslate, label: string | undefined, key: string, alias?: string
|
|
15
|
+
): string => label ?? translate(key, defaultNavLabel(alias ?? key))
|
|
16
|
+
|
|
17
|
+
return { defaultNavTranslate, defaultNavLabel, resolveNavLabel }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const navLabelHelper = createNavLabelHelper()
|
|
@@ -77,3 +77,16 @@ export interface PanelNavModel {
|
|
|
77
77
|
*/
|
|
78
78
|
hrefOf: (target: PanelNavItem | PanelNavSection) => string | undefined
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
/** How a navigation entry gets its label. */
|
|
82
|
+
export interface NavLabelHelper {
|
|
83
|
+
/** Default resolver: no i18n, the caller's fallback wins. See {@link NavTranslate}. */
|
|
84
|
+
defaultNavTranslate: NavTranslate
|
|
85
|
+
/**
|
|
86
|
+
* Turn an alias into something readable — `my-app:web:user-list` becomes `User list`.
|
|
87
|
+
* The last segment is the meaningful one; the prefixes address the app, not the screen.
|
|
88
|
+
*/
|
|
89
|
+
defaultNavLabel: (alias: string) => string
|
|
90
|
+
/** The literal label when given, else the translation of `key` falling back to the humanized alias. */
|
|
91
|
+
resolveNavLabel: (translate: NavTranslate, label: string | undefined, key: string, alias?: string) => string
|
|
92
|
+
}
|
package/src/i18n.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i18nHelper } from '@owlmeans/i18n'
|
|
2
2
|
import { buttons } from './consts.local.js'
|
|
3
3
|
|
|
4
4
|
for (const [language, values] of Object.entries(buttons)) {
|
|
5
|
-
addI18nLib(language, 'client-panel', values)
|
|
5
|
+
i18nHelper.addI18nLib(language, 'client-panel', values)
|
|
6
6
|
}
|