@hyvor/design 1.1.13 → 1.1.15

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 (29) hide show
  1. package/dist/components/Dropdown/Dropdown.svelte +7 -3
  2. package/dist/components/Dropdown/Dropdown.svelte.d.ts +4 -2
  3. package/dist/components/Dropdown/DropdownContent.svelte +5 -9
  4. package/dist/components/Dropdown/DropdownContent.svelte.d.ts +1 -0
  5. package/dist/components/Dropdown/dropdown.types.d.ts +2 -0
  6. package/dist/components/Dropdown/dropdown.types.js +1 -0
  7. package/dist/components/EmojiPicker/EmojiPicker.svelte +70 -0
  8. package/dist/components/EmojiPicker/EmojiPicker.svelte.d.ts +14 -0
  9. package/dist/components/EmojiPicker/EmojiSelector.svelte +214 -0
  10. package/dist/components/EmojiPicker/EmojiSelector.svelte.d.ts +8 -0
  11. package/dist/components/EmojiPicker/emojidata.d.ts +22 -0
  12. package/dist/components/EmojiPicker/emojidata.js +31 -0
  13. package/dist/components/HyvorBar/BarUser.svelte +22 -18
  14. package/dist/components/HyvorBar/BarUser.svelte.d.ts +2 -0
  15. package/dist/components/HyvorBar/BarUserPicture.svelte +33 -0
  16. package/dist/components/HyvorBar/BarUserPicture.svelte.d.ts +18 -0
  17. package/dist/components/HyvorBar/BarUserPreview.svelte +9 -2
  18. package/dist/components/HyvorBar/HyvorBar.svelte +44 -11
  19. package/dist/components/HyvorBar/HyvorBar.svelte.d.ts +11 -1
  20. package/dist/components/HyvorBar/bar.d.ts +4 -5
  21. package/dist/components/HyvorBar/bar.js +2 -2
  22. package/dist/components/IconButton/IconButton.svelte +37 -13
  23. package/dist/components/IconButton/IconButton.svelte.d.ts +13 -2
  24. package/dist/components/IconButton/iconButton.types.d.ts +2 -0
  25. package/dist/components/IconButton/iconButton.types.js +1 -0
  26. package/dist/components/TextInput/TextInput.svelte +48 -22
  27. package/dist/components/TextInput/TextInput.svelte.d.ts +11 -0
  28. package/dist/variables.scss +5 -2
  29. package/package.json +2 -1
@@ -1,16 +1,18 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import DropdownContent from './DropdownContent.svelte';
4
+ import type { DropdownAlign, DropdownPosition } from './dropdown.types.js';
4
5
 
5
6
  interface Props {
6
7
  show?: boolean;
7
8
  width?: number;
8
9
  relative?: boolean;
9
10
  closeOnOutsideClick?: boolean;
10
- align?: 'start' | 'center' | 'end';
11
- position?: 'left' | 'right' | 'bottom' | 'top';
11
+ align?: DropdownAlign;
12
+ position?: DropdownPosition;
12
13
  trigger?: Snippet;
13
14
  content?: Snippet;
15
+ contentPadding?: number;
14
16
  }
15
17
 
16
18
  let {
@@ -21,7 +23,8 @@
21
23
  align = 'start',
22
24
  position = 'bottom',
23
25
  trigger,
24
- content
26
+ content,
27
+ contentPadding
25
28
  }: Props = $props();
26
29
 
27
30
  let triggerEl: HTMLElement | undefined = $state();
@@ -52,6 +55,7 @@
52
55
  {position}
53
56
  {relative}
54
57
  trigger={triggerEl}
58
+ padding={contentPadding}
55
59
  >
56
60
  {@render content?.()}
57
61
  </DropdownContent>
@@ -1,13 +1,15 @@
1
1
  import type { Snippet } from 'svelte';
2
+ import type { DropdownAlign, DropdownPosition } from './dropdown.types.js';
2
3
  interface Props {
3
4
  show?: boolean;
4
5
  width?: number;
5
6
  relative?: boolean;
6
7
  closeOnOutsideClick?: boolean;
7
- align?: 'start' | 'center' | 'end';
8
- position?: 'left' | 'right' | 'bottom' | 'top';
8
+ align?: DropdownAlign;
9
+ position?: DropdownPosition;
9
10
  trigger?: Snippet;
10
11
  content?: Snippet;
12
+ contentPadding?: number;
11
13
  }
12
14
  declare const Dropdown: import("svelte").Component<Props, {}, "show">;
13
15
  type Dropdown = ReturnType<typeof Dropdown>;
@@ -1,6 +1,4 @@
1
1
  <script lang="ts">
2
- import { run } from 'svelte/legacy';
3
-
4
2
  import { onMount } from 'svelte';
5
3
  import { clickOutside } from '../directives/clickOutside.js';
6
4
  import debounce from '../directives/debounce.js';
@@ -15,6 +13,7 @@
15
13
  position: 'left' | 'right' | 'bottom' | 'top';
16
14
  trigger: HTMLElement;
17
15
  children?: import('svelte').Snippet;
16
+ padding?: number;
18
17
  }
19
18
 
20
19
  let {
@@ -25,7 +24,8 @@
25
24
  align,
26
25
  position,
27
26
  trigger,
28
- children
27
+ children,
28
+ padding = 10
29
29
  }: Props = $props();
30
30
 
31
31
  let contentWrap: HTMLElement | undefined = $state();
