rimecms 0.24.0 → 0.24.2

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 (78) hide show
  1. package/README.md +32 -19
  2. package/dist/core/config/shared/find-title.d.ts +2 -2
  3. package/dist/core/config/shared/find-title.js +1 -1
  4. package/dist/core/operations/hooks/before-read/set-document-title.server.js +15 -2
  5. package/dist/fields/blocks/component/AddBlockButton.svelte +10 -5
  6. package/dist/fields/relation/component/Cell.svelte +10 -4
  7. package/dist/fields/relation/component/default/Default.svelte +1 -1
  8. package/dist/fields/rich-text/index.d.ts +2 -0
  9. package/dist/fields/rich-text/index.js +4 -0
  10. package/dist/panel/components/sections/collection/list/row/Row.svelte +2 -0
  11. package/dist/panel/components/ui/breadcrumb/BreadCrumb.svelte +4 -0
  12. package/dist/panel/components/ui/page-header/PageHeader.svelte +4 -0
  13. package/dist/panel/context/documentForm.svelte.js +13 -1
  14. package/dist/site/components/avatar/avatar.svelte +1 -1
  15. package/dist/site/components/avatar/avatar.svelte.d.ts +1 -1
  16. package/dist/site/components/button/button.svelte +2 -2
  17. package/dist/site/components/card-document/card-document.svelte +92 -0
  18. package/dist/site/components/card-document/card-document.svelte.d.ts +7 -0
  19. package/dist/site/components/dialog/dialog-content.css +47 -0
  20. package/dist/site/components/dialog/dialog-content.svelte +28 -0
  21. package/dist/site/components/dialog/dialog-content.svelte.d.ts +10 -0
  22. package/dist/site/components/dialog/dialog-overlay.css +8 -0
  23. package/dist/site/components/dialog/dialog-overlay.svelte +12 -0
  24. package/dist/site/components/dialog/dialog-overlay.svelte.d.ts +5 -0
  25. package/dist/site/components/dialog/index.d.ts +8 -0
  26. package/dist/site/components/dialog/index.js +10 -0
  27. package/dist/site/components/feed/feed.svelte +12 -9
  28. package/dist/site/components/feed/feed.svelte.d.ts +5 -17
  29. package/dist/site/components/folder/folder.svelte +53 -0
  30. package/dist/site/components/folder/folder.svelte.d.ts +7 -0
  31. package/dist/site/components/nav/nav.svelte +15 -11
  32. package/dist/site/components/nav/post-button/post-button.svelte +52 -0
  33. package/dist/site/components/nav/post-button/post-button.svelte.d.ts +3 -0
  34. package/dist/site/components/post/post-actions/comment-button.svelte +115 -0
  35. package/dist/site/components/post/post-actions/comment-button.svelte.d.ts +6 -0
  36. package/dist/site/components/post/post-actions/like-button.svelte +76 -0
  37. package/dist/site/components/post/post-actions/like-button.svelte.d.ts +6 -0
  38. package/dist/site/components/post/post-actions/post-actions.svelte +21 -11
  39. package/dist/site/components/post/post-actions/post-actions.svelte.d.ts +1 -1
  40. package/dist/site/components/post/post-attachments.svelte +26 -0
  41. package/dist/site/components/post/post-attachments.svelte.d.ts +6 -0
  42. package/dist/site/components/post/post-date.svelte +12 -0
  43. package/dist/site/components/post/post-date.svelte.d.ts +6 -0
  44. package/dist/site/components/post/post-with-comments.svelte +42 -0
  45. package/dist/site/components/post/post-with-comments.svelte.d.ts +6 -0
  46. package/dist/site/components/post/post.svelte +28 -11
  47. package/dist/site/components/post/post.svelte.d.ts +1 -2
  48. package/dist/site/components/rich-text/bubble-menu/bubble-menu.css +17 -0
  49. package/dist/site/components/rich-text/bubble-menu/bubble-menu.svelte +119 -0
  50. package/dist/site/components/rich-text/bubble-menu/bubble-menu.svelte.d.ts +11 -0
  51. package/dist/site/components/rich-text/bubble-menu/icon-button/icon-button.css +15 -0
  52. package/dist/site/components/rich-text/bubble-menu/icon-button/icon-button.svelte +31 -0
  53. package/dist/site/components/rich-text/bubble-menu/icon-button/icon-button.svelte.d.ts +13 -0
  54. package/dist/site/components/rich-text/context.svelte.d.ts +12 -0
  55. package/dist/site/components/rich-text/context.svelte.js +32 -0
  56. package/dist/site/components/rich-text/features/bold.d.ts +2 -0
  57. package/dist/site/components/rich-text/features/bold.js +21 -0
  58. package/dist/site/components/rich-text/features/link/component/link-selector.css +4 -0
  59. package/dist/site/components/rich-text/features/link/component/link-selector.svelte +214 -0
  60. package/dist/site/components/rich-text/features/link/component/link-selector.svelte.d.ts +11 -0
  61. package/dist/site/components/rich-text/features/link/link.d.ts +2 -0
  62. package/dist/site/components/rich-text/features/link/link.js +36 -0
  63. package/dist/site/components/rich-text/rich-text.css +170 -0
  64. package/dist/site/components/rich-text/rich-text.svelte +84 -0
  65. package/dist/site/components/rich-text/rich-text.svelte.d.ts +9 -0
  66. package/dist/site/components/upload-thumbnail/upload-thumbnail.svelte +76 -0
  67. package/dist/site/components/upload-thumbnail/upload-thumbnail.svelte.d.ts +8 -0
  68. package/dist/site/components/upload-thumbnail/util.d.ts +1 -0
  69. package/dist/site/components/upload-thumbnail/util.js +13 -0
  70. package/dist/site/components/user-inline/user-inline.svelte +1 -1
  71. package/dist/site/components/user-inline/user-inline.svelte.d.ts +1 -1
  72. package/dist/site/styles/colors.css +2 -0
  73. package/dist/site/styles/fonts.css +160 -0
  74. package/dist/site/styles/fonts.mixins.css +5 -0
  75. package/dist/site/styles/main.css +8 -6
  76. package/dist/site/styles/shadows.css +10 -0
  77. package/dist/site/styles/sizes.css +43 -0
  78. package/package.json +1 -1
