@morphemecms/cms 0.9.0-next-20261105122349 → 0.9.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.
Files changed (126) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/defaults/collections.ts +110 -56
  3. package/defaults/editor.ts +2 -2
  4. package/defaults/texts.ts +14 -5
  5. package/demo/src/content/pages/app-store.md +0 -1
  6. package/demo/src/content/pages/blocks.md +0 -1
  7. package/icons/lucide/arrows-horizontal.svg +1 -0
  8. package/icons/lucide/columns-plus-left.svg +1 -0
  9. package/icons/lucide/columns-plus-right.svg +1 -0
  10. package/icons/lucide/file.svg +1 -0
  11. package/icons/material-symbols/arrows-horizontal.svg +1 -0
  12. package/icons/material-symbols/columns-plus-left.svg +1 -0
  13. package/icons/material-symbols/columns-plus-right.svg +1 -0
  14. package/icons/material-symbols/file.svg +1 -0
  15. package/icons/phosphor/arrows-horizontal.svg +1 -0
  16. package/icons/phosphor/columns-plus-left.svg +1 -0
  17. package/icons/phosphor/columns-plus-right.svg +1 -0
  18. package/icons/phosphor/file.svg +1 -0
  19. package/lib/fold-open.d.mts +2 -0
  20. package/lib/fold-open.mjs +22 -0
  21. package/lib/fold-open.test.mjs +24 -0
  22. package/lib/menu.test.mjs +16 -3
  23. package/lib/page-template.d.mts +54 -0
  24. package/lib/page-template.mjs +126 -0
  25. package/lib/page-template.test.mjs +129 -0
  26. package/lib/side-block.d.mts +2 -16
  27. package/lib/side-block.mjs +12 -30
  28. package/lib/side-block.test.mjs +13 -30
  29. package/package.json +1 -1
  30. package/scripts/check-side.mjs +49 -22
  31. package/scripts/editor-roundtrip.mjs +15 -6
  32. package/scripts/fixtures/side/side-full.md +8 -0
  33. package/scripts/fixtures/side/side-left.md +8 -0
  34. package/scripts/fixtures/side/side-none.md +7 -0
  35. package/scripts/fixtures/side/side-right.md +8 -0
  36. package/scripts/fixtures/side/side-single.md +8 -0
  37. package/scripts/side-width.mjs +29 -0
  38. package/scripts/side-width.test.mjs +30 -0
  39. package/site/content.config.ts +3 -4
  40. package/site/header.d.mts +21 -0
  41. package/site/header.mjs +35 -0
  42. package/site/header.test.mjs +59 -0
  43. package/site/icons.d.mts +16 -0
  44. package/site/icons.mjs +8 -0
  45. package/site/site.ts +8 -3
  46. package/site/templates.ts +7 -0
  47. package/site/theme.mjs +3 -0
  48. package/src/App.tsx +12 -24
  49. package/src/AppList.tsx +36 -14
  50. package/src/AppSettingsTab.tsx +5 -3
  51. package/src/BlockDialog.tsx +50 -5
  52. package/src/DataFileFields.tsx +35 -0
  53. package/src/DataForm.tsx +16 -38
  54. package/src/Deleted.tsx +7 -3
  55. package/src/EntryForm.tsx +41 -88
  56. package/src/FooterSettings.tsx +76 -0
  57. package/src/FormFrame.tsx +2 -1
  58. package/src/HeaderSettings.tsx +97 -0
  59. package/src/Library.tsx +26 -14
  60. package/src/MarkdownEditor.tsx +18 -36
  61. package/src/Media.tsx +6 -3
  62. package/src/Redirects.tsx +12 -9
  63. package/src/SectionSettings.tsx +21 -0
  64. package/src/SectionSettingsIcon.tsx +10 -0
  65. package/src/SettingsSections.tsx +57 -0
  66. package/src/ThemeStore.tsx +28 -9
  67. package/src/Versions.tsx +3 -15
  68. package/src/busyAction.d.mts +3 -0
  69. package/src/busyAction.mjs +8 -0
  70. package/src/busyAction.test.mjs +16 -0
  71. package/src/dataFileValues.ts +30 -0
  72. package/src/editor.css +37 -3
  73. package/src/fields.tsx +29 -10
  74. package/src/footerPreview.d.mts +46 -0
  75. package/src/footerPreview.mjs +84 -0
  76. package/src/footerPreview.test.mjs +100 -0
  77. package/src/formSections.ts +100 -0
  78. package/src/frontmatter.mjs +1 -1
  79. package/src/frontmatter.test.mjs +11 -11
  80. package/src/headerPreview.d.mts +48 -0
  81. package/src/headerPreview.mjs +168 -0
  82. package/src/headerPreview.test.mjs +183 -0
  83. package/src/headerTheme.tsx +58 -0
  84. package/src/i18n/da-DK/common.ts +1 -0
  85. package/src/i18n/da-DK/library.ts +2 -0
  86. package/src/i18n/da-DK/preview.ts +2 -0
  87. package/src/i18n/da-DK/text.ts +1 -0
  88. package/src/i18n/en-US/common.ts +1 -0
  89. package/src/i18n/en-US/library.ts +4 -0
  90. package/src/i18n/en-US/preview.ts +3 -0
  91. package/src/i18n/en-US/text.ts +2 -1
  92. package/src/images.ts +7 -2
  93. package/src/menuChildren.ts +1 -3
  94. package/src/movedRoutes.d.mts +3 -0
  95. package/src/movedRoutes.mjs +35 -0
  96. package/src/movedRoutes.test.mjs +22 -0
  97. package/src/pagePreview.tsx +40 -29
  98. package/src/partPreview.tsx +59 -0
  99. package/src/schema.ts +7 -1
  100. package/src/sheetPlacement.d.mts +3 -0
  101. package/src/sheetPlacement.mjs +9 -0
  102. package/src/sheetPlacement.test.mjs +23 -0
  103. package/src/standard/views.ts +3 -3
  104. package/src/templateField.ts +28 -0
  105. package/src/themes.ts +3 -0
  106. package/src/ui.tsx +35 -8
  107. package/src/useDataFiles.ts +94 -0
  108. package/starter/package.json +1 -1
  109. package/theme/components/Footer.astro +14 -7
  110. package/theme/components/Header.astro +58 -33
  111. package/theme/components/MobileNav.astro +5 -4
  112. package/theme/components/RelatedPosts.astro +1 -1
  113. package/theme/components/headerParts.ts +17 -0
  114. package/theme/layouts/PageLayout.astro +20 -16
  115. package/theme/styles/base.css +6 -5
  116. package/theme/styles/blocks.css +8 -12
  117. package/theme/styles/components.css +8 -1
  118. package/theme/styles/global.css +5 -9
  119. package/theme/styles/ui.css +111 -5
  120. package/theme/theme.json +1 -0
  121. package/theme/ui/BusyContent.tsx +16 -0
  122. package/theme/ui/Button.tsx +25 -5
  123. package/theme/ui/IconButton.tsx +17 -4
  124. package/scripts/fixtures/side/side-off.md +0 -8
  125. package/scripts/fixtures/side/side-plain.md +0 -7
  126. package/scripts/fixtures/side/side-wide.md +0 -8
