newspack-components 4.5.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/DEVELOPMENT.md +1 -1
- package/dist/cjs/accordion/index.js +43 -26
- package/dist/cjs/accordion/index.test.js +80 -0
- package/dist/cjs/accordion/style.scss +25 -21
- package/dist/cjs/action-card/style.scss +0 -22
- package/dist/cjs/breadcrumbs/index.js +80 -0
- package/dist/cjs/breadcrumbs/index.test.js +109 -0
- package/dist/cjs/breadcrumbs/style.scss +41 -0
- package/dist/cjs/card/core-card.js +8 -4
- package/dist/cjs/card/index.js +1 -1
- package/dist/cjs/card/style-core.scss +14 -0
- package/dist/cjs/card/style.scss +0 -9
- package/dist/cjs/card-feature/index.js +25 -13
- package/dist/cjs/card-settings-group/index.js +3 -0
- package/dist/cjs/confirm-dialog/index.js +34 -8
- package/dist/cjs/confirm-dialog/index.test.js +173 -0
- package/dist/cjs/divider/style.scss +4 -2
- package/dist/cjs/grid/style.scss +5 -0
- package/dist/cjs/handoff/index.js +8 -5
- package/dist/cjs/index.js +34 -1
- package/dist/cjs/integration-icon/index.js +40 -0
- package/dist/cjs/integration-icon/style.scss +42 -0
- package/dist/cjs/integration-icons/index.js +21 -2
- package/dist/cjs/modal/style.scss +4 -0
- package/dist/cjs/page/index.js +121 -0
- package/dist/cjs/page/index.test.js +69 -0
- package/dist/cjs/page/style.scss +86 -0
- package/dist/cjs/route-match.js +59 -0
- package/dist/cjs/route-match.test.js +77 -0
- package/dist/cjs/section-header/index.js +8 -1
- package/dist/cjs/section-header/style.scss +11 -11
- package/dist/cjs/select-control/GroupedSelectControl.js +1 -1
- package/dist/cjs/select-control/style.scss +4 -0
- package/dist/cjs/style.scss +1 -4
- package/dist/cjs/tabbed-navigation/index.js +188 -40
- package/dist/cjs/tabbed-navigation/index.test.js +299 -0
- package/dist/cjs/tabbed-navigation/style.scss +77 -57
- package/dist/cjs/with-wizard/index.js +34 -45
- package/dist/cjs/with-wizard-screen/index.js +82 -41
- package/dist/cjs/with-wizard-screen/index.test.js +50 -0
- package/dist/cjs/with-wizard-screen/style.scss +15 -118
- package/dist/cjs/wizard/breadcrumbs-select.js +70 -0
- package/dist/cjs/wizard/breadcrumbs-select.test.js +200 -0
- package/dist/cjs/wizard/components/WizardError.js +8 -4
- package/dist/cjs/wizard/index.js +162 -141
- package/dist/esm/accordion/index.js +42 -26
- package/dist/esm/accordion/index.test.js +79 -0
- package/dist/esm/accordion/style.scss +25 -21
- package/dist/esm/action-card/style.scss +0 -22
- package/dist/esm/breadcrumbs/index.js +75 -0
- package/dist/esm/breadcrumbs/index.test.js +105 -0
- package/dist/esm/breadcrumbs/style.scss +41 -0
- package/dist/esm/card/core-card.js +8 -4
- package/dist/esm/card/index.js +1 -1
- package/dist/esm/card/style-core.scss +14 -0
- package/dist/esm/card/style.scss +0 -9
- package/dist/esm/card-feature/index.js +25 -13
- package/dist/esm/card-settings-group/index.js +3 -0
- package/dist/esm/confirm-dialog/index.js +34 -8
- package/dist/esm/confirm-dialog/index.test.js +173 -0
- package/dist/esm/divider/style.scss +4 -2
- package/dist/esm/grid/style.scss +5 -0
- package/dist/esm/handoff/index.js +7 -5
- package/dist/esm/index.js +5 -1
- package/dist/esm/integration-icon/index.js +34 -0
- package/dist/esm/integration-icon/style.scss +42 -0
- package/dist/esm/integration-icons/index.js +22 -1
- package/dist/esm/modal/style.scss +4 -0
- package/dist/esm/page/index.js +113 -0
- package/dist/esm/page/index.test.js +65 -0
- package/dist/esm/page/style.scss +86 -0
- package/dist/esm/route-match.js +51 -0
- package/dist/esm/route-match.test.js +74 -0
- package/dist/esm/section-header/index.js +8 -1
- package/dist/esm/section-header/style.scss +11 -11
- package/dist/esm/select-control/GroupedSelectControl.js +1 -1
- package/dist/esm/select-control/style.scss +4 -0
- package/dist/esm/style.scss +1 -4
- package/dist/esm/tabbed-navigation/index.js +192 -38
- package/dist/esm/tabbed-navigation/index.test.js +296 -0
- package/dist/esm/tabbed-navigation/style.scss +77 -57
- package/dist/esm/with-wizard/index.js +33 -45
- package/dist/esm/with-wizard-screen/index.js +78 -41
- package/dist/esm/with-wizard-screen/index.test.js +46 -0
- package/dist/esm/with-wizard-screen/style.scss +15 -118
- package/dist/esm/wizard/breadcrumbs-select.js +63 -0
- package/dist/esm/wizard/breadcrumbs-select.test.js +197 -0
- package/dist/esm/wizard/components/WizardError.js +7 -5
- package/dist/esm/wizard/index.js +165 -144
- package/package.json +10 -2
- package/src/accordion/index.js +27 -15
- package/src/accordion/index.test.js +62 -0
- package/src/accordion/style.scss +25 -21
- package/src/action-card/style.scss +0 -22
- package/src/breadcrumbs/index.js +63 -0
- package/src/breadcrumbs/index.test.js +59 -0
- package/src/breadcrumbs/style.scss +41 -0
- package/src/card/core-card.js +11 -6
- package/src/card/index.js +0 -1
- package/src/card/style-core.scss +14 -0
- package/src/card/style.scss +0 -9
- package/src/card-feature/README.md +3 -3
- package/src/card-feature/index.tsx +37 -21
- package/src/card-settings-group/index.tsx +4 -0
- package/src/card-sortable-list/index.tsx +1 -1
- package/src/confirm-dialog/index.test.js +142 -0
- package/src/confirm-dialog/index.tsx +34 -7
- package/src/divider/style.scss +4 -2
- package/src/grid/style.scss +5 -0
- package/src/handoff/index.js +4 -3
- package/src/index.js +5 -1
- package/src/integration-icon/index.tsx +35 -0
- package/src/integration-icon/style.scss +42 -0
- package/src/integration-icons/index.js +23 -0
- package/src/modal/style.scss +4 -0
- package/src/page/index.js +89 -0
- package/src/page/index.test.js +43 -0
- package/src/page/style.scss +86 -0
- package/src/route-match.js +47 -0
- package/src/route-match.test.js +50 -0
- package/src/section-header/index.js +7 -3
- package/src/section-header/style.scss +11 -11
- package/src/select-control/GroupedSelectControl.js +1 -1
- package/src/select-control/style.scss +4 -0
- package/src/style.scss +1 -4
- package/src/tabbed-navigation/index.js +181 -34
- package/src/tabbed-navigation/index.test.js +228 -0
- package/src/tabbed-navigation/style.scss +77 -57
- package/src/with-wizard/index.js +18 -39
- package/src/with-wizard-screen/index.js +70 -35
- package/src/with-wizard-screen/index.test.js +36 -0
- package/src/with-wizard-screen/style.scss +15 -118
- package/src/wizard/breadcrumbs-select.js +51 -0
- package/src/wizard/breadcrumbs-select.test.js +124 -0
- package/src/wizard/components/WizardError.js +4 -3
- package/src/wizard/index.js +160 -146
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
@use "../../../colors/colors.module" as colors;
|
|
2
|
+
@use "~@wordpress/base-styles/colors" as wp-colors;
|
|
3
|
+
@use "~@wordpress/base-styles/mixins" as wp-mixins;
|
|
4
|
+
@use "~@wordpress/base-styles/variables" as wp-vars;
|
|
5
|
+
|
|
6
|
+
// Design-system tokens ride with the Page header into any consuming bundle.
|
|
7
|
+
// Known tech debt: this reaches past the package's public exports entry
|
|
8
|
+
// (`@wordpress/theme/design-tokens.css`) into its internal layout, because
|
|
9
|
+
// postcss-import cannot resolve exports maps, and resolving the public
|
|
10
|
+
// specifier through sass/webpack leaks a bogus `wp-theme/design-tokens.css`
|
|
11
|
+
// script dependency into consumers' asset manifests, which silently blocks
|
|
12
|
+
// their enqueues. If upstream moves `src/prebuilt/`, this fails loudly at
|
|
13
|
+
// build time.
|
|
14
|
+
/* stylelint-disable-next-line no-invalid-position-at-import-rule -- sass @use must precede the CSS @import; postcss-import inlines it at build time. */
|
|
15
|
+
@import "@wordpress/theme/src/prebuilt/css/design-tokens.css";
|
|
16
|
+
|
|
17
|
+
// Newspack brand mapping: the wpds brand color tokens ship in WordPress
|
|
18
|
+
// blue; remap them onto the Newspack primary scale so every design-system
|
|
19
|
+
// component (focus rings, brand buttons, toggles) renders in brand colors
|
|
20
|
+
// without per-component overrides.
|
|
21
|
+
/* stylelint-disable plugin-wpds/no-setting-wpds-custom-properties -- deliberate Newspack brand override of the wpds brand tokens. */
|
|
22
|
+
:root {
|
|
23
|
+
--wpds-color-background-interactive-brand-strong: #{colors.$primary-600};
|
|
24
|
+
--wpds-color-background-interactive-brand-strong-active: #{colors.$primary-800};
|
|
25
|
+
--wpds-color-background-interactive-brand-weak-active: #{colors.$primary-000};
|
|
26
|
+
--wpds-color-background-surface-brand: #{colors.$primary-000};
|
|
27
|
+
--wpds-color-background-thumb-brand: #{colors.$primary-600};
|
|
28
|
+
--wpds-color-background-thumb-brand-active: #{colors.$primary-600};
|
|
29
|
+
--wpds-color-foreground-interactive-brand: #{colors.$primary-600};
|
|
30
|
+
--wpds-color-foreground-interactive-brand-active: #{colors.$primary-800};
|
|
31
|
+
--wpds-color-foreground-interactive-brand-disabled: #{colors.$primary-400};
|
|
32
|
+
--wpds-color-stroke-focus: #{colors.$primary-600};
|
|
33
|
+
--wpds-color-stroke-interactive-brand: #{colors.$primary-600};
|
|
34
|
+
--wpds-color-stroke-interactive-brand-active: #{colors.$primary-800};
|
|
35
|
+
--wpds-color-stroke-interactive-brand-disabled: #{colors.$primary-100};
|
|
36
|
+
--wpds-color-stroke-surface-brand: #{colors.$primary-000};
|
|
37
|
+
--wpds-color-stroke-surface-brand-strong: #{colors.$primary-600};
|
|
38
|
+
}
|
|
39
|
+
/* stylelint-enable plugin-wpds/no-setting-wpds-custom-properties */
|
|
40
|
+
|
|
41
|
+
[role="region"].newspack-page {
|
|
42
|
+
background-color: wp-colors.$white;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.newspack-page__header-region {
|
|
46
|
+
@include wp-mixins.break-medium {
|
|
47
|
+
position: sticky;
|
|
48
|
+
top: var(--wp-admin--admin-bar--height);
|
|
49
|
+
z-index: 3;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.newspack-page__header {
|
|
54
|
+
/* stylelint-disable-next-line plugin-wpds/no-unknown-ds-tokens -- the plugin's token list lags the prebuilt design-tokens.css names. */
|
|
55
|
+
background: var(--wpds-color-background-surface-neutral-strong);
|
|
56
|
+
border-block-end: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral-weak);
|
|
57
|
+
padding: var(--wpds-dimension-padding-lg) var(--wpds-dimension-padding-2xl);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.newspack-page__header-actions {
|
|
61
|
+
flex-shrink: 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.newspack-page__header-subtitle {
|
|
65
|
+
/* stylelint-disable-next-line plugin-wpds/no-unknown-ds-tokens -- the plugin's token list lags the prebuilt design-tokens.css names. */
|
|
66
|
+
color: var(--wpds-color-foreground-content-neutral-weak);
|
|
67
|
+
padding-block-end: var(--wpds-dimension-padding-xs);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.newspack-page__header-visual {
|
|
71
|
+
display: grid;
|
|
72
|
+
flex-shrink: 0;
|
|
73
|
+
height: 24px;
|
|
74
|
+
place-items: center;
|
|
75
|
+
width: 24px;
|
|
76
|
+
|
|
77
|
+
svg path {
|
|
78
|
+
transform: scale(1.1111);
|
|
79
|
+
transform-box: view-box;
|
|
80
|
+
transform-origin: center;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.newspack-page__breadcrumbs {
|
|
85
|
+
min-height: wp-vars.$button-size;
|
|
86
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import Router from './proxied-imports/router';
|
|
5
|
+
var matchPath = Router.matchPath;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Whether a wildcard pattern (`'/base/*'` or `'/base*'`) matches a pathname.
|
|
9
|
+
* The wildcard is anchored at a `/` boundary: `'/segments*'` matches
|
|
10
|
+
* `/segments` and `/segments/123`, never `/segments-old`.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} pattern Pattern ending in `*`.
|
|
13
|
+
* @param {string} pathname Current router pathname.
|
|
14
|
+
* @return {boolean} Whether the pattern matches.
|
|
15
|
+
*/
|
|
16
|
+
var matchesWildcard = function matchesWildcard(pattern, pathname) {
|
|
17
|
+
var base = pattern.slice(0, -1).replace(/\/$/, '');
|
|
18
|
+
return pathname === base || pathname.startsWith(base + '/');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Single route matcher shared by breadcrumb selection and tabbed navigation.
|
|
23
|
+
*
|
|
24
|
+
* An item matches when one of its `activeTabPaths` hits (exact string, or a
|
|
25
|
+
* `*` wildcard anchored at a `/` boundary), or — falling through on a miss —
|
|
26
|
+
* when its `path` matches the pathname. Path matching is prefix-based except
|
|
27
|
+
* for the root path and items opting in via `exact: true`, mirroring how the
|
|
28
|
+
* wizard registers its `<Route>`s (`exact={ section.exact ?? false }`).
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} item Route item (`{ path?, exact?, activeTabPaths? }`).
|
|
31
|
+
* @param {string} pathname Current router pathname.
|
|
32
|
+
* @return {boolean} Whether the item matches the pathname.
|
|
33
|
+
*/
|
|
34
|
+
export var matchesRoute = function matchesRoute(item, pathname) {
|
|
35
|
+
if (Array.isArray(item.activeTabPaths)) {
|
|
36
|
+
var wildcardHit = item.activeTabPaths.some(function (path) {
|
|
37
|
+
return path.endsWith('*') ? matchesWildcard(path, pathname) : path === pathname;
|
|
38
|
+
});
|
|
39
|
+
if (wildcardHit) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!item.path) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
var exact = '/' === item.path || item.exact === true;
|
|
47
|
+
return !!matchPath(pathname, {
|
|
48
|
+
path: item.path,
|
|
49
|
+
exact: exact
|
|
50
|
+
});
|
|
51
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import { matchesRoute } from './route-match';
|
|
5
|
+
describe('matchesRoute', function () {
|
|
6
|
+
it('matches the root path exactly, never as a prefix', function () {
|
|
7
|
+
expect(matchesRoute({
|
|
8
|
+
path: '/'
|
|
9
|
+
}, '/')).toBe(true);
|
|
10
|
+
expect(matchesRoute({
|
|
11
|
+
path: '/'
|
|
12
|
+
}, '/segments')).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
it('matches a path as a prefix by default, at segment boundaries', function () {
|
|
15
|
+
expect(matchesRoute({
|
|
16
|
+
path: '/segments'
|
|
17
|
+
}, '/segments')).toBe(true);
|
|
18
|
+
expect(matchesRoute({
|
|
19
|
+
path: '/segments'
|
|
20
|
+
}, '/segments/123')).toBe(true);
|
|
21
|
+
expect(matchesRoute({
|
|
22
|
+
path: '/segments'
|
|
23
|
+
}, '/segments-old')).toBe(false);
|
|
24
|
+
expect(matchesRoute({
|
|
25
|
+
path: '/segments'
|
|
26
|
+
}, '/donations')).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it('matches exactly when the item opts in via exact', function () {
|
|
29
|
+
expect(matchesRoute({
|
|
30
|
+
path: '/segments',
|
|
31
|
+
exact: true
|
|
32
|
+
}, '/segments')).toBe(true);
|
|
33
|
+
expect(matchesRoute({
|
|
34
|
+
path: '/segments',
|
|
35
|
+
exact: true
|
|
36
|
+
}, '/segments/123')).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
it('matches an activeTabPaths entry exactly', function () {
|
|
39
|
+
var item = {
|
|
40
|
+
path: '/other',
|
|
41
|
+
activeTabPaths: ['/segments/new']
|
|
42
|
+
};
|
|
43
|
+
expect(matchesRoute(item, '/segments/new')).toBe(true);
|
|
44
|
+
expect(matchesRoute(item, '/segments/123')).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
it('anchors an activeTabPaths wildcard at a segment boundary', function () {
|
|
47
|
+
var item = {
|
|
48
|
+
path: '/other',
|
|
49
|
+
activeTabPaths: ['/segments/*']
|
|
50
|
+
};
|
|
51
|
+
expect(matchesRoute(item, '/segments')).toBe(true);
|
|
52
|
+
expect(matchesRoute(item, '/segments/123')).toBe(true);
|
|
53
|
+
expect(matchesRoute(item, '/segments-old')).toBe(false);
|
|
54
|
+
var noSlash = {
|
|
55
|
+
path: '/other',
|
|
56
|
+
activeTabPaths: ['/segments*']
|
|
57
|
+
};
|
|
58
|
+
expect(matchesRoute(noSlash, '/segments')).toBe(true);
|
|
59
|
+
expect(matchesRoute(noSlash, '/segments/123')).toBe(true);
|
|
60
|
+
expect(matchesRoute(noSlash, '/segments-old')).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
it('falls through to path matching when activeTabPaths misses', function () {
|
|
63
|
+
expect(matchesRoute({
|
|
64
|
+
path: '/segments',
|
|
65
|
+
activeTabPaths: ['/elsewhere']
|
|
66
|
+
}, '/segments/123')).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
it('never matches an item without a path when activeTabPaths misses', function () {
|
|
69
|
+
expect(matchesRoute({}, '/segments')).toBe(false);
|
|
70
|
+
expect(matchesRoute({
|
|
71
|
+
activeTabPaths: ['/elsewhere']
|
|
72
|
+
}, '/segments')).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -94,12 +94,18 @@ var SectionHeader = function SectionHeader(_ref) {
|
|
|
94
94
|
}
|
|
95
95
|
}, []);
|
|
96
96
|
var classes = classnames('newspack-section-header', centered && 'newspack-section-header--is-centered', isWhite && 'newspack-section-header--is-white', noMargin && 'newspack-section-header--no-margin', pageHeader && 'newspack-section-header--page-header');
|
|
97
|
-
|
|
97
|
+
|
|
98
|
+
// The breadcrumb `Page` owns the single page `<h1>`, so a `pageHeader` section
|
|
99
|
+
// is a secondary heading: its level follows `heading` (default 2). `pageHeader`
|
|
100
|
+
// controls only the enlarged, centered styling — not the tag. Pass `heading={ 1 }`
|
|
101
|
+
// on a headerless screen that needs the section header to be the page's h1.
|
|
102
|
+
var HeadingTag = "h".concat(heading);
|
|
98
103
|
var titleContent = null;
|
|
99
104
|
if (typeof title === 'string') {
|
|
100
105
|
titleContent = /*#__PURE__*/_jsxs("div", {
|
|
101
106
|
className: "newspack-section-header__title-container",
|
|
102
107
|
children: [/*#__PURE__*/_jsxs(HeadingTag, {
|
|
108
|
+
className: "newspack-section-header__title",
|
|
103
109
|
children: [title, badges !== null && badges !== void 0 && badges.length ? badges.map(function (badge, i) {
|
|
104
110
|
return /*#__PURE__*/_jsx(Badge, {
|
|
105
111
|
text: badge.label,
|
|
@@ -134,6 +140,7 @@ var SectionHeader = function SectionHeader(_ref) {
|
|
|
134
140
|
});
|
|
135
141
|
} else if (typeof title === 'function') {
|
|
136
142
|
titleContent = /*#__PURE__*/_jsx(HeadingTag, {
|
|
143
|
+
className: "newspack-section-header__title",
|
|
137
144
|
children: title()
|
|
138
145
|
});
|
|
139
146
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
@use "~@wordpress/base-styles/colors" as wp-colors;
|
|
6
6
|
@use "~@wordpress/base-styles/variables" as wp-vars;
|
|
7
|
+
@use "~@wordpress/base-styles/mixins" as wp-mixins;
|
|
7
8
|
@use "../../../colors/colors.module" as colors;
|
|
8
9
|
|
|
9
10
|
.newspack-section-header {
|
|
@@ -12,6 +13,9 @@
|
|
|
12
13
|
|
|
13
14
|
&__container {
|
|
14
15
|
position: relative;
|
|
16
|
+
p {
|
|
17
|
+
text-wrap: balance;
|
|
18
|
+
}
|
|
15
19
|
&:not(:first-child) {
|
|
16
20
|
margin-top: 48px;
|
|
17
21
|
}
|
|
@@ -84,17 +88,18 @@
|
|
|
84
88
|
color: wp-colors.$gray-700;
|
|
85
89
|
}
|
|
86
90
|
|
|
91
|
+
// Title sizing keyed to the class, not the tag, so demoting the heading level
|
|
92
|
+
// (h1 -> h2 when the breadcrumb Page owns the page h1) never changes its look,
|
|
93
|
+
// and genuine h2s elsewhere in the header are unaffected.
|
|
94
|
+
&__title {
|
|
95
|
+
@include wp-mixins.heading-x-large();
|
|
96
|
+
}
|
|
97
|
+
|
|
87
98
|
&__title-container {
|
|
88
99
|
align-items: center;
|
|
89
100
|
display: flex;
|
|
90
101
|
gap: 8px;
|
|
91
102
|
|
|
92
|
-
h2 {
|
|
93
|
-
font-size: wp-vars.$font-size-x-large;
|
|
94
|
-
font-weight: wp-vars.$font-weight-medium;
|
|
95
|
-
line-height: wp-vars.$font-line-height-x-large;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
103
|
.newspack-section-header__menu,
|
|
99
104
|
.newspack-section-header__secondary-action,
|
|
100
105
|
.newspack-badge {
|
|
@@ -161,10 +166,5 @@
|
|
|
161
166
|
fill: var(--wp-admin-theme-color);
|
|
162
167
|
}
|
|
163
168
|
}
|
|
164
|
-
h1 {
|
|
165
|
-
font-size: wp-vars.$font-size-x-large;
|
|
166
|
-
font-weight: wp-vars.$font-weight-medium;
|
|
167
|
-
line-height: wp-vars.$font-line-height-x-large;
|
|
168
|
-
}
|
|
169
169
|
}
|
|
170
170
|
}
|
|
@@ -50,7 +50,7 @@ export default function GroupedSelectControl(_ref) {
|
|
|
50
50
|
help: help,
|
|
51
51
|
className: classnames(className, 'components-select-control'),
|
|
52
52
|
children: /*#__PURE__*/_jsxs("div", {
|
|
53
|
-
className: "
|
|
53
|
+
className: "newspack-grouped-select-control__field",
|
|
54
54
|
children: [/*#__PURE__*/_jsxs("select", _objectSpread(_objectSpread({
|
|
55
55
|
id: id,
|
|
56
56
|
className: "components-select-control__input",
|
package/dist/esm/style.scss
CHANGED
|
@@ -35,12 +35,9 @@
|
|
|
35
35
|
// Checkboxes
|
|
36
36
|
.newspack-checkbox-control {
|
|
37
37
|
margin-top: 0 !important;
|
|
38
|
-
.components-checkbox-control__label {
|
|
39
|
-
font-weight: 600;
|
|
40
|
-
}
|
|
41
38
|
|
|
42
39
|
.components-base-control__help {
|
|
43
|
-
margin-left:
|
|
40
|
+
margin-left: calc(var(--checkbox-input-size) + var(--checkbox-input-margin));
|
|
44
41
|
margin-top: 0;
|
|
45
42
|
}
|
|
46
43
|
}
|
|
@@ -1,65 +1,219 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
/**
|
|
5
|
+
* WordPress dependencies.
|
|
6
|
+
*/
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
import { Tabs } from '@wordpress/ui';
|
|
9
|
+
|
|
1
10
|
/**
|
|
2
11
|
* External dependencies.
|
|
3
12
|
*/
|
|
4
13
|
import classnames from 'classnames';
|
|
5
|
-
import findIndex from 'lodash/findIndex';
|
|
6
14
|
import Router from '../proxied-imports/router';
|
|
7
15
|
|
|
8
16
|
/**
|
|
9
17
|
* Internal dependencies.
|
|
10
18
|
*/
|
|
19
|
+
import { matchesRoute } from '../route-match';
|
|
11
20
|
import './style.scss';
|
|
12
|
-
import {
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
var
|
|
21
|
+
import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
22
|
+
var useHistory = Router.useHistory,
|
|
23
|
+
useLocation = Router.useLocation;
|
|
24
|
+
var getItemValue = function getItemValue(item, index) {
|
|
25
|
+
return item.path || item.href || "item-".concat(index);
|
|
26
|
+
};
|
|
27
|
+
export var isItemActive = function isItemActive(item, pathname) {
|
|
28
|
+
if (item.selected) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (null === pathname) {
|
|
32
|
+
return Boolean(item.href) && window.location.href === item.href;
|
|
33
|
+
}
|
|
34
|
+
return matchesRoute(item, pathname);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Default layout when `Page` doesn't inject one: bar above content.
|
|
39
|
+
*/
|
|
40
|
+
var defaultRenderShell = function defaultRenderShell(bar, content) {
|
|
41
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
42
|
+
children: [bar, content]
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Router-driven tabs: a real WAI-ARIA tabs widget. The routed page content
|
|
48
|
+
* renders inside the active tab's `Tabs.Panel`, so each tab's `aria-controls`
|
|
49
|
+
* points at the panel that actually contains its content — or, when no visible
|
|
50
|
+
* tab owns the route, as a sibling of the panels (see `unownedContent` below).
|
|
51
|
+
*/
|
|
52
|
+
var RoutedTabbedNavigation = function RoutedTabbedNavigation(_ref) {
|
|
16
53
|
var items = _ref.items,
|
|
17
54
|
className = _ref.className,
|
|
18
55
|
disableUpcoming = _ref.disableUpcoming,
|
|
56
|
+
_ref$content = _ref.content,
|
|
57
|
+
content = _ref$content === void 0 ? null : _ref$content,
|
|
58
|
+
_ref$renderShell = _ref.renderShell,
|
|
59
|
+
renderShell = _ref$renderShell === void 0 ? defaultRenderShell : _ref$renderShell,
|
|
19
60
|
_ref$children = _ref.children,
|
|
20
61
|
children = _ref$children === void 0 ? null : _ref$children;
|
|
62
|
+
var history = useHistory();
|
|
63
|
+
var _useLocation = useLocation(),
|
|
64
|
+
pathname = _useLocation.pathname;
|
|
21
65
|
var displayedItems = items.filter(function (item) {
|
|
22
66
|
return !item.isHiddenInTabbedNavigation;
|
|
23
67
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
68
|
+
|
|
69
|
+
// Most-specific match wins, so a tab is never outranked by a sibling that
|
|
70
|
+
// happens to prefix-match the same pathname.
|
|
71
|
+
var activeIndex = displayedItems.reduce(function (best, item, index) {
|
|
72
|
+
var _item$path, _displayedItems$best$;
|
|
73
|
+
if (!isItemActive(item, pathname)) {
|
|
74
|
+
return best;
|
|
30
75
|
}
|
|
31
|
-
if (
|
|
32
|
-
return
|
|
33
|
-
if (path.endsWith('*')) {
|
|
34
|
-
var basePath = path.slice(0, -1);
|
|
35
|
-
return pathname.startsWith(basePath);
|
|
36
|
-
}
|
|
37
|
-
return item.activeTabPaths.includes(pathname);
|
|
38
|
-
});
|
|
76
|
+
if (best === -1 || (((_item$path = item.path) === null || _item$path === void 0 ? void 0 : _item$path.length) || 0) > (((_displayedItems$best$ = displayedItems[best].path) === null || _displayedItems$best$ === void 0 ? void 0 : _displayedItems$best$.length) || 0)) {
|
|
77
|
+
return index;
|
|
39
78
|
}
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
|
|
79
|
+
return best;
|
|
80
|
+
}, -1);
|
|
81
|
+
var activeValue = activeIndex > -1 ? getItemValue(displayedItems[activeIndex], activeIndex) : null;
|
|
82
|
+
var onTabClick = function onTabClick(event, item) {
|
|
83
|
+
// Modified clicks (new tab/window) keep native anchor behavior. Middle
|
|
84
|
+
// clicks never reach onClick (they dispatch auxclick), so the anchor's
|
|
85
|
+
// href handles those natively too.
|
|
86
|
+
if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Route through the history object so navigation guards registered via
|
|
90
|
+
// history.block() (unsaved-changes prompts) are consulted.
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
if (item.path !== pathname) {
|
|
93
|
+
history.push(item.path);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var bar = /*#__PURE__*/_jsxs("div", {
|
|
43
97
|
className: classnames('newspack-tabbed-navigation', className),
|
|
44
|
-
children: [/*#__PURE__*/_jsx(
|
|
98
|
+
children: [/*#__PURE__*/_jsx(Tabs.List, {
|
|
99
|
+
activateOnFocus: false,
|
|
45
100
|
children: displayedItems.map(function (item, index) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
101
|
+
var isDisabled = disableUpcoming && index > activeIndex;
|
|
102
|
+
var href = item.path ? "#".concat(item.path) : item.href;
|
|
103
|
+
return /*#__PURE__*/_jsx(Tabs.Tab, {
|
|
104
|
+
value: getItemValue(item, index),
|
|
105
|
+
disabled: isDisabled,
|
|
106
|
+
nativeButton: false,
|
|
107
|
+
render:
|
|
108
|
+
/*#__PURE__*/
|
|
109
|
+
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/anchor-is-valid -- content is supplied via the Tab children through @wordpress/ui's render prop, and disabled tabs intentionally drop the href.
|
|
110
|
+
_jsx("a", {
|
|
111
|
+
href: isDisabled ? undefined : href,
|
|
112
|
+
onClick: isDisabled || !item.path ? undefined : function (event) {
|
|
113
|
+
return onTabClick(event, item);
|
|
114
|
+
}
|
|
115
|
+
}),
|
|
116
|
+
children: item.label
|
|
117
|
+
}, getItemValue(item, index));
|
|
61
118
|
})
|
|
62
119
|
}), children]
|
|
63
120
|
});
|
|
121
|
+
|
|
122
|
+
// Panels must stay 1:1 with the tabs above (@wordpress/ui validates the
|
|
123
|
+
// pairing in development builds); only the active panel holds the content.
|
|
124
|
+
var panels = displayedItems.map(function (item, index) {
|
|
125
|
+
var value = getItemValue(item, index);
|
|
126
|
+
return /*#__PURE__*/_jsx(Tabs.Panel, {
|
|
127
|
+
value: value,
|
|
128
|
+
tabIndex: -1,
|
|
129
|
+
className: "newspack-tabbed-navigation__panel",
|
|
130
|
+
children: value === activeValue ? content : null
|
|
131
|
+
}, value);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// A route no visible tab owns — a hidden sub-view (an edit screen), or a path
|
|
135
|
+
// with no matching tab at all — still has to render its content, outside the
|
|
136
|
+
// panels: with no active tab there is no panel entitled to hold it. Without
|
|
137
|
+
// this the page body is blank, and because the router's own fallback (the
|
|
138
|
+
// wizard's trailing `<Redirect>`) lives inside that content, it never mounts
|
|
139
|
+
// to correct the route either.
|
|
140
|
+
//
|
|
141
|
+
// This renders without the panels' `__panel` class, which is styleless today.
|
|
142
|
+
// Keep it that way: styling `__panel` would silently indent/pad owned routes
|
|
143
|
+
// only, and the divergence would be hard to trace back to here.
|
|
144
|
+
var unownedContent = null === activeValue ? content : null;
|
|
145
|
+
return /*#__PURE__*/_jsx(Tabs.Root, {
|
|
146
|
+
value: activeValue,
|
|
147
|
+
className: "newspack-tabbed-navigation__root",
|
|
148
|
+
children: renderShell(bar, /*#__PURE__*/_jsxs(_Fragment, {
|
|
149
|
+
children: [panels, unownedContent]
|
|
150
|
+
}))
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Link-driven "tabs": plain navigation for items that trigger full page loads
|
|
156
|
+
* (e.g. the PHP-rendered wizards admin header). These are not tabs in the ARIA
|
|
157
|
+
* sense — there is no in-page panel to control — so they render as a `nav` of
|
|
158
|
+
* links with `aria-current`, styled to match the tabs widget.
|
|
159
|
+
*/
|
|
160
|
+
var LinkTabbedNavigation = function LinkTabbedNavigation(_ref2) {
|
|
161
|
+
var items = _ref2.items,
|
|
162
|
+
className = _ref2.className,
|
|
163
|
+
_ref2$content = _ref2.content,
|
|
164
|
+
content = _ref2$content === void 0 ? null : _ref2$content,
|
|
165
|
+
_ref2$renderShell = _ref2.renderShell,
|
|
166
|
+
renderShell = _ref2$renderShell === void 0 ? defaultRenderShell : _ref2$renderShell,
|
|
167
|
+
_ref2$children = _ref2.children,
|
|
168
|
+
children = _ref2$children === void 0 ? null : _ref2$children;
|
|
169
|
+
var displayedItems = items.filter(function (item) {
|
|
170
|
+
return !item.isHiddenInTabbedNavigation;
|
|
171
|
+
});
|
|
172
|
+
var bar = /*#__PURE__*/_jsxs("nav", {
|
|
173
|
+
className: classnames('newspack-tabbed-navigation', 'newspack-tabbed-navigation--links', className),
|
|
174
|
+
"aria-label": __('Secondary navigation', 'newspack-plugin'),
|
|
175
|
+
children: [displayedItems.map(function (item, index) {
|
|
176
|
+
return /*#__PURE__*/_jsx("a", {
|
|
177
|
+
href: item.href,
|
|
178
|
+
className: "newspack-tabbed-navigation__link",
|
|
179
|
+
"aria-current": isItemActive(item, null) ? 'page' : undefined,
|
|
180
|
+
children: item.label
|
|
181
|
+
}, getItemValue(item, index));
|
|
182
|
+
}), children]
|
|
183
|
+
});
|
|
184
|
+
return renderShell(bar, content);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Tabbed navigation over `{ label, path? | href?, exact?, activeTabPaths?, selected?, isHiddenInTabbedNavigation? }` items.
|
|
189
|
+
*
|
|
190
|
+
* Items with router `path`s render as a real tabs widget whose active panel
|
|
191
|
+
* contains `content`; href-only items render as plain navigation links.
|
|
192
|
+
*
|
|
193
|
+
* When no visible tab owns the current route — a hidden sub-view, or an unknown
|
|
194
|
+
* URL — `content` renders as a sibling of the panels with no tab selected. That
|
|
195
|
+
* is a safety net, not the preferred shape: for a route that is conceptually a
|
|
196
|
+
* sub-view of a tab, give that tab an `activeTabPaths` entry (e.g.
|
|
197
|
+
* `activeTabPaths: [ '/edit/*' ]`) so it stays selected and its panel keeps
|
|
198
|
+
* owning the content.
|
|
199
|
+
*
|
|
200
|
+
* `renderShell( bar, content )` is a layout injection point used by `Page` to
|
|
201
|
+
* place the bar inside its sticky header region while the content flows below.
|
|
202
|
+
*
|
|
203
|
+
* @param {Object} props
|
|
204
|
+
* @param {Array} props.items Navigation items.
|
|
205
|
+
* @param {boolean} [props.disableUpcoming] Disable tabs after the active one (setup flows).
|
|
206
|
+
* @param {*} [props.content] Page content, rendered inside the active tab's panel — or
|
|
207
|
+
* as a sibling of the panels when no visible tab owns the route.
|
|
208
|
+
* @param {*} [props.renderShell] Layout callback `( bar, content ) => element`.
|
|
209
|
+
* @param {string} [props.className]
|
|
210
|
+
* @param {*} [props.children] Extra elements rendered inside the bar (e.g. error notices).
|
|
211
|
+
* @return {JSX.Element} Tabbed navigation.
|
|
212
|
+
*/
|
|
213
|
+
var TabbedNavigation = function TabbedNavigation(props) {
|
|
214
|
+
var hasRoutedItems = props.items.some(function (item) {
|
|
215
|
+
return item.path;
|
|
216
|
+
});
|
|
217
|
+
return hasRoutedItems ? /*#__PURE__*/_jsx(RoutedTabbedNavigation, _objectSpread({}, props)) : /*#__PURE__*/_jsx(LinkTabbedNavigation, _objectSpread({}, props));
|
|
64
218
|
};
|
|
65
219
|
export default TabbedNavigation;
|