package/README.md CHANGED
@@ -31,6 +31,7 @@ Headless CMS powered by SvelteKit.
31
31
  ### Content Management
32
32
 
33
33
  Fields types:
34
+
34
35
  - Blocks
35
36
  - Tree (nested array)
36
37
  - Tabs
@@ -52,6 +53,7 @@ Fields types:
52
53
  npx sv create my-app
53
54
  cd my-app
54
55
  ```
56
+
55
57
  > [!NOTE]
56
58
  > Select TypeScript when prompted
57
59
 
@@ -83,7 +85,7 @@ import { sveltekit } from '@sveltejs/kit/vite';
83
85
  import { rime } from 'rimecms/vite';
84
86
 
85
87
  export default defineConfig({
86
- plugins: [rime(), sveltekit()]
88
+ plugins: [rime(), sveltekit()]
87
89
  });
88
90
  ```
89
91
 
@@ -198,29 +200,37 @@ export default rime({
198
200
 
199
201
  ```ts
200
202
  export const load = async (event: LayoutServerLoadEvent) => {
201
- const { rime } = event.locals;
202
- // Get an Area document
203
- const menu = await rime.area('menu').find();
204
- // Get all pages documents
205
- const pages = await rime.collection('pages').findAll({ locale: 'en' });
206
- // Get a page byId
207
- const home = await rime.collection('pages').findById({ locale: 'en', id: 'some-id' });
208
- // Get a user with a query
209
- const [user] = await rime.collection('users').find({
210
- query: `where[email][equals]=some@email.com` // qs query or ParsedQsQuery
211
- });
212
- // Get some config values
213
- const languages = rime.config.getLocalesCodes();
214
- const collections = rime.config.collections;
215
- //...
203
+ const { rime } = event.locals;
204
+ // Get an Area document
205
+ const menu = await rime.area('menu').find();
206
+ // Get all pages documents
207
+ const pages = await rime.collection('pages').findAll({ locale: 'en' });
208
+ // Get a page byId
209
+ const home = await rime.collection('pages').findById({ locale: 'en', id: 'some-id' });
210
+ // Get a user with a query
211
+ const [user] = await rime.collection('users').find({
212
+ query: `where[email][equals]=some@email.com` // qs query or ParsedQsQuery
213
+ });
214
+ // Get some config values
215
+ const languages = rime.config.getLocalesCodes();
216
+ const collections = rime.config.collections;
217
+ //...
216
218
  };
217
219
  ```
218
220
 
219
221
  ### From the API :
222
+
220
223
  ```ts
221
- const { docs } = await fetch('http://localhost:5173/api/pages').then(r => r.json())
222
- const { docs } = await fetch('http://localhost:5173/api/pages?sort=title&limit=1').then(r => r.json())
223
- const { docs } = await fetch('http://localhost:5173/api/pages?where[author][like]=some-id&locale=en`;').then(r => r.json())
224
+ const { docs } = await fetch('http://localhost:5173/api/pages').then((r) => r.json());
225
+ const { docs } = await fetch('http://localhost:5173/api/pages?sort=title&limit=1').then((r) =>
226
+ r.json()
227
+ );
228
+ const { docs } = await fetch(
229
+ 'http://localhost:5173/api/pages?where[author][equals]=some-id&locale=en`;'
230
+ ).then((r) => r.json());
231
+ const { docs } = await fetch(
232
+ 'http://localhost:5173/api/pages?where[author.email][equals]=some@email.com&locale=en`;'
233
+ ).then((r) => r.json());
224
234
  ```
225
235
 
226
236
  ## DEPLOYING
@@ -228,10 +238,12 @@ const { docs } = await fetch('http://localhost:5173/api/pages?where[author][like
228
238
  For now I am using it with @svelte/adapter-node, other adapter not tested and probably not working.
229
239
 
230
240
  With the node adapter :
241
+
231
242
  ```sh
232
243
  npx rime build
233
244
  npx rime build -d # to copy the database directory
234
245
  ```
246
+
235
247
  It's doing bascically `vite build` under the hood and create the polka server file inside an app directory, plus giving some info on how to run it.
236
248
 
237
249
  ## ROADMAP
@@ -246,6 +258,7 @@ It's doing bascically `vite build` under the hood and create the polka server fi
246
258
  - [x] Document version
247
259
  - [x] collection nested
248
260
  - [x] more better-auth integration
261
+ - [x] Handle relation poperties in queries
249
262
  - [~] Documentation
250
263
  - [ ] Live Edit system in practice
251
264
  - [ ] auto-saved draft
@@ -4,8 +4,8 @@ import type { DateField } from '../../../fields/date/index.js';
4
4
  import type { EmailField } from '../../../fields/email/index.js';
5
5
  import type { SlugField } from '../../../fields/slug/index.js';
6
6
  import type { TextField } from '../../../fields/text/index.js';
7
- import type { Field, FormField } from '../../../fields/types.js';
8
- export declare const hasMaybeTitle: (field: Field) => field is TextField | DateField | SlugField | EmailField;
7
+ import type { Field, FormField, RichTextField } from '../../../fields/types.js';
8
+ export declare const hasMaybeTitle: (field: Field) => field is TextField | DateField | SlugField | EmailField | RichTextField;
9
9
  interface TitleFieldResult {
10
10
  field: FormFieldBuilder<FormField>;
11
11
  path: string;
@@ -1,7 +1,7 @@
1
1
  import { FormFieldBuilder } from '../../fields/builders/form-field-builder.js';
2
2
  import { isGroupField } from '../../../fields/group/index.js';
3
3
  import { TabsBuilder } from '../../../fields/tabs/index.js';
4
- export const hasMaybeTitle = (field) => ['text', 'date', 'slug', 'email'].includes(field.type);
4
+ export const hasMaybeTitle = (field) => ['text', 'date', 'slug', 'email', 'richText'].includes(field.type);
5
5
  export function findTitleField(fields = [], basePath = '') {
6
6
  for (const field of fields) {
7
7
  // Direct check for isTitle
@@ -1,4 +1,5 @@
1
- import { getValueAtPath } from '../../../../util/object.js';
1
+ import { getValueAtPath, isObjectLiteral } from '../../../../util/object.js';
2
+ import { richTextJSONToText } from '../../../../fields/rich-text/client.js';
2
3
  import { Hooks } from '../index.server.js';
3
4
  export const setDocumentTitle = Hooks.beforeRead(async (args) => {
4
5
  const config = args.config;
@@ -7,8 +8,20 @@ export const setDocumentTitle = Hooks.beforeRead(async (args) => {
7
8
  const hasSelect = Array.isArray(paramSelect) && paramSelect.length;
8
9
  const shouldSetTitle = !doc.title && (!hasSelect || (hasSelect && paramSelect.includes('title')));
9
10
  if (shouldSetTitle) {
11
+ function computeTitleFromValue(value) {
12
+ // Handle rich text value
13
+ if (isObjectLiteral(value) && 'content' in value) {
14
+ return richTextJSONToText(value);
15
+ }
16
+ if (typeof value === 'string') {
17
+ return value;
18
+ }
19
+ return doc.id;
20
+ }
21
+ const titleRaw = getValueAtPath(config.asTitle, doc);
22
+ const title = computeTitleFromValue(titleRaw);
10
23
  doc = {
11
- title: getValueAtPath(config.asTitle, doc),
24
+ title,
12
25
  ...doc
13
26
  };
14
27
  }
@@ -30,7 +30,12 @@
30
30
  </script>
31
31
 
32
32
  {#if config.blocks.length === 1}
33
- <Button onclick={() => add(config.blocks[0].block)} variant="ghost" icon={CirclePlus} size="icon" />
33
+ <Button
34
+ onclick={() => add(config.blocks[0].block)}
35
+ variant="ghost"
36
+ icon={CirclePlus}
37
+ size="icon"
38
+ />
34
39
  {:else}
35
40
  <Button onclick={() => (open = true)} variant="ghost" icon={CirclePlus} size="icon" />
36
41
 
@@ -77,13 +82,13 @@
77
82
  </Command.List>
78
83
 
79
84
  <div class="rz-add-block-button__preview-wrap">
80
- {#each config.blocks as block, index (index)}
85
+ {#each config.blocks as blockFieldBuilder, index (index)}
81
86
  <div
82
- class:rz-add-block-button__preview--active={ariaSelected === block.name}
87
+ class:rz-add-block-button__preview--active={ariaSelected === blockFieldBuilder.name}
83
88
  class="rz-add-block-button__preview"
84
89
  >
85
- {#if block.block.image}
86
- <img src="{env.PUBLIC_RIME_URL}{block.image}" alt="preview" />
90
+ {#if blockFieldBuilder.block.image}
91
+ <img src="{env.PUBLIC_RIME_URL}{blockFieldBuilder.block.image}" alt="preview" />
87
92
  {:else}
88
93
  no preview
89
94
  {/if}
@@ -17,7 +17,7 @@
17
17
  let { value }: Props = $props();
18
18
  let displayCount = $derived(value && value.length > 1);
19
19
 
20
- const APIProxy = getAPIProxyContext(API_PROXY.DOCUMENT);
20
+ const APIProxy = getAPIProxyContext(API_PROXY.ROOT);
21
21
 
22
22
  let APIUrl = $derived.by(() => {
23
23
  if (value && value.length && value[0].documentId) {
@@ -33,7 +33,7 @@
33
33
  });
34
34
  </script>
35
35
 
36
- <span>
36
+ <span class="rz-relation-cell">
37
37
  {#if displayCount}
38
38
  {value.length} items
39
39
  {:else if ressource?.data?.doc}
@@ -44,15 +44,21 @@
44
44
  />
45
45
  {ressource.data.doc.title}
46
46
  {:else}
47
- {ressource.data.doc.title}
47
+ <span class="rz-relation-cell__title">{ressource.data.doc.title}</span>
48
48
  {/if}
49
49
  {/if}
50
50
  </span>
51
51
 
52
52
  <style>
53
- span {
53
+ .rz-relation-cell {
54
54
  display: flex;
55
55
  gap: var(--rz-size-2);
56
56
  align-items: center;
57
57
  }
58
+ .rz-relation-cell__title {
59
+ overflow: hidden;
60
+ display: -webkit-box;
61
+ -webkit-box-orient: vertical;
62
+ -webkit-line-clamp: 1;
63
+ }
58
64
  </style>
@@ -83,7 +83,7 @@
83
83
  >
84
84
  {#each selectedItems as item (item.documentId)}
85
85
  <Tag onRemove={() => removeValue(item.documentId)} {readOnly}>
86
- {item.label}
86
+ <a href={item.editUrl}>{item.label}</a>
87
87
  </Tag>
88
88
  {/each}
89
89
 
@@ -9,6 +9,7 @@ export declare class RichTextFieldBuilder extends FormFieldBuilder<RichTextField
9
9
  get cell(): import("svelte").Component<{
10
10
  value: string;
11
11
  }, {}, "">;
12
+ isTitle(): this;
12
13
  /**
13
14
  * Sets a custom TipTap editor configuration for the rich text field.
14
15
  *
@@ -44,6 +45,7 @@ type RichTextContent = {
44
45
  };
45
46
  export type RichTextField = FormField & {
46
47
  type: 'richText';
48
+ isTitle?: true;
47
49
  features?: Array<RichTextFeature>;
48
50
  defaultValue?: RichTextContent | DefaultValueFn<RichTextContent>;
49
51
  };
@@ -33,6 +33,10 @@ export class RichTextFieldBuilder extends FormFieldBuilder {
33
33
  get cell() {
34
34
  return Cell;
35
35
  }
36
+ isTitle() {
37
+ this.field.isTitle = true;
38
+ return this;
39
+ }
36
40
  /**
37
41
  * Sets a custom TipTap editor configuration for the rich text field.
38
42
  *
@@ -90,6 +90,8 @@
90
90
  width: var(--rz-size-10);
91
91
  border-radius: var(--rz-radius-md);
92
92
  display: flex;
93
+ flex-shrink: 0;
94
+ flex-grow: 0;
93
95
  align-items: center;
94
96
  justify-content: center;
95
97
  background-color: light-dark(hsl(var(--rz-gray-15)), hsl(var(--rz-gray-1)));
@@ -41,6 +41,10 @@
41
41
  }
42
42
  }
43
43
  .rz-aria__last {
44
+ overflow: hidden;
45
+ display: -webkit-box;
46
+ -webkit-box-orient: vertical;
47
+ -webkit-line-clamp: 1;
44
48
  opacity: 1;
45
49
  }
46
50
  </style>
@@ -70,6 +70,10 @@
70
70
  }
71
71
 
72
72
  h1 {
73
+ overflow: hidden;
74
+ display: -webkit-box;
75
+ -webkit-box-orient: vertical;
76
+ -webkit-line-clamp: 1;
73
77
  font-size: var(--rz-text-3xl);
74
78
  font-variation-settings: 'wght' 600;
75
79
  font-weight: 600;
@@ -23,6 +23,7 @@ import { getCollectionContext } from './collection.svelte.js';
23
23
  import { setErrorsContext } from './errors.svelte.js';
24
24
  import { getLocaleContext } from './locale.svelte.js';
25
25
  import { getUserContext } from './user.svelte.js';
26
+ import { richTextJSONToText } from '../../fields/rich-text/client.js';
26
27
  function createDocumentFormState({ initial, element, config, readOnly, key, onNestedDocumentCreated, onDataChange, beforeSubmit, beforeRedirect, onFieldFocus }) {
27
28
  //
28
29
  let intialDoc = $state(initial);
@@ -56,8 +57,19 @@ function createDocumentFormState({ initial, element, config, readOnly, key, onNe
56
57
  return documentConfig.label;
57
58
  }
58
59
  else {
60
+ function computeTitleFromValue(value) {
61
+ // Handle rich text value
62
+ if (isObjectLiteral(value) && 'content' in value) {
63
+ return richTextJSONToText(value);
64
+ }
65
+ if (typeof value === 'string') {
66
+ return value;
67
+ }
68
+ return initialTitle || doc.id;
69
+ }
59
70
  $effect(() => {
60
- title = getValueAtPath(documentConfig.asTitle, doc) || '[untitled]';
71
+ const rawTitle = getValueAtPath(documentConfig.asTitle, doc) || '[untitled]';
72
+ title = computeTitleFromValue(rawTitle);
61
73
  if (nestedLevel === 0) {
62
74
  titleContext.value = title;
63
75
  }
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { isRelationPopulated } from '../../../fields/relation/client';
3
3
 
4
- type Props = { user: UsersDoc };
4
+ type Props = { user: StaffDoc };
5
5
  const { user }: Props = $props();
6
6
 
7
7
  const avatar = $derived(user.avatar && isRelationPopulated(user.avatar) ? user.avatar : null);
@@ -1,5 +1,5 @@
1
1
  type Props = {
2
- user: UsersDoc;
2
+ user: StaffDoc;
3
3
  };
4
4
  declare const Avatar: import("svelte").Component<Props, {}, "">;
5
5
  type Avatar = ReturnType<typeof Avatar>;
@@ -259,8 +259,8 @@
259
259
 
260
260
  /** Text */
261
261
  .button--text {
262
- padding-left: 0;
263
- padding-right: 0;
262
+ padding: 0;
263
+ height: auto;
264
264
  background-color: var(--button-text-bg);
265
265
  color: var(--button-text-fg);
266
266
 
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import type { UploadDoc } from '../../../types';
3
+ import UploadThumbnail from '../upload-thumbnail/upload-thumbnail.svelte';
4
+
5
+ type Props = { doc: UploadDoc };
6
+ const { doc }: Props = $props();
7
+
8
+ const thumbnailUrl = $derived.by(() => {
9
+ if (doc.mimeType && doc.mimeType.includes('image')) {
10
+ return doc._thumbnail;
11
+ }
12
+ return null;
13
+ });
14
+ </script>
15
+
16
+ <div class="document-card">
17
+ <div class="document-card__preview">
18
+ <UploadThumbnail mimeType={doc.mimeType} url={thumbnailUrl} />
19
+ </div>
20
+
21
+ <div class="document-card__body">
22
+ <p class="document-card__title">
23
+ {doc.title}
24
+ </p>
25
+ </div>
26
+ </div>
27
+
28
+ <style>
29
+ :root {
30
+ --card-hover-bg: light-dark(var(--gray-14), var(--gray-3));
31
+ --card-bg: light-dark(var(--gray-16), var(--gray-2));
32
+ }
33
+
34
+ .document-card {
35
+ max-height: 200px;
36
+ border: var(--border);
37
+ background-color: var(--card-bg);
38
+ border-radius: var(--radius-sm);
39
+ aspect-ratio: 4 / 5;
40
+ width: 100%;
41
+ display: grid;
42
+ }
43
+
44
+ .document-card:hover {
45
+ background-color: var(--card-hover-bg);
46
+ }
47
+
48
+ .document-card__preview {
49
+ width: 100%;
50
+ height: 100%;
51
+ border-top-left-radius: var(--radius-sm);
52
+ border-top-right-radius: var(--radius-sm);
53
+ background-color: light-dark(var(--gray-12), var(--gray-4));
54
+ overflow: hidden;
55
+ }
56
+
57
+ .document-card :global(.upload-preview-cell) {
58
+ width: 100%;
59
+ height: 100%;
60
+ aspect-ratio: 5/4;
61
+ border-bottom-left-radius: 0;
62
+ border-bottom-right-radius: 0;
63
+ padding: var(--size-1);
64
+ overflow: hidden;
65
+ > div {
66
+ border-radius: var(--radius-sm);
67
+ overflow: hidden;
68
+ }
69
+ }
70
+
71
+ .document-card__title {
72
+ margin-top: var(--size-2);
73
+ display: -webkit-box;
74
+ -webkit-line-clamp: 2;
75
+ -webkit-box-orient: vertical;
76
+ overflow: hidden;
77
+ word-break: break-all;
78
+ text-align: left;
79
+ font-family: var(--font-text);
80
+ font-size: var(--font-size-text-xs);
81
+ line-height: 1.33;
82
+ font-weight: 400;
83
+ }
84
+
85
+ .document-card__body {
86
+ display: flex;
87
+ flex-direction: column;
88
+ gap: var(--size-2);
89
+ text-align: left;
90
+ padding: 0 0.6rem 0.6rem 0.6rem;
91
+ }
92
+ </style>
@@ -0,0 +1,7 @@
1
+ import type { UploadDoc } from '../../../types';
2
+ type Props = {
3
+ doc: UploadDoc;
4
+ };
5
+ declare const CardDocument: import("svelte").Component<Props, {}, "">;
6
+ type CardDocument = ReturnType<typeof CardDocument>;
7
+ export default CardDocument;
@@ -0,0 +1,47 @@
1
+ .dialog-content {
2
+ --gutter: var(--size-4);
3
+ background-color: var(--color-bg);
4
+ position: fixed;
5
+ left: calc(50% - var(--gutter));
6
+ top: 30%;
7
+ z-index: 1010;
8
+ display: grid;
9
+ width: calc(100% - 2 * var(--gutter));
10
+ transform: translate(-50%, -50%);
11
+ gap: var(--size-4);
12
+ padding: var(--size-3);
13
+ box-shadow: var(--shadow-lg);
14
+ border-radius: var(--radius-lg);
15
+ margin: 0 var(--gutter);
16
+ }
17
+
18
+ @media (min-width: 768px) {
19
+ .dialog-content--sm {
20
+ width: var(--size-xs);
21
+ }
22
+ .dialog-content--default {
23
+ width: var(--size-sm);
24
+ }
25
+ .dialog-content--lg {
26
+ width: var(--size-lg);
27
+ }
28
+ }
29
+
30
+ .dialog-content__close {
31
+ color: hsl(from var(--color-fg) h s l / 0.7);
32
+ position: absolute;
33
+ right: var(--size-4);
34
+ top: var(--size-3);
35
+ border-radius: var(--radius-sm);
36
+ opacity: 0.7;
37
+ transition: opacity 0.2s;
38
+ }
39
+
40
+ .dialog-content__close:focus {
41
+ outline-offset: 2px;
42
+ outline: 2px solid var(--color-fg);
43
+ }
44
+
45
+ .dialog-content__close:disabled {
46
+ pointer-events: none;
47
+ }
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import { Dialog as DialogPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
3
+ import type { Snippet } from 'svelte';
4
+ import * as Dialog from './index.js';
5
+ import './dialog-content.css';
6
+
7
+ let {
8
+ ref = $bindable(null),
9
+ class: className,
10
+ size = 'default',
11
+ children,
12
+ ...restProps
13
+ }: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
14
+ children: Snippet;
15
+ size?: 'sm' | 'default' | 'lg';
16
+ } = $props();
17
+ </script>
18
+
19
+ <Dialog.Portal>
20
+ <Dialog.Overlay />
21
+ <DialogPrimitive.Content
22
+ bind:ref
23
+ class="dialog-content dialog-content--{size} {className}"
24
+ {...restProps}
25
+ >
26
+ {@render children?.()}
27
+ </DialogPrimitive.Content>
28
+ </Dialog.Portal>
@@ -0,0 +1,10 @@
1
+ import { Dialog as DialogPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
2
+ import type { Snippet } from 'svelte';
3
+ import './dialog-content.css';
4
+ type $$ComponentProps = WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
5
+ children: Snippet;
6
+ size?: 'sm' | 'default' | 'lg';
7
+ };
8
+ declare const DialogContent: import("svelte").Component<$$ComponentProps, {}, "ref">;
9
+ type DialogContent = ReturnType<typeof DialogContent>;
10
+ export default DialogContent;
@@ -0,0 +1,8 @@
1
+ .dialog-overlay {
2
+ background-color: hsl(from var(--color-fg) h s l / 0.05);
3
+ position: fixed;
4
+ inset: 0;
5
+ z-index: 1010;
6
+ -webkit-backdrop-filter: blur(3px);
7
+ backdrop-filter: blur(3px);
8
+ }
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import { Dialog as DialogPrimitive } from 'bits-ui';
3
+ import './dialog-overlay.css';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: DialogPrimitive.OverlayProps = $props();
10
+ </script>
11
+
12
+ <DialogPrimitive.Overlay bind:ref class="dialog-overlay {className}" {...restProps} />
@@ -0,0 +1,5 @@
1
+ import { Dialog as DialogPrimitive } from 'bits-ui';
2
+ import './dialog-overlay.css';
3
+ declare const DialogOverlay: import("svelte").Component<DialogPrimitive.OverlayProps, {}, "ref">;
4
+ type DialogOverlay = ReturnType<typeof DialogOverlay>;
5
+ export default DialogOverlay;
@@ -0,0 +1,8 @@
1
+ import { Dialog as DialogPrimitive } from 'bits-ui';
2
+ import Overlay from './dialog-overlay.svelte';
3
+ import Content from './dialog-content.svelte';
4
+ declare const Root: import("svelte").Component<import("bits-ui").AlertDialogRootPropsWithoutHTML, {}, "open">;
5
+ declare const Trigger: import("svelte").Component<DialogPrimitive.TriggerProps, {}, "ref">;
6
+ declare const Close: import("svelte").Component<DialogPrimitive.TriggerProps, {}, "ref">;
7
+ declare const Portal: import("svelte").Component<import("bits-ui").PortalProps, {}, "">;
8
+ export { Root, Portal, Trigger, Overlay, Content, Close, Root as Dialog, Portal as DialogPortal, Trigger as DialogTrigger, Overlay as DialogOverlay, Content as DialogContent, Close as DialogClose };
@@ -0,0 +1,10 @@
1
+ import { Dialog as DialogPrimitive } from 'bits-ui';
2
+ import Overlay from './dialog-overlay.svelte';
3
+ import Content from './dialog-content.svelte';
4
+ const Root = DialogPrimitive.Root;
5
+ const Trigger = DialogPrimitive.Trigger;
6
+ const Close = DialogPrimitive.Close;
7
+ const Portal = DialogPrimitive.Portal;
8
+ export { Root, Portal, Trigger, Overlay, Content, Close,
9
+ //
10
+ Root as Dialog, Portal as DialogPortal, Trigger as DialogTrigger, Overlay as DialogOverlay, Content as DialogContent, Close as DialogClose };
@@ -1,17 +1,20 @@
1
1
  <script lang="ts">
2
- import { page } from '$app/state';
3
- import Post from '../post/post.svelte';
2
+ import Post from '../post/post.svelte';
3
+
4
+ let { posts }: { posts: PostsDoc[] } = $props();
4
5
  </script>
5
6
 
6
7
  <div>
7
- {#each page.data.posts as post}
8
- <Post {post} />
9
- {/each}
8
+ {#each posts as post (post.id)}
9
+ <Post {post} />
10
+ {/each}
10
11
  </div>
11
12
 
12
13
  <style>
13
- div {
14
- padding-inline: var(--size-8);
15
- padding-block: var(--size-8);
16
- }
14
+ div {
15
+ padding-inline: var(--size-8);
16
+ padding-block: var(--size-8);
17
+ display: grid;
18
+ gap: var(--size-10);
19
+ }
17
20
  </style>
@@ -1,18 +1,6 @@
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 Feed: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {}, {}, string>;
17
- type Feed = InstanceType<typeof Feed>;
1
+ type $$ComponentProps = {
2
+ posts: PostsDoc[];
3
+ };
4
+ declare const Feed: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type Feed = ReturnType<typeof Feed>;
18
6
  export default Feed;