@@ -43,6 +43,14 @@
43
43
  --button-toggle-selected-surface, -text, -hover, -shadow, -hover-shadow a filled toggle that is on
44
44
  --button-tonal-selected-surface, -text, -hover; --button-elevated-selected-…; --button-outlined-selected-… the
45
45
  other kinds on
46
+ A busy button or icon button (busy: disabled, aria-busy, .button-busy or .icon-button-busy; M3 loading indicator): for
47
+ the first delay it looks unchanged (full colours, though it is disabled); then its label and icons go, a button
48
+ shrinks to a circle (its width becomes its height; with reduced motion at once) and a loading indicator in the
49
+ label's colour, without a container, stands in the middle (an icon button keeps its size and the indicator takes
50
+ its icon's).
51
+ --button-busy-delay the wait before it shows (200ms; a shorter wait shows nothing)
52
+ --button-busy-indicator-size a button's indicator (1.5rem, or its icon's size where that is larger)
53
+ --icon-button-busy-indicator-size an icon button's indicator (its icon's size)
46
54
  Icon buttons (IconButton.tsx, iconButtonClass): .icon-button with its kind, .icon-button-standard, -filled, -tonal or
47
55
  -outlined, its size, .icon-button-xsmall, none (small), -medium, -large or -xlarge, its width, .icon-button-narrow,
48
56
  none or -wide, and its shape, none (round) or .icon-button-square. A toggle as a button's (aria-pressed; the filled
@@ -57,8 +65,8 @@
57
65
  --icon-button-tonal-selected-surface, -text; --icon-button-outlined-selected-surface, -text the toggles' that are
