bref-ui 0.0.13 → 0.1.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 (44) hide show
  1. package/README.md +1 -0
  2. package/dist/base/index.d.ts +1 -0
  3. package/dist/base/index.js +1 -0
  4. package/dist/base/theme/theme.svelte +1 -2
  5. package/dist/base/tree-view/index.d.ts +3 -0
  6. package/dist/base/tree-view/index.js +3 -0
  7. package/dist/base/tree-view/tree-node.svelte +108 -0
  8. package/dist/base/tree-view/tree-node.svelte.d.ts +5 -0
  9. package/dist/base/tree-view/tree-view.svelte +39 -0
  10. package/dist/base/tree-view/tree-view.svelte.d.ts +4 -0
  11. package/dist/base/tree-view/types.d.ts +22 -0
  12. package/dist/base/tree-view/types.js +1 -0
  13. package/dist/internal/{demo-code-snippet.svelte → layout/code-snippet.svelte} +16 -14
  14. package/dist/internal/layout/code-snippet.svelte.d.ts +6 -0
  15. package/dist/internal/layout/header.svelte +50 -0
  16. package/dist/internal/layout/header.svelte.d.ts +3 -0
  17. package/dist/internal/{demo-section.svelte → layout/section.svelte} +2 -11
  18. package/dist/internal/layout/section.svelte.d.ts +10 -0
  19. package/dist/internal/layout/sidebar/footer.svelte +71 -0
  20. package/dist/internal/layout/sidebar/footer.svelte.d.ts +3 -0
  21. package/dist/internal/layout/sidebar/logo.svelte +38 -0
  22. package/dist/internal/{demo-header.svelte.d.ts → layout/sidebar/logo.svelte.d.ts} +14 -6
  23. package/dist/internal/layout/sidebar/sidebar.svelte +52 -0
  24. package/dist/internal/layout/sidebar/sidebar.svelte.d.ts +3 -0
  25. package/dist/internal/layout/types.d.ts +9 -0
  26. package/dist/internal/layout/types.js +84 -0
  27. package/package.json +1 -1
  28. package/dist/internal/demo-author.svelte +0 -29
  29. package/dist/internal/demo-author.svelte.d.ts +0 -18
  30. package/dist/internal/demo-button.svelte +0 -80
  31. package/dist/internal/demo-button.svelte.d.ts +0 -18
  32. package/dist/internal/demo-code-snippet.svelte.d.ts +0 -6
  33. package/dist/internal/demo-header.svelte +0 -57
  34. package/dist/internal/demo-icon-button.svelte +0 -76
  35. package/dist/internal/demo-icon-button.svelte.d.ts +0 -18
  36. package/dist/internal/demo-icon-section.svelte +0 -46
  37. package/dist/internal/demo-icon-section.svelte.d.ts +0 -18
  38. package/dist/internal/demo-loading.svelte +0 -95
  39. package/dist/internal/demo-loading.svelte.d.ts +0 -18
  40. package/dist/internal/demo-section.svelte.d.ts +0 -10
  41. package/dist/internal/demo-theming.svelte +0 -173
  42. package/dist/internal/demo-theming.svelte.d.ts +0 -3
  43. package/dist/internal/demo-types.svelte +0 -61
  44. package/dist/internal/demo-types.svelte.d.ts +0 -18
package/README.md CHANGED
@@ -63,6 +63,7 @@ Bref handles:
63
63
  - [x] Pulsing Dots
64
64
  - [x] Morphing Shapes
65
65
  - [x] Textual
66
+ - [x] Tree View
66
67
  - [ ] Input
67
68
  - [ ] Textarea
68
69
  - [ ] Avatar
@@ -3,3 +3,4 @@ export * from './icon/index.ts';
3
3
  export * from './theme/index.ts';
4
4
  export * from './button/index.ts';
5
5
  export * from './loading/index.ts';
6
+ export * from './tree-view/index.ts';
@@ -3,3 +3,4 @@ export * from "./icon/index.js";
3
3
  export * from "./theme/index.js";
4
4
  export * from "./button/index.js";
5
5
  export * from "./loading/index.js";
6
+ export * from "./tree-view/index.js";
@@ -51,8 +51,7 @@
51
51
  color: var(--color-foreground);
52
52
  }
53
53
 