@@ -94,7 +94,7 @@
94
94
  position;
95
95
  align;
96
96
  positionWrap();
97
- })
97
+ });
98
98
 
99
99
  function debouncedPosition() {
100
100
  debounce(positionWrap, 10)();
@@ -142,7 +142,7 @@
142
142
  style="width: {width}px"
143
143
  transition:slideIn
144
144
  >
145
- <div class="hds-box content">
145
+ <div class="hds-box content" style:padding="{padding}px">
146
146
  {@render children?.()}
147
147
  </div>
148
148
  </div>
@@ -150,8 +150,4 @@
150
150
  <style>.content-wrap {
151
151
  position: fixed;
152
152
  z-index: 1000000;
153
- }
154
-
155
- .content-wrap > .content {
156
- padding: 10px;
157
153
  }</style>
@@ -7,6 +7,7 @@ interface Props {
7
7
  position: 'left' | 'right' | 'bottom' | 'top';
8
8
  trigger: HTMLElement;
9
9
  children?: import('svelte').Snippet;
10
+ padding?: number;
10
11
  }
11
12
  declare const DropdownContent: import("svelte").Component<Props, {}, "show">;
12
13
  type DropdownContent = ReturnType<typeof DropdownContent>;
@@ -0,0 +1,2 @@
1
+ export type DropdownAlign = 'start' | 'center' | 'end';
2
+ export type DropdownPosition = 'left' | 'right' | 'bottom' | 'top';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,70 @@
1
+ <script lang="ts">
2
+ import Dropdown from '../Dropdown/Dropdown.svelte';
3
+ import type { DropdownAlign, DropdownPosition } from '../Dropdown/dropdown.types.js';
4
+ import IconButton from '../IconButton/IconButton.svelte';
5
+ import type { IconButtonColor, IconButtonSize } from '../IconButton/iconButton.types.js';
6
+ import EmojiSelector from './EmojiSelector.svelte';
7
+
8
+ interface Props {
9
+ emoji?: string;
10
+ iconButtonSize?: IconButtonSize;
11
+ iconButtonColor?: IconButtonColor;
12
+ dropdownAlign?: DropdownAlign;
13
+ dropdownPosition?: DropdownPosition;
14
+ onselect?: (emoji: string | undefined) => void;
15
+ removable?: boolean;
16
+ }
17
+
18
+ let show = $state(false);
19
+
20
+ let {
21
+ emoji = $bindable(undefined),
22
+ iconButtonSize = undefined,
23
+ iconButtonColor = undefined,
24
+ dropdownAlign = 'center',
25
+ dropdownPosition = 'bottom',
26
+ onselect = undefined,
27
+ removable = false
28
+ }: Props = $props();
29
+
30
+ function handleOnSelect(e: string | undefined) {
31
+ emoji = e;
32
+ show = false;
33
+ onselect?.(e);
34
+ }
35
+
36
+ function handleClose() {
37
+ show = false;
38
+ }
39
+ </script>
40
+
41
+ <Dropdown
42
+ bind:show
43
+ align={dropdownAlign}
44
+ position={dropdownPosition}
45
+ width={420}
46
+ contentPadding={0}
47
+ >
48
+ {#snippet trigger()}
49
+ <IconButton size={iconButtonSize} color={iconButtonColor}>
50
+ {#if emoji === undefined}
51
+ <span class="no-emoji">😀</span>
52
+ {:else}
53
+ <span class="emoji">{emoji}</span>
54
+ {/if}
55
+ </IconButton>
56
+ {/snippet}
57
+ {#snippet content()}
58
+ <EmojiSelector onselect={handleOnSelect} onclose={handleClose} {removable} />
59
+ {/snippet}
60
+ </Dropdown>
61
+
62
+ <style>
63
+ .no-emoji {
64
+ filter: grayscale(100%);
65
+ font-size: 18px;
66
+ }
67
+ .emoji {
68
+ font-size: 18px;
69
+ }
70
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { DropdownAlign, DropdownPosition } from '../Dropdown/dropdown.types.js';
2
+ import type { IconButtonColor, IconButtonSize } from '../IconButton/iconButton.types.js';
3
+ interface Props {
4
+ emoji?: string;
5
+ iconButtonSize?: IconButtonSize;
6
+ iconButtonColor?: IconButtonColor;
7
+ dropdownAlign?: DropdownAlign;
8
+ dropdownPosition?: DropdownPosition;
9
+ onselect?: (emoji: string | undefined) => void;
10
+ removable?: boolean;
11
+ }
12
+ declare const EmojiPicker: import("svelte").Component<Props, {}, "emoji">;
13
+ type EmojiPicker = ReturnType<typeof EmojiPicker>;
14
+ export default EmojiPicker;
@@ -0,0 +1,214 @@
1
+ <script lang="ts">
2
+ import { onMount, tick } from 'svelte';
3
+ import { loadEmojis, type CompactEmoji, type EmojiGroup } from './emojidata.js';
4
+ import Loader from '../Loader/Loader.svelte';
5
+ import TextInput from '../TextInput/TextInput.svelte';
6
+ import Button from '../Button/Button.svelte';
7
+
8
+ interface Props {
9
+ onselect: (emoji: string | undefined) => void;
10
+ onclose: () => void;
11
+ removable: boolean;
12
+ }
13
+
14
+ let { onselect, onclose, removable }: Props = $props();
15
+
16
+ let loading = $state(true);
17
+ let searchInput = $state({} as HTMLInputElement);
18
+ let groupsEl = $state({} as HTMLDivElement);
19
+
20
+ let data: EmojiGroup[] = $state([]);
21
+ let search = $state('');
22
+
23
+ let searchedEmojis: CompactEmoji[] | null = $derived.by(() => {
24
+ let searchVal = search.trim().toLowerCase();
25
+ if (!searchVal) return null;
26
+
27
+ return data
28
+ .flatMap((group) => group.emojis)
29
+ .filter((emoji) => {
30
+ const shortcodes = emoji.shortcodes || [];
31
+ const tags = emoji.tags || [];
32
+
33
+ const searchIn = [emoji.unicode, emoji.annotation, ...shortcodes, ...tags]
34
+ .filter((s) => Boolean(s))
35
+ .map((s) => s.toLowerCase());
36
+
37
+ return searchIn.some((code) => code.includes(searchVal));
38
+ });
39
+ });
40
+
41
+ async function load() {
42
+ data = await loadEmojis();
43
+ loading = false;
44
+
45
+ await tick();
46
+ searchInput?.focus();
47
+ }
48
+
49
+ function handleSectionButtonClick(group: number) {
50
+ if (!groupsEl) return;
51
+
52
+ const groupEl = groupsEl.querySelector(`.group[data-group="${group}"]`);
53
+ if (groupEl) {
54
+ groupEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
55
+ }
56
+ }
57
+
58
+ function handleKeydown(e: KeyboardEvent) {
59
+ if (e.key === 'Escape') {
60
+ onclose();
61
+ }
62
+ }
63
+
64
+ onMount(() => {
65
+ load();
66
+ });
67
+ </script>
68
+
69
+ <div class="wrap">
70
+ {#if loading}
71
+ <Loader padding={100} block />
72
+ {:else}
73
+ <div class="header">
74
+ <div class="left">
75
+ {@render SectionButton('☺️', 0)}
76
+ {@render SectionButton('👋', 1)}
77
+ {@render SectionButton('😺', 2)}
78
+ {@render SectionButton('🍕', 3)}
79
+ {@render SectionButton('🗼', 4)}
80
+ {@render SectionButton('⚽', 5)}
81
+ {@render SectionButton('📕', 6)}
82
+ {@render SectionButton('✔️', 7)}
83
+ {@render SectionButton('🇨🇵', 8)}
84
+ </div>
85
+ <div class="right">
86
+ {#if removable}
87
+ <Button size="small" color="input" onclick={() => onselect(undefined)}
88
+ >Remove</Button
89
+ >
90
+ {/if}
91
+ </div>
92
+ </div>
93
+ <div class="input">
94
+ <TextInput
95
+ block
96
+ bind:value={search}
97
+ bind:input={searchInput}
98
+ onkeydown={handleKeydown}
99
+ />
100
+ </div>
101
+ <div class="groups" bind:this={groupsEl}>
102
+ {#if searchedEmojis !== null}
103
+ {#if searchedEmojis.length === 0}
104
+ <div class="no-results">No results found</div>
105
+ {:else}
106
+ <div class="group">
107
+ <div class="name">Search Results</div>
108
+ {@render Emojis(searchedEmojis)}
109
+ </div>
110
+ {/if}
111
+ {:else}
112
+ {#each data as group, i}
113
+ <div class="group" data-group={i}>
114
+ <div class="name">{group.name}</div>
115
+ {@render Emojis(group.emojis)}
116
+ </div>
117
+ {/each}
118
+ {/if}
119
+ </div>
120
+ {/if}
121
+ </div>
122
+
123
+ {#snippet Emojis(emojis: CompactEmoji[])}
124
+ <div class="emojis">
125
+ {#each emojis as emoji}
126
+ <button class="emoji" onclick={() => onselect(emoji.unicode)}>
127
+ {emoji.unicode}
128
+ </button>
129
+ {/each}
130
+ </div>
131
+ {/snippet}
132
+
133
+ {#snippet SectionButton(icon: string, group: number)}
134
+ <button
135
+ class="section-button"
136
+ data-group={group}
137
+ onclick={() => handleSectionButtonClick(group)}
138
+ >
139
+ {icon}
140
+ </button>
141
+ {/snippet}
142
+
143
+ <style>
144
+ .group {
145
+ display: flex;
146
+ flex-direction: column;
147
+ margin-bottom: 20px;
148
+ }
149
+ .group .name {
150
+ font-weight: 600;
151
+ margin-bottom: 10px;
152
+ }
153
+ .input {
154
+ padding: 0 10px;
155
+ margin: 10px 0;
156
+ }
157
+ .groups {
158
+ padding: 15px;
159
+ padding-top: 5px;
160
+ max-height: 370px;
161
+ overflow-y: auto;
162
+ }
163
+ .wrap {
164
+ }
165
+ .emojis {
166
+ display: flex;
167
+ flex-wrap: wrap;
168
+ }
169
+ .emoji {
170
+ font-size: 22px;
171
+ cursor: pointer;
172
+ transition: transform 0.2s;
173
+ width: 34px;
174
+ height: 34px;
175
+ text-align: center;
176
+ border-radius: 5px;
177
+ }
178
+ .emoji:hover {
179
+ background-color: var(--hover-dark);
180
+ }
181
+
182
+ .no-results {
183
+ color: var(--text-light);
184
+ font-size: 14px;
185
+ text-align: center;
186
+ padding: 30px;
187
+ }
188
+
189
+ .header {
190
+ padding: 0 15px;
191
+ border-bottom: 1px solid var(--border);
192
+ display: flex;
193
+ align-items: center;
194
+ }
195
+ .header .left {
196
+ flex: 1;
197
+ }
198
+
199
+ .section-button {
200
+ font-size: 20px;
201
+ padding: 10px 4px;
202
+ cursor: pointer;
203
+ color: var(--text-light);
204
+ filter: grayscale(100%);
205
+ border-bottom: 2px solid transparent;
206
+ transition:
207
+ filter 0.2s,
208
+ border-bottom 0.2s;
209
+ }
210
+ .section-button:hover {
211
+ filter: grayscale(50%);
212
+ border-bottom: 2px solid var(--text-light);
213
+ }
214
+ </style>
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ onselect: (emoji: string | undefined) => void;
3
+ onclose: () => void;
4
+ removable: boolean;
5
+ }
6
+ declare const EmojiSelector: import("svelte").Component<Props, {}, "">;
7
+ type EmojiSelector = ReturnType<typeof EmojiSelector>;
8
+ export default EmojiSelector;
@@ -0,0 +1,22 @@
1
+ export interface CompactEmoji {
2
+ annotation: string;
3
+ group: number;
4
+ hexcode: string;
5
+ order: number;
6
+ shortcodes: string[];
7
+ tags: string[];
8
+ unicode: string;
9
+ skins: {
10
+ annotation: string;
11
+ group: number;
12
+ hexcode: string;
13
+ order: number;
14
+ shortcodes: string[];
15
+ unicode: string;
16
+ }[];
17
+ }
18
+ export interface EmojiGroup {
19
+ name: string;
20
+ emojis: CompactEmoji[];
21
+ }
22
+ export declare function loadEmojis(): Promise<EmojiGroup[]>;
@@ -0,0 +1,31 @@
1
+ // https://emojibase.dev/docs/datasets/#compact-format
2
+ let emojis = [];
3
+ const groups = [
4
+ 'Smileys & Emotion',
5
+ 'People & Body',
6
+ 'Components', // removed
7
+ 'Animals & Nature',
8
+ 'Food & Drink',
9
+ 'Travel & Places',
10
+ 'Activities',
11
+ 'Objects',
12
+ 'Symbols',
13
+ 'Flags'
14
+ ];
15
+ // check if the emoji is a ZWJ (Zero Width Joiner) sequence
16
+ // when I tested on MacOS, ZWJs were shown as separate emojis
17
+ function isZWJEmoji(emoji) {
18
+ return emoji.includes('\u200D');
19
+ }
20
+ export async function loadEmojis() {
21
+ const data = await import('emojibase-data/en/compact.json');
22
+ const emojiData = data.default;
23
+ emojis = emojiData.filter(emoji => emoji.group !== undefined);
24
+ const groupedEmojis = groups.map(group => ({
25
+ name: group,
26
+ emojis: emojis.filter(emoji => emoji.group === groups.indexOf(group) && !isZWJEmoji(emoji.unicode))
27
+ }));
28
+ // remove components
29
+ groupedEmojis.splice(2, 1);
30
+ return groupedEmojis;
31
+ }
@@ -3,23 +3,27 @@
3
3
  import ActionListItem from '../ActionList/ActionListItem.svelte';
4
4
  import Dropdown from '../Dropdown/Dropdown.svelte';
5
5
  import IconBoxArrowUpRight from '@hyvor/icons/IconBoxArrowUpRight';
6
- import { barUser } from './bar.js';
7
6
  import BarUserPreview from './BarUserPreview.svelte';
7
+ import BarUserPicture from './BarUserPicture.svelte';
8
8
 
9
9
  interface Props {
10
10
  instance: string;
11
+ logoutUrl?: string;
12
+ cloud: boolean;
11
13
  }
12
14
 
13
- let { instance }: Props = $props();
15
+ let {
16
+ instance,
17
+ logoutUrl = `${instance}/account/logout`,
18
+ cloud,
19
+ }: Props = $props();
14
20
  </script>
15
21
 
16
22
  <div class="wrap">
17
23
  <Dropdown align="end" width={325}>
18
24
  {#snippet trigger()}
19
25
  <button class="user-wrap">
20
- {#if $barUser}
21
- <img class="user-picture" src={$barUser?.picture_url} alt={$barUser?.name} />
22
- {/if}
26
+ <BarUserPicture />
23
27
  </button>
24
28
  {/snippet}
25
29
 
@@ -27,15 +31,19 @@
27
31
  <ActionList>
28
32
  <BarUserPreview />
29
33
 
30
- <a href="{instance}/account" target="_blank">
31
- <ActionListItem>
32
- Manage Account
33
- {#snippet end()}
34
- <IconBoxArrowUpRight size={12} />
35
- {/snippet}
36
- </ActionListItem>
37
- </a>
38
- <a href="{instance}/account/logout">
34
+ {#if cloud}
35
+ <a href="{instance}/account" target="_blank">
36
+ <ActionListItem>
37
+ Manage Account
38
+ {#snippet end()}
39
+ <IconBoxArrowUpRight size={12} />
40
+ {/snippet}
41
+ </ActionListItem>
42
+ </a>
43
+ {/if}
44
+
45
+
46
+ <a href="{logoutUrl}">
39
47
  <ActionListItem>Logout</ActionListItem>
40
48
  </a>
41
49
  </ActionList>
@@ -65,8 +73,4 @@
65
73
  .user-wrap:hover {
66
74
  box-shadow: 0 0 0 4px var(--input);
67
75
  }
68
- img {
69
- width: 100%;
70
- height: 100%;
71
- }
72
76
  </style>
@@ -1,5 +1,7 @@
1
1
  interface Props {
2
2
  instance: string;
3
+ logoutUrl?: string;
4
+ cloud: boolean;
3
5
  }
4
6
  declare const BarUser: import("svelte").Component<Props, {}, "">;
5
7
  type BarUser = ReturnType<typeof BarUser>;
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import { barUser } from "./bar.js";
3
+ </script>
4
+
5
+
6
+ {#if $barUser}
7
+ {#if $barUser.picture_url}
8
+ <img src={$barUser.picture_url} alt={$barUser.name} />
9
+ {:else}
10
+ <span class="user-placeholder">
11
+ {$barUser.name ? $barUser.name[0].toUpperCase() : '?'}
12
+ </span>
13
+ {/if}
14
+ {/if}
15
+
16
+ <style>
17
+ img {
18
+ width: 30px;
19
+ height: 30px;
20
+ border-radius: 50%;
21
+ }
22
+ .user-placeholder {
23
+ display: inline-flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ width: 30px;
27
+ height: 30px;
28
+ border-radius: 50%;
29
+ color: var(--text);
30
+ font-size: 14px;
31
+ background-color: var(--input);
32
+ }
33
+ </style>
@@ -0,0 +1,18 @@
1
+ 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> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const BarUserPicture: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type BarUserPicture = InstanceType<typeof BarUserPicture>;
18
+ export default BarUserPicture;
@@ -1,14 +1,21 @@
1
1
  <script lang="ts">
2
2
  import { barUser } from './bar.js';
3
+ import BarUserPicture from './BarUserPicture.svelte';
3
4
  </script>
4
5
 
5
6
  {#if $barUser}
6
7
  <div class="preview">
7
8
  <div class="left">
8
- <img src={$barUser.picture_url} alt={$barUser.name} />
9
+ <BarUserPicture />
9
10
  </div>
10
11
  <div class="right">
11
- <div class="username">@{$barUser.username}</div>
12
+ <div class="username">
13
+ {#if $barUser.username}
14
+ @{$barUser.username}
15
+ {:else if $barUser.name}
16
+ {$barUser.name}
17
+ {/if}
18
+ </div>
12
19
  <div class="email">{$barUser.email}</div>
13
20
  </div>
14
21
  </div>
@@ -3,7 +3,7 @@
3
3
  import { onMount } from 'svelte';
4
4
  import BarProducts, { PRODUCTS } from './BarProducts.svelte';
5
5
  import BarSupport from './BarSupport.svelte';
6
- import { loadBarUser, setInstanceAndProduct, type BarConfig, type BarProduct } from './bar.js';
6
+ import { barUser, initBar, setInstanceAndProduct, type BarConfig, type BarUser as BarUserType } from './bar.js';
7
7
  import BarUpdates from './BarUpdates.svelte';
8
8
  import IconCaretDownFill from '@hyvor/icons/IconCaretDownFill';
9
9
  import BarNotice from './Notice/BarNotice.svelte';
@@ -13,9 +13,31 @@
13
13
  instance?: string;
14
14
  product: string;
15
15
  config?: Partial<BarConfig>;
16
+
17
+ // set a custom logo URL
18
+ // defaults to instance + '/api/public/logo/' + product + '.svg'
19
+ // recommended to use this for self-hostable products
20
+ logo?: string;
21
+
22
+ /**
23
+ * Whether it is a HYVOR cloud-hosted product.
24
+ * If false, we will hide some features
25
+ */
26
+ cloud?: boolean;
27
+ authOverride?: {
28
+ user: BarUserType | null;
29
+ logoutUrl: string;
30
+ }
16
31
  }
17
32
 
18
- let { instance = 'https://hyvor.com', product, config = {} }: Props = $props();
33
+ let {
34
+ instance = 'https://hyvor.com',
35
+ product,
36
+ logo = `${instance}/api/public/logo/${product}.svg`,
37
+ config = {},
38
+ cloud = true,
39
+ authOverride = undefined
40
+ }: Props = $props();
19
41
 
20
42
  let mobileShow = $state(false);
21
43
 
@@ -41,7 +63,14 @@
41
63
 
42
64
  onMount(() => {
43
65
  setInstanceAndProduct(instance, product);
44
- loadBarUser();
66
+
67
+ if (cloud) {
68
+ initBar();
69
+ } else {
70
+ if (authOverride) {
71
+ barUser.set(authOverride.user);
72
+ }
73
+ }
45
74
  });
46
75
 
47
76
  function getName() {
@@ -59,7 +88,7 @@
59
88
  <div class="left">
60
89
  <a class="logo" href="/">
61
90
  <img
62
- src={instance + '/api/public/logo/' + product + '.svg'}
91
+ src={logo}
63
92
  alt={product}
64
93
  width="20"
65
94
  height="20"
@@ -74,16 +103,20 @@
74
103
  <BarNotice {instance} />
75
104
 
76
105
  <div class="hidden-on-mobile">
77
- <BarSupport config={configComplete} {product} mobile={mobileShow} />
78
- <BarProducts {instance} mobile={mobileShow} />
79
- <BarUpdates {instance} {product} />
106
+ {#if cloud}
107
+ <BarSupport config={configComplete} {product} mobile={mobileShow} />
108
+ <BarProducts {instance} mobile={mobileShow} />
109
+ <BarUpdates {instance} {product} />
110
+ {/if}
80
111
  </div>
81
112
 
82
- <div class="mobile">
83
- <IconCaretDownFill />
84
- </div>
113
+ {#if cloud}
114
+ <div class="mobile">
115
+ <IconCaretDownFill />
116
+ </div>
117
+ {/if}
85
118
 
86
- <BarUser {instance} />
119
+ <BarUser {instance} logoutUrl={authOverride?.logoutUrl} {cloud} />
87
120
  </div>
88
121
  </div>
89
122
 
@@ -1,8 +1,18 @@
1
- import { type BarConfig } from './bar.js';
1
+ import { type BarConfig, type BarUser as BarUserType } from './bar.js';
2
2
  interface Props {
3
3
  instance?: string;
4
4
  product: string;
5
5
  config?: Partial<BarConfig>;
6
+ logo?: string;
7
+ /**
8
+ * Whether it is a HYVOR cloud-hosted product.
9
+ * If false, we will hide some features
10
+ */
11
+ cloud?: boolean;
12
+ authOverride?: {
13
+ user: BarUserType | null;
14
+ logoutUrl: string;
15
+ };
6
16
  }
7
17
  declare const HyvorBar: import("svelte").Component<Props, {}, "">;
8
18
  type HyvorBar = ReturnType<typeof HyvorBar>;
@@ -5,11 +5,11 @@ export interface BarConfig {
5
5
  chat: boolean;
6
6
  g2: string | null;
7
7
  }
8
- interface BarUser {
8
+ export interface BarUser {
9
9
  name: string | null;
10
- username: string;
10
+ username?: string | null;
11
11
  email: string;
12
- picture_url: string;
12
+ picture_url: string | null;
13
13
  }
14
14
  export interface BarUpdate {
15
15
  id: number;
@@ -34,7 +34,7 @@ export declare const barUnreadUpdates: import("svelte/store").Writable<number>;
34
34
  export declare const barLicense: import("svelte/store").Writable<BarResolvedLicense | null>;
35
35
  export declare const barHasFailedInvoices: import("svelte/store").Writable<boolean>;
36
36
  export declare function setInstanceAndProduct(instance_: string, product_: string): void;
37
- export declare function loadBarUser(): void;
37
+ export declare function initBar(): void;
38
38
  export declare class UnreadUpdatesTimeLocalStorage {
39
39
  static KEY: string;
40
40
  static get(): number | null;
@@ -49,4 +49,3 @@ export declare const bar: {
49
49
  */
50
50
  reload: () => void;
51
51
  };
52
- export {};
@@ -9,7 +9,7 @@ export function setInstanceAndProduct(instance_, product_) {
9
9
  instance = instance_;
10
10
  product = product_;
11
11
  }
12
- export function loadBarUser() {
12
+ export function initBar() {
13
13
  const query = new URLSearchParams();
14
14
  query.set('product', product);
15
15
  const lastUnreadTime = UnreadUpdatesTimeLocalStorage.get();
@@ -90,6 +90,6 @@ export const bar = {
90
90
  * This is useful to create after, for example, a user creates a new blog
91
91
  */
92
92
  reload: () => {
93
- loadBarUser();
93
+ initBar();
94
94
  }
95
95
  };
@@ -1,16 +1,28 @@
1
1
  <script lang="ts">
2
2
  import { createBubbler } from 'svelte/legacy';
3
+ import type { IconButtonSize, IconButtonColor } from './iconButton.types.js';
4
+ import { legacyHandlers } from '../../legacy.js';
3
5
 
4
6
  const bubble = createBubbler();
5
7
 
6
8
  interface Props {
7
- size?: 'small' | 'medium' | 'large' | number;
8
- // export let color : 'accent' | 'soft' | 'invisible' | 'danger' = 'accent';
9
- color?: 'accent' | 'gray' | 'input' | 'green' | 'red' | 'blue' | 'orange';
9
+ size?: IconButtonSize;
10
+ color?: IconButtonColor;
10
11
  variant?: 'fill' | 'fill-light' | 'outline' | 'outline-fill' | 'invisible';
11
12
  as?: 'button' | 'a';
12
13
  children?: import('svelte').Snippet;
13
14
  [key: string]: any;
15
+
16
+ onkeyup?: (event: KeyboardEvent) => void;
17
+ onkeydown?: (event: KeyboardEvent) => void;
18
+ onkeypress?: (event: KeyboardEvent) => void;
19
+ onfocus?: (event: FocusEvent) => void;
20
+ onblur?: (event: FocusEvent) => void;
21
+ onclick?: (event: MouseEvent) => void;
22
+ onmouseover?: (event: MouseEvent) => void;
23
+ onmouseenter?: (event: MouseEvent) => void;
24
+ onmouseleave?: (event: MouseEvent) => void;
25
+ onchange?: (event: Event) => void;
14
26
  }
15
27
 
16
28
  let {
@@ -19,6 +31,18 @@
19
31
  variant = 'fill',
20
32
  as = 'button',
21
33
  children,
34
+
35
+ onkeyup,
36
+ onkeydown,
37
+ onkeypress,
38
+ onfocus,
39
+ onblur,
40
+ onclick,
41
+ onmouseover,
42
+ onmouseenter,
43
+ onmouseleave,
44
+ onchange,
45
+
22
46
  ...rest
23
47
  }: Props = $props();
24
48
 
@@ -36,16 +60,16 @@
36
60
  class="button {color} {variant}"
37
61
  style:width={size}
38
62
  style:height={size}
39
- onkeyup={bubble('keyup')}
40
- onkeydown={bubble('keydown')}
41
- onkeypress={bubble('keypress')}
42
- onfocus={bubble('focus')}
43
- onblur={bubble('blur')}
44
- onclick={bubble('click')}
45
- onmouseover={bubble('mouseover')}
46
- onmouseenter={bubble('mouseenter')}
47
- onmouseleave={bubble('mouseleave')}
48
- onchange={bubble('change')}
63
+ onkeyup={legacyHandlers(onkeyup, bubble('keyup'))}
64
+ onkeydown={legacyHandlers(onkeydown, bubble('keydown'))}
65
+ onkeypress={legacyHandlers(onkeypress, bubble('keypress'))}
66
+ onfocus={legacyHandlers(onfocus, bubble('focus'))}
67
+ onblur={legacyHandlers(onblur, bubble('blur'))}
68
+ onclick={legacyHandlers(onclick, bubble('click'))}
69
+ onmouseover={legacyHandlers(onmouseover, bubble('mouseover'))}
70
+ onmouseenter={legacyHandlers(onmouseenter, bubble('mouseenter'))}
71
+ onmouseleave={legacyHandlers(onmouseleave, bubble('mouseleave'))}
72
+ onchange={legacyHandlers(onchange, bubble('change'))}
49
73
  role="button"
50
74
  tabindex="0"
51
75
  {...rest}
@@ -1,10 +1,21 @@
1
+ import type { IconButtonSize, IconButtonColor } from './iconButton.types.js';
1
2
  interface Props {
2
- size?: 'small' | 'medium' | 'large' | number;
3
- color?: 'accent' | 'gray' | 'input' | 'green' | 'red' | 'blue' | 'orange';
3
+ size?: IconButtonSize;
4
+ color?: IconButtonColor;
4
5
  variant?: 'fill' | 'fill-light' | 'outline' | 'outline-fill' | 'invisible';
5
6
  as?: 'button' | 'a';
6
7
  children?: import('svelte').Snippet;
7
8
  [key: string]: any;
9
+ onkeyup?: (event: KeyboardEvent) => void;
10
+ onkeydown?: (event: KeyboardEvent) => void;
11
+ onkeypress?: (event: KeyboardEvent) => void;
12
+ onfocus?: (event: FocusEvent) => void;
13
+ onblur?: (event: FocusEvent) => void;
14
+ onclick?: (event: MouseEvent) => void;
15
+ onmouseover?: (event: MouseEvent) => void;
16
+ onmouseenter?: (event: MouseEvent) => void;
17
+ onmouseleave?: (event: MouseEvent) => void;
18
+ onchange?: (event: Event) => void;
8
19
  }
9
20
  declare const IconButton: import("svelte").Component<Props, {}, "size">;
10
21
  type IconButton = ReturnType<typeof IconButton>;
@@ -0,0 +1,2 @@
1
+ export type IconButtonSize = 'small' | 'medium' | 'large' | number;
2
+ export type IconButtonColor = 'accent' | 'gray' | 'input' | 'green' | 'red' | 'blue' | 'orange';
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { legacyHandlers } from '../../legacy.js';
2
3
  import { createBubbler } from 'svelte/legacy';
3
4
 
4
5
  const bubble = createBubbler();
@@ -14,6 +15,18 @@
14
15
  select?: boolean;
15
16
  selectInput?: HTMLSelectElement;
16
17
  [key: string]: any;
18
+
19
+ onkeyup?: (event: KeyboardEvent) => void;
20
+ onkeydown?: (event: KeyboardEvent) => void;
21
+ onkeypress?: (event: KeyboardEvent) => void;
22
+ onfocus?: (event: FocusEvent) => void;
23
+ onblur?: (event: FocusEvent) => void;
24
+ onclick?: (event: MouseEvent) => void;
25
+ onmouseover?: (event: MouseEvent) => void;
26
+ onmouseenter?: (event: MouseEvent) => void;
27
+ onmouseleave?: (event: MouseEvent) => void;
28
+ onchange?: (event: Event) => void;
29
+ oninput?: (event: InputEvent) => void;
17
30
  }
18
31
 
19
32
  let {
@@ -26,6 +39,19 @@
26
39
  end,
27
40
  select = false,
28
41
  selectInput = $bindable({} as HTMLSelectElement),
42
+
43
+ onkeyup,
44
+ onkeydown,
45
+ onkeypress,
46
+ onfocus,
47
+ onblur,
48
+ onclick,
49
+ onmouseover,
50
+ onmouseenter,
51
+ onmouseleave,
52
+ onchange,
53
+ oninput,
54
+
29
55
  ...rest
30
56
  }: Props = $props();
31
57
  </script>
@@ -42,17 +68,17 @@
42
68
  {...rest}
43
69
  bind:value
44
70
  bind:this={selectInput}
45
- onkeyup={bubble('keyup')}
46
- onkeydown={bubble('keydown')}
47
- onkeypress={bubble('keypress')}
48
- onfocus={bubble('focus')}
49
- onblur={bubble('blur')}
50
- onclick={bubble('click')}
51
- onmouseover={bubble('mouseover')}
52
- onmouseenter={bubble('mouseenter')}
53
- onmouseleave={bubble('mouseleave')}
54
- onchange={bubble('change')}
55
- oninput={bubble('input')}
71
+ onkeyup={legacyHandlers(onkeyup, bubble('keyup'))}
72
+ onkeydown={legacyHandlers(onkeydown, bubble('keydown'))}
73
+ onkeypress={legacyHandlers(onkeypress, bubble('keypress'))}
74
+ onfocus={legacyHandlers(onfocus, bubble('focus'))}
75
+ onblur={legacyHandlers(onblur, bubble('blur'))}
76
+ onclick={legacyHandlers(onclick, bubble('click'))}
77
+ onmouseover={legacyHandlers(onmouseover, bubble('mouseover'))}
78
+ onmouseenter={legacyHandlers(onmouseenter, bubble('mouseenter'))}
79
+ onmouseleave={legacyHandlers(onmouseleave, bubble('mouseleave'))}
80
+ onchange={legacyHandlers(onchange, bubble('change'))}
81
+ oninput={legacyHandlers(oninput, bubble('input'))}
56
82
  >
57
83
  {@render rest?.children()}
58
84
  </select>
@@ -61,17 +87,17 @@
61
87
  {...rest}
62
88
  bind:value
63
89
  bind:this={input}
64
- onkeyup={bubble('keyup')}
65
- onkeydown={bubble('keydown')}
66
- onkeypress={bubble('keypress')}
67
- onfocus={bubble('focus')}
68
- onblur={bubble('blur')}
69
- onclick={bubble('click')}
70
- onmouseover={bubble('mouseover')}
71
- onmouseenter={bubble('mouseenter')}
72
- onmouseleave={bubble('mouseleave')}
73
- onchange={bubble('change')}
74
- oninput={bubble('input')}
90
+ onkeyup={legacyHandlers(onkeyup, bubble('keyup'))}
91
+ onkeydown={legacyHandlers(onkeydown, bubble('keydown'))}
92
+ onkeypress={legacyHandlers(onkeypress, bubble('keypress'))}
93
+ onfocus={legacyHandlers(onfocus, bubble('focus'))}
94
+ onblur={legacyHandlers(onblur, bubble('blur'))}
95
+ onclick={legacyHandlers(onclick, bubble('click'))}
96
+ onmouseover={legacyHandlers(onmouseover, bubble('mouseover'))}
97
+ onmouseenter={legacyHandlers(onmouseenter, bubble('mouseenter'))}
98
+ onmouseleave={legacyHandlers(onmouseleave, bubble('mouseleave'))}
99
+ onchange={legacyHandlers(onchange, bubble('change'))}
100
+ oninput={legacyHandlers(oninput, bubble('input'))}
75
101
  />
76
102
  {/if}
77
103
 
@@ -9,6 +9,17 @@ interface Props {
9
9
  select?: boolean;
10
10
  selectInput?: HTMLSelectElement;
11
11
  [key: string]: any;
12
+ onkeyup?: (event: KeyboardEvent) => void;
13
+ onkeydown?: (event: KeyboardEvent) => void;
14
+ onkeypress?: (event: KeyboardEvent) => void;
15
+ onfocus?: (event: FocusEvent) => void;
16
+ onblur?: (event: FocusEvent) => void;
17
+ onclick?: (event: MouseEvent) => void;
18
+ onmouseover?: (event: MouseEvent) => void;
19
+ onmouseenter?: (event: MouseEvent) => void;
20
+ onmouseleave?: (event: MouseEvent) => void;
21
+ onchange?: (event: Event) => void;
22
+ oninput?: (event: InputEvent) => void;
12
23
  }
13
24
  declare const TextInput: import("svelte").Component<Props, {}, "value" | "input" | "selectInput">;
14
25
  type TextInput = ReturnType<typeof TextInput>;
@@ -41,10 +41,13 @@
41
41
  --gray-dark: #555;
42
42
 
43
43
  --hover: #fafafa;
44
+ --hover-dark: #f0f0f0;
45
+
44
46
  --link: #1d85d2;
45
47
 
46
48
  --input: #f3f3f3;
47
- --input-hover: #ccc; /* checkbox/radio */
49
+ --input-hover: #ccc;
50
+ /* checkbox/radio */
48
51
 
49
52
  --box-shadow: 0 0 30px #0000000d;
50
53
  --box-shadow-light: 0 0 10px 6px rgba(0, 0, 0, 0.06);
@@ -76,4 +79,4 @@ $breakpoint-xs: 320px;
76
79
  $breakpoint-sm: 576px;
77
80
  $breakpoint-md: 768px;
78
81
  $breakpoint-lg: 992px;
79
- $breakpoint-xl: 1200px;
82
+ $breakpoint-xl: 1200px;
package/package.json CHANGED
@@ -50,6 +50,7 @@
50
50
  "@fontsource/readex-pro": "^5.0.8",
51
51
  "@hyvor/icons": "^1.1.1",
52
52
  "deepmerge-ts": "^5.1.0",
53
+ "emojibase-data": "^16.0.3",
53
54
  "highlight.js": "^11.9.0",
54
55
  "intl-messageformat": "^10.5.11",
55
56
  "svelte-awesome-color-picker": "^3.0.4",
@@ -59,5 +60,5 @@
59
60
  "publishConfig": {
60
61
  "access": "public"
61
62
  },
62
- "version": "1.1.13"
63
+ "version": "1.1.15"
63
64
  }