58
66
  not the filled one's
59
67
  Set here by the sizes, the widths and the shapes, not by a theme: --button-box, --button-inline, --button-glyph,
60
- --button-round, --button-square, --button-pressed, --button-corners, and --icon-button-box, -ratio, -width, -glyph,
61
- -square, -pressed and -corners.
68
+ --button-round, --button-square, --button-pressed, --button-corners, --button-busy-after, --button-busy-size,
69
+ --button-busy-from, and --icon-button-box, -ratio, -width, -glyph, -square, -pressed and -corners.
62
70
  */
63
71
  /* The classes in the CMS's own code (this package, also from node_modules/, where Tailwind does not look by itself);
64
72
  the names in the icon packs' maps (MATERIAL_SYMBOLS, LUCIDE, site/icons.mjs) and the tests' examples are not classes. */
@@ -207,6 +215,12 @@
207
215
  --type-label-small-line-height: 1rem;
208
216
  --type-label-small-weight: 500;
209
217
  --type-label-small-tracking: 0.03125rem;
218
+ /* The lead (an intro paragraph: type-lead, a blog post's first paragraph, a single page's first paragraph). Each is the
219
+ title large's; a theme sets these four to change every lead. */
220
+ --type-lead-size: var(--type-title-large-size);
221
+ --type-lead-line-height: var(--type-title-large-line-height);
222
+ --type-lead-weight: var(--type-title-large-weight);
223
+ --type-lead-tracking: var(--type-title-large-tracking);
210
224
  }
211
225
  /* A role as a utility, for a theme's own components and layouts: type-title-large, medium:type-headline-small … */
212
226
  @utility type-display-large {
@@ -251,6 +265,12 @@
251
265
  font-weight: var(--type-title-large-weight);
252
266
  letter-spacing: var(--type-title-large-tracking);
253
267
  }
268
+ @utility type-lead {
269
+ font-size: var(--type-lead-size);
270
+ line-height: var(--type-lead-line-height);
271
+ font-weight: var(--type-lead-weight);
272
+ letter-spacing: var(--type-lead-tracking);
273
+ }
254
274
  @utility type-title-medium {
255
275
  font-size: var(--type-title-medium-size);
256
276
  line-height: var(--type-title-medium-line-height);
@@ -352,6 +372,45 @@
352
372
  opacity: 0.6;
353
373
  cursor: default;
354
374
  }
375
+ /* Busy (M3 loading indicator): under the delay it looks unchanged. After it the label and icons go and a button
376
+ shrinks to a circle (--button-busy-from is its width, set by Button.tsx); the indicator, in the label's colour,
377
+ stands in the middle. */
378
+ :is(.button-busy, .icon-button-busy) {
379
+ --button-busy-after: var(--button-busy-delay, 200ms);
380
+ position: relative;
381
+ }
382
+ /* Busy is disabled but not dimmed: full colours, the indicator too. */
383
+ :is(.button.button-busy, .icon-button.icon-button-busy):disabled {
384
+ opacity: 1;
385
+ cursor: progress;
386
+ }
387
+ .button-busy {
388
+ --button-busy-size: var(--button-busy-indicator-size, max(1.5rem, var(--button-glyph)));
389
+ overflow: hidden;
390
+ animation: button-busy-shrink var(--motion-spatial) var(--button-busy-after) both;
391
+ }
392
+ .icon-button-busy {
393
+ --button-busy-size: var(--icon-button-busy-indicator-size, var(--icon-button-glyph));
394
+ }
395
+ :is(.button-busy, .icon-button-busy) > .button-busy-content {
396
+ display: contents;
397
+ animation: button-busy-hide 0s var(--button-busy-after) forwards;
398
+ }
399
+ :is(.button-busy, .icon-button-busy) > .button-busy-indicator {
400
+ --loading-indicator-size: var(--button-busy-size);
401
+ --loading-indicator-shape-size: calc(var(--button-busy-size) * 2.375 / 3);
402
+ --loading-indicator-colour: currentColor;
403
+ position: absolute;
404
+ inset: 0;
405
+ margin: auto;
406
+ visibility: hidden;
407
+ animation: button-busy-show 0s var(--button-busy-after) forwards;
408
+ }
409
+ @media (prefers-reduced-motion: reduce) {
410
+ .button-busy {
411
+ animation-duration: 0s;
412
+ }
413
+ }
355
414
  /* The sizes: Material's five, the medium one the default. */