54
- :global(body),
55
- :global(main) {
54
+ :global(body) {
56
55
  background-color: var(--color-background);
57
56
  }
58
57
 
@@ -0,0 +1,3 @@
1
+ export { default as TreeView } from './tree-view.svelte';
2
+ export { default as TreeNode } from './tree-node.svelte';
3
+ export * from './types.ts';
@@ -0,0 +1,3 @@
1
+ export { default as TreeView } from './tree-view.svelte';
2
+ export { default as TreeNode } from './tree-node.svelte';
3
+ export * from "./types.js";
@@ -0,0 +1,108 @@
1
+ <script lang="ts">
2
+ import type { TreeNodeProps } from './types.js';
3
+ import Icon from '../icon/icon.svelte';
4
+ import TreeNode from './tree-node.svelte';
5
+ const { node, level, size, onSelect, selectedIds, filledIcon, wide }: TreeNodeProps = $props();
6
+
7
+ let expanded: boolean = $state(false);
8
+ let selected: boolean = $derived(selectedIds?.has(node.id) ?? false);
9
+
10
+ const hasChildren = $derived(node.children && node.children.length > 0);
11
+
12
+ const toggleExpand = (e: MouseEvent) => {
13
+ e.stopPropagation();
14
+ if (hasChildren) expanded = !expanded;
15
+ };
16
+
17
+ const handleSelect = () => onSelect(node.id);
18
+ </script>
19
+
20
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
21
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
22
+ <div
23
+ class={size}
24
+ class:selected
25
+ style:padding-left="calc({level} * var(--tree-indent))"
26
+ onclick={handleSelect}
27
+ class:wide
28
+ >
29
+ {#if hasChildren}
30
+ <button class:expanded onclick={toggleExpand}>
31
+ <Icon name="chevron_right" {size} color={selected ? 'primary' : undefined} />
32
+ </button>
33
+ {:else}
34
+ <span class="spacer"></span>
35
+ {/if}
36
+
37
+ {#if node.icon}
38
+ <Icon
39
+ name={node.icon}
40
+ filled={selected || filledIcon}
41
+ {size}
42
+ color={selected ? 'primary' : undefined}
43
+ />
44
+ {/if}
45
+
46
+ <span>{node.label}</span>
47
+ </div>
48
+
49
+ {#if expanded && hasChildren}
50
+ {#each node.children as child (child.id)}
51
+ <TreeNode node={child} level={level + 1} {size} {onSelect} {selectedIds} {filledIcon} {wide} />
52
+ {/each}
53
+ {/if}
54
+
55
+ <style>
56
+ div,
57
+ span,
58
+ button {
59
+ transition: all 0.1s ease-in-out;
60
+ }
61
+
62
+ div {
63
+ --tree-indent: 1em;
64
+ display: flex;
65
+ align-items: center;
66
+ padding: 0.25em 0.5em;
67
+ gap: 0.25em;
68
+ border-radius: var(--border-radius);
69
+ cursor: pointer;
70
+ }
71
+
72
+ div:hover {
73
+ background-color: color-mix(in srgb, var(--color-foreground) 10%, transparent);
74
+ }
75
+
76
+ .selected {
77
+ background-color: var(--color-primary-soft);
78
+ }
79
+ .selected:hover {
80
+ background-color: color-mix(in srgb, var(--color-primary) 20%, var(--color-primary-soft));
81
+ }
82
+
83
+ .selected span {
84
+ color: var(--color-primary);
85
+ }
86
+ span {
87
+ user-select: none;
88
+ }
89
+
90
+ button {
91
+ all: unset;
92
+ display: flex;
93
+ padding: 0.25em;
94
+ cursor: pointer;
95
+ }
96
+
97
+ button.expanded {
98
+ transform: rotate(90deg);
99
+ }
100
+
101
+ .spacer {
102
+ width: 1em;
103
+ }
104
+
105
+ .wide {
106
+ width: 100%;
107
+ }
108
+ </style>
@@ -0,0 +1,5 @@
1
+ import type { TreeNodeProps } from './types.ts';
2
+ import TreeNode from './tree-node.svelte';
3
+ declare const TreeNode: import("svelte").Component<TreeNodeProps, {}, "">;
4
+ type TreeNode = ReturnType<typeof TreeNode>;
5
+ export default TreeNode;
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ import type { TreeViewProps } from './types.js';
3
+ import TreeNode from './tree-node.svelte';
4
+
5
+ let { data, size = 'medium', onSelect, selectedIds, wide, filledIcon }: TreeViewProps = $props();
6
+ </script>
7
+
8
+ <div role="tree" class={size} class:wide>
9
+ {#each data as node (node.id)}
10
+ <TreeNode {node} level={0} {size} {onSelect} {selectedIds} {wide} {filledIcon} />
11
+ {/each}
12
+ </div>
13
+
14
+ <style>
15
+ div {
16
+ display: flex;
17
+ flex-direction: column;
18
+ }
19
+
20
+ .wide {
21
+ width: 100%;
22
+ }
23
+
24
+ .x-small {
25
+ gap: 0.125rem;
26
+ }
27
+ .small {
28
+ gap: 0.25rem;
29
+ }
30
+ .medium {
31
+ gap: 0.5rem;
32
+ }
33
+ .large {
34
+ gap: 0.75rem;
35
+ }
36
+ .x-large {
37
+ gap: 1rem;
38
+ }
39
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { TreeViewProps } from './types.ts';
2
+ declare const TreeView: import("svelte").Component<TreeViewProps, {}, "">;
3
+ type TreeView = ReturnType<typeof TreeView>;
4
+ export default TreeView;
@@ -0,0 +1,22 @@
1
+ import type { Size } from '../types.ts';
2
+ import type { IconName, IconProps } from '../icon/types.ts';
3
+ export interface NodeDataProps {
4
+ id: string;
5
+ label: string;
6
+ icon?: IconName;
7
+ children?: NodeDataProps[];
8
+ }
9
+ export interface BaseTreeProps {
10
+ onSelect: (id: string) => void;
11
+ selectedIds: Set<string>;
12
+ size?: Size;
13
+ filledIcon?: IconProps['filled'];
14
+ wide?: boolean;
15
+ }
16
+ export interface TreeViewProps extends BaseTreeProps {
17
+ data: NodeDataProps[];
18
+ }
19
+ export interface TreeNodeProps extends BaseTreeProps {
20
+ node: NodeDataProps;
21
+ level: number;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import IconButton from '../../base/button/icon-button.svelte';
3
+
2
4
  const {
3
5
  snippet
4
6
  }: {
@@ -7,7 +9,7 @@
7
9
 
8
10
  let hasCopied = $state(false);
9
11
 
10
- const onCopyClick = () => {
12
+ const onClick = () => {
11
13
  navigator.clipboard.writeText(snippet).then(() => {
12
14
  hasCopied = true;
13
15
  setTimeout(() => (hasCopied = false), 3000);
@@ -19,17 +21,26 @@
19
21
  <pre>
20
22
  <code>{snippet}</code>
21
23
  </pre>
22
- <button onclick={onCopyClick}>copy</button>
24
+
25
+ <IconButton
26
+ {onClick}
27
+ size="x-small"
28
+ color={hasCopied ? 'success' : 'primary'}
29
+ variant={hasCopied ? 'filled' : 'soft'}
30
+ filled
31
+ name={hasCopied ? 'check' : 'copy_all'}
32
+ disabled={hasCopied}
33
+ />
23
34
  </div>
24
35
 
25
36
  <style>
26
37
  div {
27
38
  display: flex;
28
39
  width: 100%;
40
+ position: relative;
29
41
  height: fit-content;
30
42
  padding: 1rem;
31
- gap: 1rem;
32
- flex-direction: column;
43
+ gap: 0.5rem;
33
44
  border-radius: 0.75rem;
34
45
  background: color-mix(in srgb, var(--color-foreground) 5%, transparent);
35
46
  border: 1px solid color-mix(in srgb, var(--color-border) 30%, transparent);
@@ -48,6 +59,7 @@
48
59
  code,
49
60
  pre {
50
61
  width: 100%;
62
+ flex: 1; align-self: center;
51
63
  height: fit-content;
52
64
  }
53
65
  pre {
@@ -55,14 +67,4 @@
55
67
  display: flex;
56
68
  justify-content: flex-start;
57
69
  }
58
- button {
59
- display: flex;
60
- padding: 0.5rem 1rem;
61
- width: 100%;
62
- height: fit-content;
63
- text-transform: capitalize;
64
- align-items: center;
65
- cursor: pointer;
66
- justify-content: center;
67
- }
68
70
  </style>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ snippet: string;
3
+ };
4
+ declare const CodeSnippet: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type CodeSnippet = ReturnType<typeof CodeSnippet>;
6
+ export default CodeSnippet;
@@ -0,0 +1,50 @@
1
+ <script lang="ts">
2
+ import Icon from '../../base/icon/icon.svelte';
3
+ import { PAGES, type PageProps } from '../layout/types.js';
4
+ import { page } from '$app/state';
5
+
6
+ const { title, description, icon } = $derived.by((): PageProps => {
7
+ if (page.status !== 200) {
8
+ return {
9
+ title: 'Error: ' + page.status,
10
+ description: page.error?.message || 'An unexpected error occurred.',
11
+ icon: 'error',
12
+ href: ''
13
+ };
14
+ }
15
+ const firstPage = PAGES[0];
16
+ const allPages = PAGES.flatMap((p) => [p, ...(p.children || [])]);
17
+ const currentPage = allPages.find((p) => page.url.pathname.endsWith(p.href));
18
+ return currentPage || firstPage;
19
+ });
20
+ </script>
21
+
22
+ <div class="container">
23
+ <div>
24
+ <Icon name={icon} size="large" color="primary" />
25
+ <h1>{title}</h1>
26
+ </div>
27
+ <p>{description}</p>
28
+ </div>
29
+
30
+ <style>
31
+ div {
32
+ display: flex;
33
+ align-items: center;
34
+ min-height: fit-content;
35
+ height: fit-content;
36
+ }
37
+ .container {
38
+ width: 100%;
39
+ padding: 2rem;
40
+ flex-direction: column;
41
+ align-items: flex-start;
42
+ flex-shrink: 0;
43
+ }
44
+ p {
45
+ opacity: 0.8;
46
+ }
47
+ h1 {
48
+ font-size: 3rem;
49
+ }
50
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const Header: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type Header = ReturnType<typeof Header>;
3
+ export default Header;
@@ -14,7 +14,7 @@
14
14
  } = $props();
15
15
  </script>
16
16
 
17
- <section {id}>
17
+ <section {id} class="container">
18
18
  {#if title}
19
19
  <h2>{title}</h2>
20
20
  {/if}
@@ -30,18 +30,9 @@
30
30
  gap: 1rem;
31
31
  align-items: center;
32
32
  flex-direction: column;
33
- background-color: color-mix(in srgb, var(--color-background) 90%, transparent);
34
- backdrop-filter: blur(2rem);
35
33
  padding: 2rem;
36
34
  width: 100%;
37
- max-width: 50rem;
38
- transition: all 0.3s ease;
39
- border-radius: 2rem;
40
- border: 1px solid color-mix(in srgb, var(--color-foreground) 10%, transparent);
41
- }
42
- section:hover {
43
- border: 1px solid color-mix(in srgb, var(--color-foreground) 20%, transparent);
44
- box-shadow: 0 0 3rem color-mix(in srgb, var(--color-background) 50%, transparent);
35
+ flex-shrink: 0;
45
36
  }
46
37
  h2 {
47
38
  font-size: 2rem;
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children: Snippet;
4
+ title?: string;
5
+ description?: string;
6
+ id?: string;
7
+ };
8
+ declare const Section: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type Section = ReturnType<typeof Section>;
10
+ export default Section;
@@ -0,0 +1,71 @@
1
+ <script lang="ts">
2
+ import IconButton from '../../../base/button/icon-button.svelte';
3
+ import {
4
+ type ThemeMode,
5
+ initializeThemeMode,
6
+ toggleThemeMode
7
+ } from '../../../base/theme/index.js';
8
+ import { untrack } from 'svelte';
9
+ import Icon from '../../../base/icon/icon.svelte';
10
+
11
+ let themeMode: ThemeMode = $state('light');
12
+
13
+ $effect.pre(() => {
14
+ untrack(() => {
15
+ themeMode = initializeThemeMode();
16
+ });
17
+ });
18
+
19
+ const onClick = () => {
20
+ const newMode = themeMode === 'dark' ? 'light' : 'dark';
21
+ toggleThemeMode(newMode);
22
+ themeMode = newMode;
23
+ };
24
+ </script>
25
+
26
+ <div>
27
+ <IconButton
28
+ variant="ghost"
29
+ name={themeMode === 'dark' ? 'light_mode' : 'dark_mode'}
30
+ ariaLabel="Toggle theme"
31
+ {onClick}
32
+ />
33
+ <span>
34
+ Made with <Icon name="favorite" color="danger" filled size="x-small" /> in Paris, by
35
+ <a href="https://github.com/feuersteiner" target="_blank" rel="noopener noreferrer"
36
+ >Feuerstein.</a
37
+ >
38
+ </span>
39
+ </div>
40
+
41
+ <style>
42
+ div {
43
+ display: flex;
44
+ justify-content: center;
45
+ align-items: center;
46
+ width: 100%;
47
+ height: fit-content;
48
+ flex-direction: column;
49
+ gap: 0.5rem;
50
+ padding: 1rem;
51
+ }
52
+
53
+ a {
54
+ text-decoration: none;
55
+ color: inherit;
56
+ display: inline-block;
57
+ width: 5rem;
58
+ height: 1.5rem;
59
+ }
60
+
61
+ a:hover {
62
+ font-weight: bold;
63
+ }
64
+
65
+ span {
66
+ font-size: 0.75rem;
67
+ text-align: center;
68
+ align-items: center;
69
+ color: color-mix(in srgb, var(--color-foreground) 50%, var(--color-background));
70
+ }
71
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const Footer: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type Footer = ReturnType<typeof Footer>;
3
+ export default Footer;
@@ -0,0 +1,38 @@
1
+ <div class="logo">
2
+ <div class="icon">
3
+ <img src={'favicon.svg'} alt="Logo" />
4
+ <span>Bref</span>
5
+ </div>
6
+
7
+ <p>A truly Svelte-esque UI Component Library.</p>
8
+ </div>
9
+
10
+ <style>
11
+ div {
12
+ display: flex;
13
+ gap: 1rem;
14
+ }
15
+ .icon {
16
+ align-items: center;
17
+ }
18
+ .logo {
19
+ padding: 1rem;
20
+ flex-direction: column;
21
+ width: 100%;
22
+ height: fit-content;
23
+ background-color: color-mix(in srgb, var(--color-foreground) 5%, transparent);
24
+ }
25
+ span {
26
+ font-size: 2rem;
27
+ font-family: 'Meow Script', cursive;
28
+ }
29
+ p {
30
+ font-size: 0.75rem;
31
+ opacity: 0.7;
32
+ font-weight: 300;
33
+ }
34
+ img {
35
+ width: 3rem;
36
+ height: 3rem;
37
+ }
38
+ </style>
@@ -1,5 +1,18 @@
1
+ export default Logo;
2
+ type Logo = SvelteComponent<{
3
+ [x: string]: never;
4
+ }, {
5
+ [evt: string]: CustomEvent<any>;
6
+ }, {}> & {
7
+ $$bindings?: string | undefined;
8
+ };
9
+ declare const Logo: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
1
14
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
15
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
3
16
  $$bindings?: Bindings;
4
17
  } & Exports;
5
18
  (internal: unknown, props: {
@@ -11,8 +24,3 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
11
24
  };
12
25
  z_$$bindings?: Bindings;
13
26
  }
14
- declare const DemoHeader: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {}, {}, string>;
17
- type DemoHeader = InstanceType<typeof DemoHeader>;
18
- export default DemoHeader;
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import Logo from './logo.svelte';
3
+ import Footer from './footer.svelte';
4
+ import { PAGES, type PageProps } from '../types.js';
5
+ import { page } from '$app/state';
6
+ import TreeView from '../../../base/tree-view/tree-view.svelte';
7
+ import type { NodeDataProps } from '../../../index.js';
8
+ import { goto } from '$app/navigation';
9
+
10
+ const pageToNode = (page: PageProps): NodeDataProps => ({
11
+ id: page.href,
12
+ label: page.title,
13
+ icon: page.icon,
14
+ children: page.children?.map(pageToNode)
15
+ });
16
+ const selectedPage = $derived.by(() => {
17
+ const allPages = PAGES.flatMap((p) => [p, ...(p.children || [])]);
18
+ return allPages.find((p) => page.url.pathname.endsWith(p.href)) ?? PAGES[0];
19
+ });
20
+ const selectedIds = $derived(new Set([selectedPage.href]));
21
+ const onSelect = (href: string) => goto(href);
22
+ </script>
23
+
24
+ <div class="container">
25
+ <Logo />
26
+ <div class="content">
27
+ <TreeView wide size="small" {selectedIds} {onSelect} data={PAGES.map(pageToNode)} />
28
+ </div>
29
+ <Footer />
30
+ </div>
31
+
32
+ <style>
33
+ div {
34
+ display: flex;
35
+ justify-content: center;
36
+ align-items: center;
37
+ height: 100%;
38
+ width: 100%;
39
+ }
40
+ .container {
41
+ max-width: 15rem;
42
+ width: 15rem;
43
+ /* height: 100%; */
44
+ }
45
+ .content {
46
+ flex: 1;
47
+ width: 100%;
48
+ justify-content: flex-start;
49
+ align-items: flex-start;
50
+ padding: 1rem;
51
+ }
52
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const Sidebar: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type Sidebar = ReturnType<typeof Sidebar>;
3
+ export default Sidebar;
@@ -0,0 +1,9 @@
1
+ import type { IconName } from '../../base/icon/types.ts';
2
+ export interface PageProps {
3
+ title: string;
4
+ description: string;
5
+ href: string;
6
+ icon: IconName;
7
+ children?: PageProps[];
8
+ }
9
+ export declare const PAGES: PageProps[];