@functionalcms/svelte-components 5.3.1 → 5.3.3

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.
@@ -29,7 +29,7 @@
29
29
 
30
30
  interface Props {
31
31
  action: string;
32
- css?: string;
32
+ class?: string;
33
33
  successMessage?: string;
34
34
  fields?: Array<Field>;
35
35
  onMessageSubmitted?: (result: SubmitResult) => void;
@@ -39,7 +39,7 @@
39
39
  let {
40
40
  action,
41
41
  fields = [],
42
- css = '',
42
+ class: css = '',
43
43
  successMessage,
44
44
  onMessageSubmitted,
45
45
  buttons = [
@@ -58,7 +58,6 @@
58
58
 
59
59
  const handleSubmit = async (event: Event) => {
60
60
  event.preventDefault();
61
- console.log('Submitting form to:', action);
62
61
  loading = true;
63
62
  try {
64
63
  if (!isSendingMessage) {
@@ -180,6 +179,7 @@
180
179
  {/snippet}
181
180
 
182
181
  {#snippet renderPhoneInput(field: PhoneInputField)}
182
+ <Label class={field.labelClass}>{field.label}</Label>
183
183
  <PhoneInput
184
184
  id={field.id}
185
185
  name={field.id}
@@ -187,7 +187,6 @@
187
187
  readonly={field.readonly}
188
188
  disabled={field.disabled}
189
189
  class={field.class}
190
- size={field.size}
191
190
  placeholder={field.placeholder}
192
191
  />
193
192
  {/snippet}
@@ -1,7 +1,7 @@
1
1
  import { type Field, SubmitResult, type ButtonConfig } from './form.js';
2
2
  interface Props {
3
3
  action: string;
4
- css?: string;
4
+ class?: string;
5
5
  successMessage?: string;
6
6
  fields?: Array<Field>;
7
7
  onMessageSubmitted?: (result: SubmitResult) => void;
@@ -54,9 +54,10 @@
54
54
  nonActiveClass: 'text-gray-700 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white',
55
55
  footer:
56
56
  'absolute start-0 bottom-0 z-20 w-full border-t border-gray-200 bg-white p-4 shadow-sm md:flex md:items-center md:justify-between md:p-6 dark:border-gray-600 dark:bg-gray-800',
57
- footerLinks: ''
57
+ footerLinks: '',
58
+ main: 'min-h-screen'
58
59
  };
59
- const mergedClasses = $derived(() => ({ ...defaultClasses, ...classes }));
60
+ const mergedClasses = $derived({ ...defaultClasses, ...classes });
60
61
 
61
62
  let activeUrl = $state(page.url.pathname);
62
63
 
@@ -74,40 +75,40 @@
74
75
  </svelte:head>
75
76
 
76
77
  <div class="w-full relative" id="defaultLayout">
77
- <Navbar class={mergedClasses().navigation}>
78
+ <Navbar class={mergedClasses.navigation}>
78
79
  <NavBrand href="/" class="logo">
79
- <img src={logoUrl} class={mergedClasses().logo} alt={companyName} />
80
+ <img src={logoUrl} class={mergedClasses.logo} alt={companyName} />
80
81
  {#if renderCompanyName}
81
- <span class={mergedClasses().companyName}>{companyName}</span>
82
+ <span class={mergedClasses.companyName}>{companyName}</span>
82
83
  {/if}
83
84
  </NavBrand>
84
85
  <NavHamburger />
85
86
  <NavUl
86
87
  {activeUrl}
87
- classes={{ active: mergedClasses().activeClass, nonActive: mergedClasses().nonActiveClass }}
88
+ classes={{ active: mergedClasses.activeClass, nonActive: mergedClasses.nonActiveClass }}
88
89
  >
89
90
  {#each menuItems as item}
90
91
  {#if item?.items}
91
- <NavLi class={mergedClasses().menuItem}>
92
+ <NavLi class={mergedClasses.menuItem}>
92
93
  <a href={item.href}>{item.label}</a>
93
94
  <ChevronDownOutline class="text-primary-800 ms-2 inline h-6 w-6 dark:text-white" />
94
95
  </NavLi>
95
96
  <Dropdown simple class="w-44">
96
97
  {#each item.items as subItem}
97
- <DropdownItem class={mergedClasses().menuItem} href={subItem.href}
98
+ <DropdownItem class={mergedClasses.menuItem} href={subItem.href}
98
99
  >{subItem.label}</DropdownItem
99
100
  >
100
101
  {/each}
101
102
  </Dropdown>
102
103
  {:else}
103
- <NavLi class={mergedClasses().menuItem}>
104
+ <NavLi class={mergedClasses.menuItem}>
104
105
  <a href={item.href}>{item.label}</a>
105
106
  </NavLi>
106
107
  {/if}
107
108
  {/each}
108
109
  </NavUl>
109
110
  </Navbar>
110
- <main class="min-h-screen">
111
+ <main class={mergedClasses.main}>
111
112
  {@render children?.()}
112
113
  </main>
113
114
  {@render footerRender?.()}
@@ -1,8 +1,6 @@
1
- import { deleteCookie, loadCookie, saveCookie } from "../cookies";
2
1
  import { userAuthenticationProvider } from "./userAuthenticationProvider";
3
2
  import { encrypt } from "../crypto";
4
3
  const accessTokenCookie = "functionalcms_access_token";
5
- const idTokenCookie = "functionalcms_id_token";
6
4
  const refreshTokenCookie = "functionalcms_refresh_token";
7
5
  export const machineAuthenticationHandle = (secret, serverUrl, clientId, clientSecret) => {
8
6
  const provider = userAuthenticationProvider(serverUrl, clientId);
@@ -39,7 +37,6 @@ const login = async (serverUrl, clientId, clientSecret) => {
39
37
  const tokens = await tokenRes.json();
40
38
  return {
41
39
  access_token: tokens.access_token,
42
- id_token: tokens.id_token,
43
40
  refresh_token: tokens.refresh_token,
44
41
  };
45
42
  };
@@ -1,5 +1,6 @@
1
1
  import { deleteCookie, loadCookie, saveCookie } from "../cookies";
2
2
  import { userAuthenticationProvider } from "./userAuthenticationProvider";
3
+ import { parse } from "svelte/compiler";
3
4
  const pkceCookieName = "functionalcms_pkce";
4
5
  const stateCookieName = "functionalcms_state";
5
6
  const accessTokenCookie = "functionalcms_access_token";
@@ -34,6 +35,16 @@ function handleLogout(event) {
34
35
  event.locals.user = null;
35
36
  event.locals.session = {};
36
37
  }
38
+ function parseJwt(token) {
39
+ const base64 = token.split('.')[1]
40
+ .replace(/-/g, '+')
41
+ .replace(/_/g, '/');
42
+ const json = decodeURIComponent(atob(base64)
43
+ .split('')
44
+ .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
45
+ .join(''));
46
+ return JSON.parse(json);
47
+ }
37
48
  async function handleLogin(event, provider, secret) {
38
49
  const callbackUrl = new URL(`${event.url.origin}/auth/callback`);
39
50
  const response = await provider.handleLogin(callbackUrl);
@@ -49,7 +60,8 @@ async function handleCallback(event, secret, provider) {
49
60
  const response = await provider.handleCallback(expectedState, codeVerifier, event.url);
50
61
  const headers = new Headers();
51
62
  headers.set('Location', event.url.origin);
52
- await saveCookie(headers, secret, accessTokenCookie, response.access_token);
63
+ const accessToken = parseJwt(response.access_token);
64
+ await saveCookie(headers, secret, accessTokenCookie, response.access_token, accessToken.exp - Math.floor(Date.now() / 1000));
53
65
  if (response.id_token) {
54
66
  await saveCookie(headers, secret, idTokenCookie, response.id_token);
55
67
  }
@@ -1,3 +1,3 @@
1
- export declare const saveCookie: (headers: Headers, secret: string, name: string, value: string) => Promise<void>;
1
+ export declare const saveCookie: (headers: Headers, secret: string, name: string, value: string, maxage?: number) => Promise<void>;
2
2
  export declare const loadCookie: (cookies: any, secret: string, name: string) => Promise<string>;
3
3
  export declare const deleteCookie: (cookies: any, headers: Headers, cookieName: string) => void;
@@ -1,7 +1,7 @@
1
1
  import { decrypt, encrypt } from "./crypto";
2
- export const saveCookie = async (headers, secret, name, value) => {
2
+ export const saveCookie = async (headers, secret, name, value, maxage = 3600) => {
3
3
  const encryptedValue = await encrypt(secret, value);
4
- headers.append('Set-Cookie', `${name}=${encryptedValue}; HttpOnly; SameSite=None; Secure; Max-Age=3600; Path=/`);
4
+ headers.append('Set-Cookie', `${name}=${encryptedValue}; HttpOnly; SameSite=None; Secure; Max-Age=${maxage}; Path=/`);
5
5
  };
6
6
  export const loadCookie = async (cookies, secret, name) => {
7
7
  const encryptedValue = cookies.get(name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@functionalcms/svelte-components",
3
- "version": "5.3.1",
3
+ "version": "5.3.3",
4
4
  "license": "MIT",
5
5
  "watch": {
6
6
  "build": {
@@ -1,115 +0,0 @@
1
- <script lang="ts">
2
- import { page } from '$app/state';
3
- import type { Snippet } from 'svelte';
4
- import {
5
- Navbar,
6
- NavBrand,
7
- NavHamburger,
8
- NavUl,
9
- NavLi,
10
- Dropdown,
11
- DropdownItem,
12
- } from 'flowbite-svelte';
13
- import { type MenuOption } from './menuItems.js';
14
- import { ChevronDownOutline } from 'flowbite-svelte-icons';
15
- import { afterNavigate } from "$app/navigation";
16
-
17
- interface Styles {
18
- logo?: string;
19
- companyName?: string;
20
- navigation?: string;
21
- menuItem?: string;
22
- activeClass?: string;
23
- nonActiveClass?: string;
24
- }
25
-
26
- interface Props {
27
- children: Snippet;
28
- canonicalUrl: string;
29
- logoUrl?: string;
30
- companyName?: string;
31
- renderCompanyName?: boolean;
32
- menuItems?: Array<MenuOption>;
33
- footerRender?: Snippet;
34
- classes?: Styles;
35
- }
36
-
37
- let {
38
- children,
39
- canonicalUrl,
40
- logoUrl = undefined,
41
- companyName = '',
42
- renderCompanyName = true,
43
- menuItems = [],
44
- footerRender = undefined,
45
- classes = {},
46
- }: Partial<Props> = $props();
47
-
48
- const defaultClasses = {
49
- logo: 'me-3 h-6 sm:h-9',
50
- companyName: 'self-center whitespace-nowrap text-xl font-semibold dark:text-white',
51
- navigation: '',
52
- menuItem: '',
53
- activeClass: 'text-blue-700 dark:text-white',
54
- nonActiveClass: 'text-gray-700 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white',
55
- footer:
56
- 'absolute start-0 bottom-0 z-20 w-full border-t border-gray-200 bg-white p-4 shadow-sm md:flex md:items-center md:justify-between md:p-6 dark:border-gray-600 dark:bg-gray-800',
57
- footerLinks: ''
58
- };
59
- const mergedClasses = $derived(() => ({ ...defaultClasses, ...classes }));
60
-
61
- let activeUrl = $state(page.url.pathname);
62
-
63
- $effect(() => {
64
- activeUrl = page.url.pathname;
65
- });
66
-
67
- afterNavigate(() => {
68
- window.scrollTo({ top: 0, behavior: "instant" });
69
- });
70
- </script>
71
-
72
- <svelte:head>
73
- <link rel="canonical" href="{canonicalUrl}{page.url.pathname}" />
74
- </svelte:head>
75
-
76
- <div class="w-full relative" id="defaultLayout">
77
- <Navbar class={mergedClasses().navigation}>
78
- <NavBrand href="/" class="logo">
79
- <img src={logoUrl} class={mergedClasses().logo} alt={companyName} />
80
- {#if renderCompanyName}
81
- <span class={mergedClasses().companyName}>{companyName}</span>
82
- {/if}
83
- </NavBrand>
84
- <NavHamburger />
85
- <NavUl
86
- {activeUrl}
87
- classes={{ active: mergedClasses().activeClass, nonActive: mergedClasses().nonActiveClass }}
88
- >
89
- {#each menuItems as item}
90
- {#if item?.items}
91
- <NavLi class={mergedClasses().menuItem}>
92
- <a href={item.href}>{item.label}</a>
93
- <ChevronDownOutline class="text-primary-800 ms-2 inline h-6 w-6 dark:text-white" />
94
- </NavLi>
95
- <Dropdown simple class="w-44">
96
- {#each item.items as subItem}
97
- <DropdownItem class={mergedClasses().menuItem} href={subItem.href}
98
- >{subItem.label}</DropdownItem
99
- >
100
- {/each}
101
- </Dropdown>
102
- {:else}
103
- <NavLi class={mergedClasses().menuItem}>
104
- <a href={item.href}>{item.label}</a>
105
- </NavLi>
106
- {/if}
107
- {/each}
108
- </NavUl>
109
- </Navbar>
110
- <main class="min-h-screen">
111
- {@render children?.()}
112
- </main>
113
- {@render footerRender?.()}
114
-
115
- </div>
@@ -1,21 +0,0 @@
1
- import type { Snippet } from 'svelte';
2
- import { type MenuOption } from './menuItems.js';
3
- declare const DefaultLayout: import("svelte").Component<Partial<{
4
- children: Snippet;
5
- canonicalUrl: string;
6
- logoUrl?: string;
7
- companyName?: string;
8
- renderCompanyName?: boolean;
9
- menuItems?: Array<MenuOption>;
10
- footerRender?: Snippet;
11
- classes?: {
12
- logo?: string;
13
- companyName?: string;
14
- navigation?: string;
15
- menuItem?: string;
16
- activeClass?: string;
17
- nonActiveClass?: string;
18
- };
19
- }>, {}, "">;
20
- type DefaultLayout = ReturnType<typeof DefaultLayout>;
21
- export default DefaultLayout;
@@ -1,63 +0,0 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import type { MenuItem, MenuSection } from './menuItems.js';
4
- import Icon from '../Icon.svelte';
5
- import { afterNavigate } from '$app/navigation';
6
- import {
7
- Sidebar,
8
- SidebarDropdownItem,
9
- SidebarDropdownWrapper,
10
- SidebarGroup,
11
- SidebarItem,
12
- SidebarButton,
13
- uiHelpers
14
- } from 'flowbite-svelte';
15
-
16
- interface TwoColumnLayoutProps {
17
- children: Snippet;
18
- menuItems: Array<MenuSection | MenuItem>;
19
- }
20
-
21
- let { children, menuItems = [] }: Partial<TwoColumnLayoutProps> = $props();
22
-
23
- let container: HTMLElement | undefined = undefined;
24
- const helpers = uiHelpers();
25
- afterNavigate(() => {
26
- window.scrollTo({ top: 0, behavior: 'instant' });
27
- });
28
- </script>
29
-
30
- <section class="flex flex-column md:flex-row" bind:this={container}>
31
- <SidebarButton onclick={helpers.toggle} class="mb-2" />
32
- <Sidebar
33
- backdrop={false}
34
- isOpen={helpers.isOpen}
35
- closeSidebar={helpers.close}
36
- position="fixed"
37
- class="z-50 h-full"
38
- >
39
- <SidebarGroup>
40
- {#each menuItems as item}
41
- {#if 'items' in item}
42
- <SidebarDropdownWrapper label={item.label}>
43
- {#each item.items as subItem}
44
- <SidebarDropdownItem label={subItem.label} href={subItem.href} />
45
- {/each}
46
- </SidebarDropdownWrapper>
47
- {:else}
48
- <SidebarItem label={item.label} href={item.href}>
49
- {#snippet icon()}
50
- <Icon icon={item.icon} />
51
- {/snippet}
52
- </SidebarItem>
53
- {/if}
54
- {/each}
55
- </SidebarGroup>
56
- </Sidebar>
57
- <div class="content" style:flex="1">
58
- {@render children?.()}
59
- </div>
60
- </section>
61
-
62
- <style>
63
- </style>
@@ -1,8 +0,0 @@
1
- import type { Snippet } from 'svelte';
2
- import type { MenuItem, MenuSection } from './menuItems.js';
3
- declare const MenuLayout: import("svelte").Component<Partial<{
4
- children: Snippet;
5
- menuItems: Array<MenuSection | MenuItem>;
6
- }>, {}, "">;
7
- type MenuLayout = ReturnType<typeof MenuLayout>;
8
- export default MenuLayout;
@@ -1,29 +0,0 @@
1
- import type { Page } from "@sveltejs/kit";
2
- export declare enum Visibility {
3
- Always = 0,
4
- Authenticated = 1,
5
- NotAuthenticated = 2
6
- }
7
- export type MenuItem = {
8
- label: string;
9
- href?: string;
10
- icon?: string;
11
- visibility?: Visibility;
12
- };
13
- export type MenuSection = {
14
- label: string;
15
- items: MenuItem[];
16
- href?: string;
17
- icon?: string;
18
- visibility?: Visibility;
19
- };
20
- export type MenuDivider = {};
21
- export type MenuOption = MenuItem | MenuSection;
22
- export declare function selectVisible(pages: Array<MenuItem>, visibility: Visibility): MenuItem[];
23
- export declare function isSelected(includeSubpagesForSelect: boolean, page: any, item: MenuItem): any;
24
- export declare const defaultCss: {
25
- buttonCss: never[];
26
- container: never[];
27
- link: never[];
28
- };
29
- export declare function isAuthenticated(page: Page): Visibility.Authenticated | Visibility.NotAuthenticated;
@@ -1,28 +0,0 @@
1
- export var Visibility;
2
- (function (Visibility) {
3
- Visibility[Visibility["Always"] = 0] = "Always";
4
- Visibility[Visibility["Authenticated"] = 1] = "Authenticated";
5
- Visibility[Visibility["NotAuthenticated"] = 2] = "NotAuthenticated";
6
- })(Visibility || (Visibility = {}));
7
- export function selectVisible(pages, visibility) {
8
- return pages.filter((page) => page?.visibility === Visibility.Always ||
9
- page?.visibility === visibility);
10
- }
11
- export function isSelected(includeSubpagesForSelect, page, item) {
12
- if (page?.url?.pathname === '/') {
13
- return page?.url?.pathname === item.href;
14
- }
15
- return includeSubpagesForSelect
16
- ? (page?.url.pathname.startsWith(item.href ?? '-') && item.href !== '/')
17
- : page?.url.pathname === item.href;
18
- }
19
- export const defaultCss = {
20
- buttonCss: [],
21
- container: [],
22
- link: []
23
- };
24
- export function isAuthenticated(page) {
25
- const isAuthenticated = page?.data?.session != null;
26
- const visibility = isAuthenticated ? Visibility.Authenticated : Visibility.NotAuthenticated;
27
- return visibility;
28
- }