356
415
  .button-xsmall {
357
416
  --button-box: var(--button-xsmall-height, 2.25rem);
@@ -2045,10 +2104,13 @@
2045
2104
  /*
2046
2105
  Cards (Card.tsx, cardClass; m3.material.io/components/cards): the surface a block or an app's block is, .card with its
2047
2106
  kind, .card-elevated (on the surface with its shadow), .card-filled (on the tint) or .card-outlined (on the surface
2048
- with a thin edge); a card that is a link is an <a class="card …">, the whole card the link, lifted when pointed at;
2107
+ with a thin edge); a card that is a link is an <a class="card …">, the whole card the link, lifted and tinted when pointed at or focused;
2049
2108
  .card-dragged while it is dragged to another place. What is on it (its icon, heading, text, picture) is the block's
2050
2109
  own (block-…).
2051
- --card-radius, --card-padding its corners and the room inside it
2110
+ --card-radius, --card-padding its corners and the room inside it (16px, as Material 3's)
2111
+ --card-gap the room between cards (8px; the grid or list of cards sets it as its gap)
2112
+ --card-hover-layer, --card-focus-layer the state layer of a card that links, in its text's colour: pointed at 8%,
2113
+ focused 10% (opacity)
2052
2114
  --card-<kind>-surface, --card-<kind>-line, --card-<kind>-shadow each kind's surface, its edge (a 1px line inside
2053
2115
  it, transparent for none) and its shadow (0 0 #0000 for none): elevated level 1,
2054
2116
  filled and outlined level 0
@@ -2101,7 +2163,7 @@
2101
2163
  .card {
2102
2164
  display: block;
2103
2165
  border-radius: var(--card-radius, var(--block-radius));
2104
- padding: var(--card-padding, 1.5rem);
2166
+ padding: var(--card-padding, 1rem);
2105
2167
  }
2106
2168
  a.card {
2107
2169
  color: inherit;
@@ -2112,6 +2174,27 @@
2112
2174
  :where(a.card) {
2113
2175
  transition: box-shadow 0.3s var(--block-ease-quick);
2114
2176
  }
2177
+ /* The state layer (M3's): a card that is a link or a button, tinted in its text's colour over the whole card in its
2178
+ shape, 8% when pointed at and 10% with focus from the keyboard. It is under the ripple (the ripple is the last in the
2179
+ card) and does not take a click. */
2180
+ :is(a, button).card::before {
2181
+ content: '';
2182
+ position: absolute;
2183
+ inset: 0;
2184
+ border-radius: inherit;
2185
+ background: currentColor;
2186
+ opacity: 0;
2187
+ pointer-events: none;
2188
+ transition: opacity var(--motion-effects);
2189
+ }
2190
+ @media (hover: hover) {
2191
+ :is(a, button:enabled).card:hover::before {
2192
+ opacity: var(--card-hover-layer, 0.08);
2193
+ }
2194
+ }
2195
+ :is(a, button:enabled).card:focus-visible::before {
2196
+ opacity: var(--card-focus-layer, 0.1);
2197
+ }
2115
2198
  .card-elevated {
2116
2199
  --card-shadow: var(--card-elevated-shadow, var(--elevation-1-rest-shadow));
2117
2200
  }
@@ -5276,6 +5359,26 @@
5276
5359
  }
5277
5360
  }
5278
5361
 
5362
+ @keyframes button-busy-shrink {
5363
+ from {
5364
+ width: var(--button-busy-from, auto);
5365
+ }
5366
+ to {
5367
+ width: var(--button-box);
5368
+ padding-inline: 0;
5369
+ }
5370
+ }
5371
+ @keyframes button-busy-hide {
5372
+ to {
5373
+ visibility: hidden;
5374
+ }
5375
+ }
5376
+ @keyframes button-busy-show {
5377
+ to {
5378
+ visibility: visible;
5379
+ }
5380
+ }
5381
+
5279
5382
  /* Material's shapes, each a polygon of the same 60 points, turning three times in all. */
5280
5383
  /* prettier-ignore */
5281
5384
  @keyframes loading-indicator-morph {
@@ -6872,6 +6975,9 @@
6872
6975
  }
