@streamscloud/kit 0.26.0 → 0.27.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.
@@ -1,7 +1,9 @@
1
- <script lang="ts">import { Icon } from '../icon';
1
+ <script lang="ts">import LayoutControlButton from './layout-control-button.svelte';
2
2
  import { PageLayoutLocalization } from './page-layout-localization';
3
+ import IconDismiss from '@fluentui/svg-icons/icons/dismiss_20_regular.svg?raw';
3
4
  import IconWindow from '@fluentui/svg-icons/icons/window_20_regular.svg?raw';
4
- const { children, title, actions, windowToggleActive = false, scrollable = false, on } = $props();
5
+ import IconWindowMultiple from '@fluentui/svg-icons/icons/window_multiple_20_regular.svg?raw';
6
+ const { children, title, actions, navigationToggle, editorControls, scrollable = false } = $props();
5
7
  const localization = new PageLayoutLocalization();
6
8
  </script>
7
9
 
@@ -18,16 +20,29 @@ const localization = new PageLayoutLocalization();
18
20
  {#if actions}
19
21
  {@render actions()}
20
22
  {/if}
21
- {#if on?.windowToggle}
22
- <button
23
- type="button"
24
- class="page-layout__window-toggle"
25
- class:page-layout__window-toggle--active={windowToggleActive}
26
- onclick={on.windowToggle}
27
- aria-label={localization.toggleNavigation}
28
- aria-pressed={windowToggleActive}>
29
- <Icon src={IconWindow} />
30
- </button>
23
+ {#if navigationToggle || editorControls}
24
+ <div class="page-layout__controls">
25
+ {#if navigationToggle}
26
+ <LayoutControlButton
27
+ icon={IconWindow}
28
+ label={localization.toggleNavigation}
29
+ variant="flat"
30
+ active={navigationToggle.active}
31
+ on={{ click: navigationToggle.onClick }} />
32
+ {/if}
33
+ {#if editorControls}
34
+ <LayoutControlButton
35
+ icon={IconWindowMultiple}
36
+ label={localization.togglePanels}
37
+ active={editorControls.panelsToggle.active}
38
+ on={{ click: editorControls.panelsToggle.onClick }} />
39
+ <LayoutControlButton
40
+ icon={IconDismiss}
41
+ label={localization.close}
42
+ active={editorControls.close.active}
43
+ on={{ click: editorControls.close.onClick }} />
44
+ {/if}
45
+ </div>
31
46
  {/if}
32
47
  </div>
33
48
  </div>
@@ -38,12 +53,14 @@ const localization = new PageLayoutLocalization();
38
53
 
39
54
  <!--
40
55
  @component
41
- PageLayout — top-level window chrome. Renders a fixed-height header (title + actions + optional
42
- window-toggle button) above a flex-column body that typically contains a `PagePanels` row.
56
+ PageLayout — top-level window chrome. Renders a fixed-height header (title + actions + an optional
57
+ group of chrome buttons) above a flex-column body that typically contains a `PagePanels` row.
43
58
 
44
- Pass `on.windowToggle` to surface a navigation-toggle button at the header's right edge wire it to
45
- whatever sidebar / drawer state your app owns. Omit it to hide the button. Set `scrollable` to let
46
- the whole page scroll as one document with sticky panel headers.
59
+ `navigationToggle` surfaces the app-navigation button as a bare glyph; `editorControls` is the fixed
60
+ editor pair panels toggle + close, both required so an editor never ships half the preset, drawn as
61
+ outlined squares. Every entry takes `{ active?, onClick }`, and the group renders in the order
62
+ navigation → panels → close. Set `scrollable` to let the whole page scroll as one document with
63
+ sticky panel headers.
47
64
 
48
65
  ### CSS Custom Properties
49
66
  | Property | Description | Default |
@@ -86,27 +103,10 @@ the whole page scroll as one document with sticky panel headers.
86
103
  align-items: center;
87
104
  gap: 1rem;
88
105
  }
89
- .page-layout__window-toggle {
90
- --sc-kit--icon--size: 1.125rem;
91
- display: inline-flex;
106
+ .page-layout__controls {
107
+ display: flex;
92
108
  align-items: center;
93
- justify-content: center;
94
- width: 1.75rem;
95
- height: 1.75rem;
96
- padding: 0;
97
- background: none;
98
- border: none;
99
- border-radius: var(--sc-kit--radius--sm);
100
- color: var(--sc-kit--color--text--muted);
101
- cursor: pointer;
102
- }
103
- .page-layout__window-toggle:hover {
104
- background: var(--sc-kit--color--bg--hover);
105
- color: var(--sc-kit--color--text--primary);
106
- }
107
- .page-layout__window-toggle--active {
108
- background: var(--sc-kit--color--bg--active);
109
- color: var(--sc-kit--color--text--primary);
109
+ gap: var(--sc-kit--space--2);
110
110
  }
111
111
  .page-layout__body {
112
112
  flex: 1;
@@ -1,23 +1,24 @@
1
+ import type { EditorControls, PageLayoutAction } from './types';
1
2
  import type { Snippet } from 'svelte';
2
3
  type Props = {
3
4
  children: Snippet;
4
5
  title?: string | Snippet;
5
6
  actions?: Snippet;
6
- /** Visual + ARIA state for the window-toggle button. Set `true` when the navigation it controls is hidden / collapsed — the button gets `aria-pressed` and an active background. @default false */
7
- windowToggleActive?: boolean;
7
+ /** Hides / shows the host app's own navigation. When set, a bare glyph button is rendered in the header's control group. */
8
+ navigationToggle?: PageLayoutAction;
9
+ /** Editor chrome — the fixed panels-toggle + close pair. Set it to render both buttons; omit it entirely for a non-editor page. */
10
+ editorControls?: EditorControls;
8
11
  scrollable?: boolean;
9
- on?: {
10
- /** When set, a window-toggle button is rendered at the right edge of the header. */
11
- windowToggle?: () => void;
12
- };
13
12
  };
14
13
  /**
15
- * PageLayout — top-level window chrome. Renders a fixed-height header (title + actions + optional
16
- * window-toggle button) above a flex-column body that typically contains a `PagePanels` row.
14
+ * PageLayout — top-level window chrome. Renders a fixed-height header (title + actions + an optional
15
+ * group of chrome buttons) above a flex-column body that typically contains a `PagePanels` row.
17
16
  *
18
- * Pass `on.windowToggle` to surface a navigation-toggle button at the header's right edge wire it to
19
- * whatever sidebar / drawer state your app owns. Omit it to hide the button. Set `scrollable` to let
20
- * the whole page scroll as one document with sticky panel headers.
17
+ * `navigationToggle` surfaces the app-navigation button as a bare glyph; `editorControls` is the fixed
18
+ * editor pair panels toggle + close, both required so an editor never ships half the preset, drawn as
19
+ * outlined squares. Every entry takes `{ active?, onClick }`, and the group renders in the order
20
+ * navigation → panels → close. Set `scrollable` to let the whole page scroll as one document with
21
+ * sticky panel headers.
21
22
  *
22
23
  * ### CSS Custom Properties
23
24
  * | Property | Description | Default |
@@ -5,4 +5,4 @@ export { default as PagePanels } from './cmp.page-panels.svelte';
5
5
  export { default as PanelCollapseButton } from './cmp.panel-collapse-button.svelte';
6
6
  export { default as PanelContent } from './cmp.panel-content.svelte';
7
7
  export { PanelEntry, PanelState } from './panel-state.svelte';
8
- export type { PanelBorder, PanelResizingSettings } from './types';
8
+ export type { EditorControls, PageLayoutAction, PanelBorder, PanelResizingSettings } from './types';
@@ -0,0 +1,71 @@
1
+ <script lang="ts">import { Icon } from '../icon';
2
+ const { icon, label, variant = 'outlined', active, on } = $props();
3
+ </script>
4
+
5
+ <button
6
+ type="button"
7
+ class="layout-control-button layout-control-button--{variant}"
8
+ class:layout-control-button--active={active}
9
+ aria-label={label}
10
+ aria-pressed={active}
11
+ onclick={on.click}>
12
+ <Icon src={icon} size={variant === 'flat' ? 'control-xl' : 'control-md'} />
13
+ </button>
14
+
15
+ <!--
16
+ @component
17
+ Folder-internal chrome button for the `PageLayout` header. Two variants: `flat` for the
18
+ app-navigation toggle and `outlined` for the editor pair. Rendered by `PageLayout`; not exported.
19
+ -->
20
+ <style>.layout-control-button {
21
+ display: inline-flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ flex-shrink: 0;
25
+ inline-size: var(--_lcb--size);
26
+ block-size: var(--_lcb--size);
27
+ padding: 0;
28
+ color: var(--_lcb--color);
29
+ background: var(--_lcb--background);
30
+ border: 1px solid var(--_lcb--border-color);
31
+ border-radius: var(--_lcb--border-radius);
32
+ cursor: pointer;
33
+ transition: background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), border-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), color var(--sc-kit--duration--base) var(--sc-kit--ease--default);
34
+ }
35
+ .layout-control-button--outlined {
36
+ --_lcb--size: var(--sc-kit--field--height--md);
37
+ --_lcb--border-radius: var(--sc-kit--field--border-radius--md);
38
+ --_lcb--color: var(--sc-kit--color--text--primary);
39
+ --_lcb--color-hover: var(--sc-kit--color--text--primary);
40
+ --_lcb--color-active: var(--sc-kit--color--text--primary);
41
+ --_lcb--background: var(--sc-kit--color--bg--panel);
42
+ --_lcb--background-hover: var(--sc-kit--color--bg--hover);
43
+ --_lcb--background-active: var(--sc-kit--color--bg--active);
44
+ --_lcb--border-color: var(--sc-kit--color--border);
45
+ --_lcb--border-color-active: var(--sc-kit--color--border--strong);
46
+ }
47
+ .layout-control-button--flat {
48
+ --_lcb--size: 1.75rem;
49
+ --_lcb--border-radius: var(--sc-kit--radius--sm);
50
+ --_lcb--color: var(--sc-kit--color--text--muted);
51
+ --_lcb--color-hover: var(--sc-kit--color--text--primary);
52
+ --_lcb--color-active: var(--sc-kit--color--text--primary);
53
+ --_lcb--background: transparent;
54
+ --_lcb--background-hover: var(--sc-kit--color--bg--hover);
55
+ --_lcb--background-active: var(--sc-kit--color--bg--active);
56
+ --_lcb--border-color: transparent;
57
+ --_lcb--border-color-active: transparent;
58
+ }
59
+ .layout-control-button:hover {
60
+ color: var(--_lcb--color-hover);
61
+ background: var(--_lcb--background-hover);
62
+ }
63
+ .layout-control-button:focus-visible {
64
+ outline: 2px solid var(--sc-kit--color--border--focus);
65
+ outline-offset: 2px;
66
+ }
67
+ .layout-control-button--active {
68
+ color: var(--_lcb--color-active);
69
+ background: var(--_lcb--background-active);
70
+ border-color: var(--_lcb--border-color-active);
71
+ }</style>
@@ -0,0 +1,19 @@
1
+ type Props = {
2
+ /** Glyph — a string SVG source. */
3
+ icon: string;
4
+ label: string;
5
+ /** `flat` = bare 20px glyph in a 28px hit area (app-navigation toggle); `outlined` = 32px bordered square with a 16px glyph (editor chrome). @default 'outlined' */
6
+ variant?: 'outlined' | 'flat';
7
+ /** Toggle state. Leave undefined for a plain action button so no `aria-pressed` is emitted. */
8
+ active?: boolean;
9
+ on: {
10
+ click: () => void;
11
+ };
12
+ };
13
+ /**
14
+ * Folder-internal chrome button for the `PageLayout` header. Two variants: `flat` for the
15
+ * app-navigation toggle and `outlined` for the editor pair. Rendered by `PageLayout`; not exported.
16
+ */
17
+ declare const LayoutControlButton: import("svelte").Component<Props, {}, "">;
18
+ type LayoutControlButton = ReturnType<typeof LayoutControlButton>;
19
+ export default LayoutControlButton;
@@ -2,4 +2,6 @@ export declare class PageLayoutLocalization {
2
2
  get resizePanel(): string;
3
3
  get togglePanel(): string;
4
4
  get toggleNavigation(): string;
5
+ get togglePanels(): string;
6
+ get close(): string;
5
7
  }
@@ -11,6 +11,14 @@ const loc = {
11
11
  toggleNavigation: {
12
12
  en: 'Toggle navigation',
13
13
  no: 'Veksle navigasjon'
14
+ },
15
+ togglePanels: {
16
+ en: 'Toggle panels',
17
+ no: 'Veksle paneler'
18
+ },
19
+ close: {
20
+ en: 'Close',
21
+ no: 'Lukk'
14
22
  }
15
23
  };
16
24
  export class PageLayoutLocalization {
@@ -23,4 +31,10 @@ export class PageLayoutLocalization {
23
31
  get toggleNavigation() {
24
32
  return loc.toggleNavigation[AppLocale.current];
25
33
  }
34
+ get togglePanels() {
35
+ return loc.togglePanels[AppLocale.current];
36
+ }
37
+ get close() {
38
+ return loc.close[AppLocale.current];
39
+ }
26
40
  }
@@ -17,3 +17,16 @@ export type PanelResizingSettings = {
17
17
  maxWidth?: number;
18
18
  };
19
19
  export type PanelBorder = 'top' | 'right' | 'bottom' | 'left';
20
+ /**
21
+ * Config for one `PageLayout` header chrome button. Set `active` only for toggles — it drives both
22
+ * `aria-pressed` and the pressed background, and is omitted entirely for plain action buttons.
23
+ */
24
+ export type PageLayoutAction = {
25
+ active?: boolean;
26
+ onClick: () => void;
27
+ };
28
+ /** Editor chrome — a fixed pair, so an editor never ships half of it. */
29
+ export type EditorControls = {
30
+ panelsToggle: PageLayoutAction;
31
+ close: PageLayoutAction;
32
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",