6873
6976
  @layer fallback {
6874
6977
  @media (prefers-reduced-motion: reduce) {
6978
+ :is(a, button).card::before {
6979
+ transition: none;
6980
+ }
6875
6981
  .ripple-wave {
6876
6982
  animation: ripple-in 105ms linear;
6877
6983
  }
package/theme/theme.json CHANGED
@@ -7,6 +7,7 @@
7
7
  "thirdParty": [],
8
8
  "icons": "material-symbols",
9
9
  "motion": "expressive",
10
+ "templates": ["single", "sidebar-right", "sidebar-left"],
10
11
  "description": {
11
12
  "da-DK": "Hjemmesiden i Googles Material Design 3, som mange kender fra Android: klar, enkel og velkendt.",
12
13
  "en-US": "The website in Google's Material Design 3, which many know from Android: clear, simple and familiar."
@@ -0,0 +1,16 @@
1
+ // The inside of a busy Button or IconButton (.button-busy-content, .button-busy-indicator in ../styles/ui.css): the
2
+ // usual content, hidden after the delay, and a loading indicator (aria-hidden) in the middle. Exposes BusyContent.
3
+ // Reuses loadingIndicatorClass from ./LoadingIndicator.
4
+ import type { ReactNode } from 'react';
5
+ import { loadingIndicatorClass } from './LoadingIndicator';
6
+
7
+ export default function BusyContent({ children }: { children: ReactNode }) {
8
+ return (
9
+ <>
10
+ <span className="button-busy-content">{children}</span>
11
+ <span className={`${loadingIndicatorClass()} button-busy-indicator`} aria-hidden="true">
12
+ <span className="loading-indicator-shape"></span>
13
+ </span>
14
+ </>
15
+ );
16
+ }
@@ -8,10 +8,15 @@
8
8
  // is on, where the site draws the icon filled. With `href` it is a link (Link: on the site; in the editor only drawn;
9
9
  // never a toggle), else a <button> (`type`, button by default). Other attributes (data-…, aria-…, disabled) go on it, and `className`
10
10
  // after its own classes (its width). An Astro component without React gives an element the same classes with
11
- // buttonClass, and a toggle aria-pressed itself.
12
- import type { ReactNode } from 'react';
11
+ // buttonClass, and a toggle aria-pressed itself. With `busy` (a <button> only; a link ignores it) the button is
12
+ // disabled, aria-busy and .button-busy while its action runs. Nothing else changes for the first --button-busy-delay
13
+ // (200 ms; M3 loading indicator); then the label and icons go, the button shrinks to a circle (its width becomes its
14
+ // height) and a loading indicator in the label's colour, without a container, stands in the middle (aria-hidden: the
15
+ // button keeps its name). When busy ends, it is the button again.
16
+ import { useEffect, useRef, type ReactNode } from 'react';
13
17
  import { Glyph, Svg, iconFile, type IconRef } from '@morphemecms/cms/src/icon';
14
18
  import { Link, type ButtonKind } from '@morphemecms/cms/src/standard/view';
19
+ import BusyContent from './BusyContent';
15
20
 
16
21
  export const buttonSizes = ['xsmall', 'small', 'medium', 'large', 'xlarge'] as const;
17
22
  export type ButtonSize = (typeof buttonSizes)[number];
@@ -40,6 +45,7 @@ export default function Button({
40
45
  size,
41
46
  shape,
42
47
  selected,
48
+ busy,
43
49
  icon,
44
50
  trailingIcon,
45
51
  href,
@@ -52,6 +58,7 @@ export default function Button({
52
58
  size?: ButtonSize;
53
59
  shape?: ButtonShape;
54
60
  selected?: boolean;
61
+ busy?: boolean;
55
62
  icon?: IconRef;
56
63
  trailingIcon?: IconRef;
57
64
  href?: string;
@@ -59,7 +66,13 @@ export default function Button({
59
66
  className?: string;
60
67
  children: ReactNode;
61
68
  } & Record<string, unknown>) {
62
- const classes = [buttonClass(kind, size, shape), className].filter(Boolean).join(' ');
69
+ const isBusy = busy === true && href === undefined;
70
+ const ref = useRef<HTMLButtonElement>(null);
71
+ // The width it shrinks from (a width cannot move from auto), read while the label is still there.
72
+ useEffect(() => {
73
+ if (isBusy && ref.current) ref.current.style.setProperty('--button-busy-from', `${ref.current.offsetWidth}px`);
74
+ }, [isBusy]);
75
+ const classes = [buttonClass(kind, size, shape), isBusy && 'button-busy', className].filter(Boolean).join(' ');
63
76
  const toggle = selected !== undefined && href === undefined;
64
77
  const content = (
65
78
  <>
@@ -69,8 +82,15 @@ export default function Button({
69
82
  </>
70
83
  );
71
84
  return href === undefined ? (
72
- <button type={type} className={classes} aria-pressed={toggle ? selected : undefined} {...rest}>
73
- {content}
85
+ <button
86
+ ref={ref}
87
+ type={type}
88
+ className={classes}
89
+ aria-pressed={toggle ? selected : undefined}
90
+ {...rest}
91
+ {...(isBusy ? { disabled: true, 'aria-busy': true } : {})}
92
+ >
93
+ {isBusy ? <BusyContent>{content}</BusyContent> : content}
74
94
  </button>
75
95
  ) : (
76
96
  <Link href={href} className={classes} {...rest}>
@@ -8,9 +8,12 @@
8
8
  // it filled. `badge`: the theme's Badge at its icon's corner, a number or a dot (true), which `label` should say too. With
9
9
  // `href` it is a link (Link: on the site; in the editor only drawn; never a toggle), else a <button type="button">.
10
10
  // Other attributes (data-…, disabled) go on it, and `className` after its own classes (its place). An Astro component
11
- // without React gives an element the same classes with iconButtonClass, and a toggle aria-pressed itself.
11
+ // without React gives an element the same classes with iconButtonClass, and a toggle aria-pressed itself. With `busy`
12
+ // (a <button> only) it is disabled, aria-busy and .icon-button-busy: after --button-busy-delay (200 ms) the icon is
13
+ // replaced by a loading indicator of its size (BusyContent); the button keeps its size.
12
14
  import { Glyph, type IconRef } from '@morphemecms/cms/src/icon';
13
15
  import Badge from '@theme/ui/Badge';
16
+ import BusyContent from '@theme/ui/BusyContent';
14
17
  import { Link, type IconButtonKind } from '@morphemecms/cms/src/standard/view';
15
18
  import { ToggleIcon, type ButtonShape, type ButtonSize } from '@theme/ui/Button';
16
19
 
@@ -44,6 +47,7 @@ export default function IconButton({
44
47
  shape,
45
48
  selected,
46
49
  badge,
50
+ busy,
47
51
  href,
48
52
  className,
49
53
  ...rest
@@ -56,17 +60,26 @@ export default function IconButton({
56
60
  shape?: ButtonShape;
57
61
  selected?: boolean;
58
62
  badge?: number | boolean;
63
+ busy?: boolean;
59
64
  href?: string;
60
65
  className?: string;
61
66
  } & Record<string, unknown>) {
62
- const classes = [iconButtonClass(kind, size, width, shape), className].filter(Boolean).join(' ');
67
+ const isBusy = busy === true && href === undefined;
68
+ const classes = [iconButtonClass(kind, size, width, shape), isBusy && 'icon-button-busy', className].filter(Boolean).join(' ');
63
69
  const toggle = selected !== undefined && href === undefined;
64
70
  const glyph =
65
71
  toggle && typeof icon === 'string' ? <ToggleIcon part="icon-button" name={icon} size={22} /> : <Glyph icon={icon} size={22} />;
66
72
  const button =
67
73
  href === undefined ? (
68
- <button type="button" aria-label={label} className={classes} aria-pressed={toggle ? selected : undefined} {...rest}>
69
- {glyph}
74
+ <button
75
+ type="button"
76
+ aria-label={label}
77
+ className={classes}
78
+ aria-pressed={toggle ? selected : undefined}
79
+ {...rest}
80
+ {...(isBusy ? { disabled: true, 'aria-busy': true } : {})}
81
+ >
82
+ {isBusy ? <BusyContent>{glyph}</BusyContent> : glyph}
70
83
  </button>
71
84
  ) : (
72
85
  <Link href={href} aria-label={label} className={classes} {...rest}>
@@ -1,8 +0,0 @@
1
- ---
2
- title: "Side off"
3
- side: false
4
- ---
5
-
6
- # Side off
7
-
8
- A page with side false: no column.
@@ -1,7 +0,0 @@
1
- ---
2
- title: "Side plain"
3
- ---
4
-
5
- # Side plain
6
-
7
- A plain page: the column is beside this text.
@@ -1,8 +0,0 @@
1
- ---
2
- title: "Side wide"
3
- wide: true
4
- ---
5
-
6
- # Side wide
7
-
8
- A wide page: no column.