@turnipxenon/pineapple 2.4.13 → 2.4.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 (99) hide show
  1. package/.idea/discord.xml +1 -1
  2. package/.idea/workspace.xml +179 -149
  3. package/.svelte-kit/__package__/app.postcss +43 -3
  4. package/.svelte-kit/__package__/assets/icons/close.svg +1 -0
  5. package/.svelte-kit/__package__/components/Card.svelte +1 -1
  6. package/.svelte-kit/__package__/components/Chip.svelte +1 -0
  7. package/.svelte-kit/__package__/components/DialogOverlay.svelte +48 -47
  8. package/.svelte-kit/__package__/components/dialog_manager/DialogManager.d.ts +8 -3
  9. package/.svelte-kit/__package__/components/dialog_manager/DialogManager.js +35 -11
  10. package/.svelte-kit/__package__/components/dialog_manager/DialogManagerStore.js +1 -1
  11. package/.svelte-kit/__package__/components/layouts/SeaweedBaseLayout.svelte +2 -2
  12. package/.svelte-kit/__package__/components/{layouts → pineapple}/PineappleBaseLayout.svelte +66 -30
  13. package/.svelte-kit/__package__/components/pineapple/toast/DefaultToastBody.d.ts +8 -0
  14. package/.svelte-kit/__package__/components/pineapple/toast/DefaultToastBody.js +1 -0
  15. package/.svelte-kit/__package__/components/pineapple/toast/DefaultToastBody.svelte +48 -0
  16. package/.svelte-kit/__package__/components/pineapple/toast/DefaultToastBody.svelte.d.ts +20 -0
  17. package/.svelte-kit/__package__/components/pineapple/toast/Toast.d.ts +27 -0
  18. package/.svelte-kit/__package__/components/pineapple/toast/Toast.js +37 -0
  19. package/.svelte-kit/__package__/components/pineapple/toast/Toast.svelte +90 -0
  20. package/.svelte-kit/__package__/components/pineapple/toast/Toast.svelte.d.ts +14 -0
  21. package/.svelte-kit/__package__/components/pineapple/toast/custom-toast/TestCustomToast.d.ts +5 -0
  22. package/.svelte-kit/__package__/components/pineapple/toast/custom-toast/TestCustomToast.js +1 -0
  23. package/.svelte-kit/__package__/components/pineapple/toast/custom-toast/TestCustomToast.svelte +1 -0
  24. package/.svelte-kit/__package__/components/pineapple/toast/custom-toast/TestCustomToast.svelte.d.ts +23 -0
  25. package/.svelte-kit/__package__/index.d.ts +1 -1
  26. package/.svelte-kit/__package__/index.js +1 -1
  27. package/.svelte-kit/__package__/scripts/pineapple_fiber/PineappleFiberParser.d.ts +2 -0
  28. package/.svelte-kit/__package__/scripts/pineapple_fiber/PineappleFiberParser.js +137 -0
  29. package/.svelte-kit/__package__/scripts/pineapple_fiber/PineappleWeaver.js +3 -134
  30. package/.svelte-kit/__package__/types/pineapple_fiber/DialogState.d.ts +2 -1
  31. package/.svelte-kit/__package__/types/pineapple_fiber/DialogState.js +1 -0
  32. package/.svelte-kit/ambient.d.ts +2 -0
  33. package/.svelte-kit/generated/client/app.js +1 -1
  34. package/.svelte-kit/generated/client/nodes/5.js +1 -1
  35. package/.svelte-kit/generated/server/internal.js +1 -1
  36. package/.svelte-kit/types/route_meta_data.json +1 -1
  37. package/.svelte-kit/types/src/routes/$types.d.ts +1 -1
  38. package/.svelte-kit/types/src/routes/(pineapple)/$types.d.ts +1 -1
  39. package/.svelte-kit/types/src/routes/(pineapple)/{personal → pineapple}/$types.d.ts +1 -1
  40. package/dist/app.postcss +43 -3
  41. package/dist/assets/icons/close.svg +1 -0
  42. package/dist/components/Card.svelte +1 -1
  43. package/dist/components/Chip.svelte +1 -0
  44. package/dist/components/DialogOverlay.svelte +48 -47
  45. package/dist/components/dialog_manager/DialogManager.d.ts +8 -3
  46. package/dist/components/dialog_manager/DialogManager.js +35 -11
  47. package/dist/components/dialog_manager/DialogManagerStore.js +1 -1
  48. package/dist/components/layouts/SeaweedBaseLayout.svelte +2 -2
  49. package/dist/components/{layouts → pineapple}/PineappleBaseLayout.svelte +66 -30
  50. package/dist/components/pineapple/toast/DefaultToastBody.d.ts +8 -0
  51. package/dist/components/pineapple/toast/DefaultToastBody.js +1 -0
  52. package/dist/components/pineapple/toast/DefaultToastBody.svelte +48 -0
  53. package/dist/components/pineapple/toast/DefaultToastBody.svelte.d.ts +20 -0
  54. package/dist/components/pineapple/toast/Toast.d.ts +27 -0
  55. package/dist/components/pineapple/toast/Toast.js +37 -0
  56. package/dist/components/pineapple/toast/Toast.svelte +90 -0
  57. package/dist/components/pineapple/toast/Toast.svelte.d.ts +14 -0
  58. package/dist/components/pineapple/toast/custom-toast/TestCustomToast.d.ts +5 -0
  59. package/dist/components/pineapple/toast/custom-toast/TestCustomToast.js +1 -0
  60. package/dist/components/pineapple/toast/custom-toast/TestCustomToast.svelte +1 -0
  61. package/dist/components/pineapple/toast/custom-toast/TestCustomToast.svelte.d.ts +23 -0
  62. package/dist/index.d.ts +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/scripts/pineapple_fiber/PineappleFiberParser.d.ts +2 -0
  65. package/dist/scripts/pineapple_fiber/PineappleFiberParser.js +137 -0
  66. package/dist/scripts/pineapple_fiber/PineappleWeaver.js +3 -134
  67. package/dist/types/pineapple_fiber/DialogState.d.ts +2 -1
  68. package/dist/types/pineapple_fiber/DialogState.js +1 -0
  69. package/docs/OverlaySpec.md +23 -0
  70. package/docs/pull_request_template.md +34 -0
  71. package/package.json +1 -1
  72. package/src/lib/app.postcss +43 -3
  73. package/src/lib/assets/icons/close.svg +1 -0
  74. package/src/lib/components/Card.svelte +1 -1
  75. package/src/lib/components/Chip.svelte +1 -0
  76. package/src/lib/components/DialogOverlay.svelte +56 -54
  77. package/src/lib/components/dialog_manager/DialogManager.ts +38 -12
  78. package/src/lib/components/dialog_manager/DialogManagerStore.ts +1 -1
  79. package/src/lib/components/layouts/SeaweedBaseLayout.svelte +2 -2
  80. package/src/lib/components/pineapple/PineappleBaseLayout.svelte +220 -0
  81. package/src/lib/components/pineapple/toast/DefaultToastBody.svelte +43 -0
  82. package/src/lib/components/pineapple/toast/DefaultToastBody.ts +10 -0
  83. package/src/lib/components/pineapple/toast/Toast.svelte +114 -0
  84. package/src/lib/components/pineapple/toast/Toast.ts +57 -0
  85. package/src/lib/components/pineapple/toast/custom-toast/TestCustomToast.svelte +1 -0
  86. package/src/lib/components/pineapple/toast/custom-toast/TestCustomToast.ts +6 -0
  87. package/src/lib/index.ts +1 -1
  88. package/src/lib/scripts/pineapple_fiber/PineappleFiberParser.ts +177 -0
  89. package/src/lib/scripts/pineapple_fiber/PineappleWeaver.ts +3 -176
  90. package/src/lib/types/pineapple_fiber/DialogState.ts +2 -1
  91. package/src/routes/(pineapple)/+layout.svelte +1 -1
  92. package/src/routes/(pineapple)/+page.svelte +3 -3
  93. package/src/routes/(pineapple)/pineapple/+page.svelte +62 -0
  94. package/src/routes/(pineapple)/pineapple/TestDialog.yarn +7 -0
  95. package/vite.config.ts +2 -1
  96. package/src/lib/components/layouts/PineappleBaseLayout.svelte +0 -182
  97. package/src/routes/(pineapple)/personal/+page.svelte +0 -37
  98. /package/.svelte-kit/__package__/components/{layouts → pineapple}/PineappleBaseLayout.svelte.d.ts +0 -0
  99. /package/dist/components/{layouts → pineapple}/PineappleBaseLayout.svelte.d.ts +0 -0
@@ -6,7 +6,7 @@ import { writable } from "svelte/store";
6
6
  import type { DialogDetail } from "$pkg/types/pineapple_fiber/DialogDetail";
7
7
  import { DialogState } from "$pkg/types/pineapple_fiber/DialogState";
8
8
  import { tweened } from "svelte/motion";
9
- import { cubicOut } from "svelte/easing";
9
+ import { backOut } from "svelte/easing";
10
10
  import { PortraitType } from "$pkg/types/pineapple_fiber/PortraitType";
11
11
  import AresHappy from "$pkg/assets/characters/ares/ares_happy.webp";
12
12
  import AresBlushing from "$pkg/assets/characters/ares/ares_blushing.webp";
@@ -24,6 +24,7 @@ import {
24
24
  updateRate
25
25
  } from "$pkg/components/dialog_manager/DialogManagerStore";
26
26
  import { DialogProcessor } from "$pkg/components/dialog_manager/DialogProcessor";
27
+ import { parseYarn } from "$pkg/scripts/pineapple_fiber/PineappleFiberParser";
27
28
 
28
29
  const shouldDebugYarn = false;
29
30
 
@@ -31,7 +32,7 @@ export type OnSetDialogChoiceCallback = (newMessage: DialogDetail) => void;
31
32
 
32
33
  export class DialogManager {
33
34
  dialogMessageMap: Map<string, DialogDetail> = new Map();
34
- dialogMessageList: DialogDetail[] = [];
35
+ currentDialogTree: DialogDetail[] = [];
35
36
  fullCurrentMessage: string = defaultDialogMessage[0].textContent;
36
37
  currentMessageMeta: DialogDetail = defaultDialogMessage[0];
37
38
  currentMessage = writable("");
@@ -41,9 +42,10 @@ export class DialogManager {
41
42
  currentPortrait = writable();
42
43
  portraitMap: Map<string, any> = new Map();
43
44
  currentState = DialogState.Visible;
45
+ currentReadableState = writable(this.currentState);
44
46
  hidePercent = tweened(100, {
45
47
  duration: 400,
46
- easing: cubicOut
48
+ easing: backOut
47
49
  }); // 100 = 100%
48
50
  skipNextActiveTime = 0;
49
51
  dialogProcessor = new DialogProcessor();
@@ -51,16 +53,23 @@ export class DialogManager {
51
53
  _setDialogChoiceQueue: DialogDetail[] = [];
52
54
  _setDialogChoiceMutex = false;
53
55
  onSetDialogListeners: OnSetDialogChoiceCallback[] = [];
56
+ enableDialogueOverlayCache = false;
54
57
 
55
58
  constructor() {
56
59
  enableDialogueOverlay.subscribe((value) => {
57
60
  // todo: investigate why we cant put setDialogDefault inside the then clause
58
61
  // ISSUE #82 https://github.com/TurnipXenon/pineapple/issues/82
62
+ this.enableDialogueOverlayCache = value;
59
63
  if (value) {
60
- this.hidePercent.set(0);
61
- this.setDialogToDefault();
64
+ this.hidePercent.set(0).then(() => {
65
+ this.currentState = DialogState.Visible;
66
+ this.currentReadableState.set(this.currentState);
67
+ });
62
68
  } else {
63
- this.hidePercent.set(100);
69
+ this.hidePercent.set(100).then(() => {
70
+ this.currentState = DialogState.Invisible;
71
+ this.currentReadableState.set(this.currentState);
72
+ });
64
73
  this.setDialogTree([{ textContent: "" }]);
65
74
  }
66
75
  });
@@ -93,13 +102,13 @@ export class DialogManager {
93
102
  * sets the possible dialog that might appear on the Dialog UI
94
103
  * note that it overwrites the previous tree and does not append on it due to the possibility
95
104
  * of node name conflicts
96
- * @param newMessageList
105
+ * @param newDialogTree
97
106
  */
98
- setDialogTree = (newMessageList: DialogDetail[]) => {
99
- this.dialogMessageList = newMessageList;
107
+ setDialogTree = (newDialogTree: DialogDetail[]) => {
108
+ this.currentDialogTree = newDialogTree;
100
109
 
101
110
  this.dialogMessageMap.clear();
102
- newMessageList.map((value) => {
111
+ newDialogTree.map((value) => {
103
112
  if (value.dialogId) {
104
113
  this.dialogMessageMap.set(value.dialogId!, value);
105
114
  }
@@ -118,7 +127,7 @@ export class DialogManager {
118
127
  this.portraitMap.set(PortraitType.AresYay.toString(), AresYay);
119
128
  }
120
129
 
121
- this.setDialogChoice(newMessageList[0]);
130
+ this.setDialogChoice(newDialogTree[0]);
122
131
  };
123
132
 
124
133
  /**
@@ -265,7 +274,7 @@ export class DialogManager {
265
274
  while (
266
275
  this.fullCurrentMessage[this.currentIndex] == "<" &&
267
276
  this.currentIndex + 1 < this.fullCurrentMessage.length
268
- ) {
277
+ ) {
269
278
  // find valid character, trap with closing
270
279
  this.currentIndex = this.fullCurrentMessage.indexOf(">", this.currentIndex) + 1;
271
280
  // normalize
@@ -279,4 +288,21 @@ export class DialogManager {
279
288
 
280
289
  window.requestAnimationFrame(this.update);
281
290
  };
291
+
292
+ enableDialogOverlay(enable: boolean) {
293
+ enableDialogueOverlay.set(enable);
294
+ }
295
+
296
+ toggleDialogOverlay() {
297
+ enableDialogueOverlay.set(!this.enableDialogueOverlayCache);
298
+ };
299
+
300
+ async parseAndSetDialogTree(dialogYarn: string): Promise<DialogDetail[]> {
301
+ return parseYarn(dialogYarn)
302
+ .then((dialogTree) => {
303
+ console.log(dialogTree);
304
+ this.setDialogTree(dialogTree);
305
+ return dialogTree;
306
+ });
307
+ }
282
308
  }
@@ -32,7 +32,7 @@ export const updateRate: number = 40 / 1000; // *at least* 40ms per letter
32
32
  // todo: if we go through doing yarn to typescript, move this!
33
33
  export const defaultDialogMessage: DialogDetail[] = [
34
34
  {
35
- textContent: `<p>Have you drank water? Or perhaps, you've checked out <a target="_blank" class="external-link" href="http://crouton.net">one of the best webpages</a> out there?`
35
+ textContent: `<p>I don't really have anything to say. Have you drank water? Or perhaps, you've checked out <a target="_blank" class="external-link" href="http://crouton.net">one of the best webpages</a> out there?`
36
36
  }
37
37
  ];
38
38
 
@@ -66,8 +66,8 @@
66
66
 
67
67
  <style lang="postcss">
68
68
  :root {
69
- --dialog-left-pad: clamp(0em, 5vw, 2em);
70
- --dialog-box-width: min(calc(50em + 4em), calc(100vw - var(--dialog-left-pad) - var(--theme-border-base)));
69
+ --dialog-start-pad: clamp(0em, 5vw, 2em);
70
+ --dialog-box-width: min(calc(50em + 4em), calc(100vw - var(--dialog-start-pad) - var(--theme-border-base)));
71
71
  --dialog-box-height: clamp(15em, 50vw, 18em);
72
72
  }
73
73
 
@@ -0,0 +1,220 @@
1
+ <script lang="ts">
2
+ // For auto dark/light mode
3
+ import { AppBar, AppShell, autoModeWatcher, LightSwitch } from "@skeletonlabs/skeleton";
4
+ import RandomizedBackground from "$pkg/components/RandomizedBackground.svelte";
5
+
6
+ // navigation
7
+ import { page } from "$app/stores";
8
+ // store
9
+ import { enableBackground } from "$pkg/store";
10
+ import type { BreadcrumbData } from "$pkg/types/BreadcrumbData";
11
+ // assets
12
+ // import DialogOverlay from "$lib/components/DialogOverlay.svelte";
13
+ import AresLogo from "$pkg/assets/characters/ares/ares_logo.webp";
14
+ import FABIcon from "$pkg/assets/placeholder/placeholder_circle.png";
15
+ import CloseIcon from "$pkg/assets/icons/close.svg";
16
+ import { dialogManager, enableDialogueOverlay } from "$pkg/components/dialog_manager/DialogManagerStore";
17
+ import Toast from "$pkg/components/pineapple/toast/Toast.svelte";
18
+ import DialogOverlay from "$pkg/components/DialogOverlay.svelte";
19
+ // todo: clean up all these imports!
20
+
21
+ let pages: BreadcrumbData[] = [];
22
+
23
+ const updateBreadcrumb = (pathname: string) => {
24
+ pages = [];
25
+ let basePath = "";
26
+ pathname.split("/").forEach((value, index) => {
27
+ if (index === 0) {
28
+ basePath = "/";
29
+ pages.push({
30
+ path: "/",
31
+ name: "Home"
32
+ });
33
+ return;
34
+ }
35
+
36
+ if (value === "") {
37
+ return;
38
+ }
39
+
40
+ basePath += value + "/";
41
+ pages.push({
42
+ path: basePath,
43
+ name: value
44
+ });
45
+ });
46
+ pages = pages;
47
+ };
48
+
49
+ $: updateBreadcrumb($page.url.pathname); // run every time we navigate
50
+
51
+ let enableBackgroundValue = true;
52
+ enableBackground.subscribe((value) => {
53
+ enableBackgroundValue = value;
54
+ });
55
+
56
+ let enableDialogueOverlayValue = true;
57
+ enableDialogueOverlay.subscribe((value) => {
58
+ enableDialogueOverlayValue = value;
59
+ });
60
+
61
+ enableDialogueOverlay.set(false);
62
+ </script>
63
+
64
+ <!-- App Shell -->
65
+ <svelte:head>
66
+ {@html `<script>${autoModeWatcher.toString()} autoModeWatcher();</script>`}
67
+ </svelte:head>
68
+
69
+ <!--todo: turn off hidden when it's time-->
70
+ <button type="button" class="fab" on:click={()=>{
71
+ dialogManager.toggleDialogOverlay()
72
+ }}>
73
+ {#if (enableDialogueOverlayValue)}
74
+ <img class="img-icon" src={CloseIcon} alt="interactive floating action button represented as a turnip">
75
+ {:else }
76
+ <img src={FABIcon} alt="interactive floating action button represented as a turnip">
77
+ {/if}
78
+ </button>
79
+
80
+ <AppShell>
81
+ <svelte:fragment slot="header">
82
+ <!-- App Bar -->
83
+ <AppBar
84
+ background="app-shell-token"
85
+ slotDefault="place-content-start"
86
+ slotTrail="place-content-end">
87
+ <svelte:fragment slot="lead">
88
+ <!--TODO: add logo or something for the lead in layout-->
89
+ <img
90
+ alt="Ares's head titled towards the left with his tongue out and winking"
91
+ class="ares-logo"
92
+ src={AresLogo}
93
+ />
94
+ <span class="mr-2" />
95
+ <ol class="breadcrumb">
96
+ {#each pages as crumb, i}
97
+ {#if i < pages.length - 1}
98
+ <li class="crumb">
99
+ <a href={crumb.path}>{crumb.name.charAt(0).toUpperCase() + crumb.name.slice(1)}</a>
100
+ </li>
101
+ <li class="crumb-separator" aria-hidden="true">&rsaquo;</li>
102
+ {:else}
103
+ <li class="crumb">{crumb.name.charAt(0).toUpperCase() + crumb.name.slice(1)}</li>
104
+ {/if}
105
+ {/each}
106
+ </ol>
107
+ </svelte:fragment>
108
+ <svelte:fragment slot="trail">
109
+ <LightSwitch bgLight="bg-surface-400" />
110
+ </svelte:fragment>
111
+ </AppBar>
112
+ </svelte:fragment>
113
+
114
+ <RandomizedBackground enable={enableBackgroundValue} />
115
+
116
+ <Toast></Toast>
117
+
118
+ <DialogOverlay></DialogOverlay>
119
+
120
+ <div class="default-page-container">
121
+ <slot />
122
+ <div class="footer-space" />
123
+ </div>
124
+ <!--{#if enableDialogueOverlayValue}-->
125
+ <!-- &lt;!&ndash; Page Route Content &ndash;&gt;-->
126
+ <!-- <div class="default-page-container">-->
127
+ <!-- <slot />-->
128
+ <!-- <div class="footer-space" />-->
129
+ <!-- </div>-->
130
+ <!-- <DialogOverlay />-->
131
+ <!--{:else}-->
132
+ <!-- <DialogOverlay />-->
133
+ <!-- <slot />-->
134
+ <!--{/if}-->
135
+ </AppShell>
136
+
137
+ <style lang="postcss">
138
+ :root {
139
+ --dialog-start-pad: clamp(0em, 5vw, 2em);
140
+ --dialog-box-width: min(calc(50em + 4em), calc(100vw - var(--dialog-start-pad) - var(--theme-border-base)));
141
+ --dialog-box-height: clamp(15em, 50vw, 18em);
142
+
143
+ /** FAB icon margin/position calculation origin:
144
+ Criteria:
145
+ - We want at mobile (360px) our margin to be at 1em (16px)
146
+ - We want at web (1960px) our margin to be at 2em (32px)
147
+
148
+ A useful scaling factor might vw. At 360px, 16px would be around 4.44vw (360/16).
149
+ At 360px: margin is at 16px or 1em.
150
+ At 1960px: 4.44vw is at 87px but that will be clamped to 32px or 2em.
151
+ The calculation implies that the natural point that the margin becomes 2em is clamped on
152
+ wider screens is at 727px.
153
+ */
154
+ --fab-margin: clamp(1em, 4.44vw, 2em);
155
+ }
156
+
157
+ .default-page-container {
158
+ @apply flex justify-center items-center;
159
+ margin-top: 4em;
160
+ margin-right: 1em;
161
+ flex-direction: column;
162
+ z-index: 0;
163
+ }
164
+
165
+ .ares-logo {
166
+ object-fit: contain;
167
+ height: 2em;
168
+ margin-inline-end: 0.5em;
169
+ }
170
+
171
+ /* breadcrumb does not work due to a lot of magic stuff i do
172
+ the code below is from skeleton's tailwind css:
173
+ https://github.com/skeletonlabs/skeleton/blob/54f4ecedabf2be6d94a670b56dc8821095ca3fc9/packages/plugin/src/styles/components/breadcrumbs.css
174
+
175
+ it likely disappeared due to code gen shenanigans and package magic */
176
+ .breadcrumb,
177
+ .breadcrumb-nonresponsive {
178
+ @apply flex items-center space-x-4 w-full overflow-x-auto;
179
+ /*@apply flex items-center space-x-4 w-full hide-scrollbar overflow-x-auto;*/
180
+ }
181
+
182
+ .crumb {
183
+ @apply flex justify-center items-center space-x-2;
184
+ }
185
+
186
+ .crumb-separator {
187
+ @apply flex text-surface-700-200-token opacity-50;
188
+ }
189
+
190
+ /* === Auto-Responsive === */
191
+
192
+ .breadcrumb li {
193
+ @apply hidden md:block;
194
+ }
195
+
196
+ .breadcrumb li:nth-last-child(3),
197
+ .breadcrumb li:nth-last-child(2),
198
+ .breadcrumb li:nth-last-child(1) {
199
+ @apply block;
200
+ }
201
+
202
+ .fab {
203
+ position: fixed;
204
+ bottom: var(--fab-margin);
205
+ width: 4em;
206
+ border-radius: 50%;
207
+ }
208
+
209
+ .fab > img {
210
+ width: 100%;
211
+ }
212
+
213
+ .fab:dir(ltr) {
214
+ right: var(--fab-margin);
215
+ }
216
+
217
+ .fab:dir(rtl) {
218
+ left: var(--fab-margin);
219
+ }
220
+ </style>
@@ -0,0 +1,43 @@
1
+ <script lang="ts">
2
+ import type { Props } from "$pkg/components/pineapple/toast/DefaultToastBody";
3
+ import type { DismissToastCallback } from "$pkg/components/pineapple/toast/Toast";
4
+ import CloseIcon from "$pkg/assets/icons/close.svg";
5
+
6
+ export let props: Props;
7
+ export let dismissToastCallback: DismissToastCallback | undefined;
8
+ export let shouldEnableButton = false;
9
+ </script>
10
+
11
+ <div class="body-container">
12
+ <!-- todo: support markdown? -->
13
+ <button class="btn"
14
+ disabled={!shouldEnableButton}
15
+ on:click={dismissToastCallback}>
16
+ <img class="img-icon" src={CloseIcon} alt="close button">
17
+ </button>
18
+ <div class="text-container">
19
+ <span>{props.message}</span>
20
+ </div>
21
+ </div>
22
+
23
+ <style lang="postcss">
24
+ .body-container {
25
+ display: flex;
26
+ gap: 1em;
27
+ }
28
+
29
+ .text-container {
30
+ margin-top: 0.25lh;
31
+ margin-right: 1em;
32
+ }
33
+
34
+ .text-container:dir(rtl) {
35
+ margin-left: 1em;
36
+ }
37
+
38
+ .btn {
39
+ @apply bg-surface-100 dark:bg-surface-900;
40
+ border-radius: 8px;
41
+ padding: 0.5em;
42
+ }
43
+ </style>
@@ -0,0 +1,10 @@
1
+ import DefaultToastBody from "./DefaultToastBody.svelte";
2
+
3
+ export interface Props {
4
+ message: string;
5
+ }
6
+
7
+ export interface DefaultToastPair {
8
+ component: typeof DefaultToastBody;
9
+ props: Props;
10
+ }
@@ -0,0 +1,114 @@
1
+ <script lang="ts">
2
+ import {
3
+ activeToast,
4
+ type CustomToastPairs,
5
+ DefaultToastParamsDuration,
6
+ toastQueue
7
+ } from "$pkg/components/pineapple/toast/Toast";
8
+ import { type ComponentType } from "svelte";
9
+ import Card from "$pkg/components/Card.svelte";
10
+ import { spring, tweened } from "svelte/motion";
11
+
12
+ let localComponent: ComponentType | undefined;
13
+ let localProps: CustomToastPairs["props"];
14
+
15
+ // todo: make hidden value reliant on current vh
16
+ const HIDDEN_VALUE = -15;
17
+ const SHOWN_VALUE = 0;
18
+ const progress = tweened(0, { duration: DefaultToastParamsDuration });
19
+ const position = spring(HIDDEN_VALUE);
20
+ position.damping = 0.4;
21
+ let isDismissed = false;
22
+ let shouldEnableButton = false;
23
+
24
+ const onToastDisappear = () => {
25
+ // clear the local component to undefined to make the component disappear
26
+ // also set activeToast to null to indicate to the system that it's ready to take new
27
+ // Toast requests
28
+ localComponent = undefined;
29
+ activeToast.update(() => {
30
+ if (toastQueue.length === 0) {
31
+ return undefined;
32
+ }
33
+ return toastQueue.shift();
34
+ });
35
+ };
36
+
37
+ activeToast.subscribe((params) => {
38
+ if (!params) {
39
+ return;
40
+ }
41
+
42
+ if (params.componentAndProps?.component === localComponent) {
43
+ return;
44
+ }
45
+
46
+ localComponent = params.componentAndProps.component;
47
+ localProps = params.componentAndProps.props;
48
+
49
+ // todo: make unnested
50
+ // set progress to 0 before showing
51
+ progress.set(0, { duration: 0 }).then(() => {
52
+ // animate showing the toast
53
+ position.set(SHOWN_VALUE).then(() => {
54
+ shouldEnableButton = true;
55
+
56
+ // now animate the lifespan of the current toast
57
+ progress.set(100, { delay: 500, duration: params.duration ?? DefaultToastParamsDuration }).then(() => {
58
+ // during the lifecycle the button to cause dismissal can only happen when
59
+ // position is completely set SHOWN_VALUE and when progress has reached to 100
60
+ shouldEnableButton = false;
61
+ if (isDismissed) {
62
+ return;
63
+ }
64
+
65
+ // when the lifespan duration is over, animate hiding the toast by putting its bottom
66
+ // position out of bounds
67
+ position.set(HIDDEN_VALUE).then(onToastDisappear);
68
+ });
69
+ });
70
+ });
71
+ });
72
+
73
+ const dismissToast = () => {
74
+ shouldEnableButton = false;
75
+ isDismissed = true;
76
+ position.set(HIDDEN_VALUE).then(onToastDisappear);
77
+ };
78
+ </script>
79
+
80
+ {#if (localComponent !== undefined)}
81
+ <div class="toast-positioner" style={`bottom: ${$position}lh;`}>
82
+ <!-- todo: adjust shadow to be more dynamic or transparent -->
83
+ <Card marginBottom="1lh" overrideStyle="box-shadow: 3px 3px 3px var(--shadow-color);">
84
+ <div slot="content">
85
+ {#if (localProps !== undefined)}
86
+ <svelte:component this={localComponent}
87
+ props={localProps}
88
+ dismissToastCallback={dismissToast}
89
+ shouldEnableButton={shouldEnableButton} />
90
+ {:else }
91
+ <svelte:component this={localComponent} />
92
+ {/if}
93
+ <progress id="toast-progress" value={$progress/100}></progress>
94
+ </div>
95
+ </Card>
96
+ </div>
97
+ {/if}
98
+
99
+ <style lang="postcss">
100
+ .toast-positioner {
101
+ position: fixed;
102
+ /* 12em = this component's margin (4em) + fab margin + width (8em) */
103
+ max-width: calc(100vw - 12em);
104
+ z-index: 100;
105
+ }
106
+
107
+ .toast-positioner:dir(ltr) {
108
+ left: 2em;
109
+ }
110
+
111
+ .toast-positioner:dir(rtl) {
112
+ right: 2em;
113
+ }
114
+ </style>
@@ -0,0 +1,57 @@
1
+ import { writable } from "svelte/store";
2
+ import type { DefaultToastPair } from "$pkg/components/pineapple/toast/DefaultToastBody";
3
+ import DefaultToastBody from "$pkg/components/pineapple/toast/DefaultToastBody.svelte";
4
+ import type { TestCustomToastPair } from "$pkg/components/pineapple/toast/custom-toast/TestCustomToast";
5
+
6
+ /**
7
+ * Default toast duration is 5 seconds
8
+ */
9
+ export const DefaultToastParamsDuration = 5000;
10
+
11
+ export type CustomToastPairs = DefaultToastPair
12
+ | TestCustomToastPair;
13
+
14
+ export type DismissToastCallback = () => void;
15
+
16
+ // todo: note may not be possible
17
+ export interface ToastParams {
18
+ componentAndProps: CustomToastPairs;
19
+ /**
20
+ * Time in millisecond. If undefined, defaults to DefaultToastParamsDuration
21
+ */
22
+ duration?: number;
23
+ }
24
+
25
+ /**
26
+ * todo: do not call directly documentation
27
+ */
28
+ export const activeToast = writable<ToastParams | undefined>();
29
+
30
+ export const toastQueue: ToastParams[] = [];
31
+
32
+ /**
33
+ *
34
+ * @param newToast
35
+ */
36
+ export const showComponentInToast = (newToast: ToastParams) => {
37
+ // todo: queue component
38
+ activeToast.update((currentToast) => {
39
+ if (currentToast !== undefined) {
40
+ toastQueue.push(newToast);
41
+ return currentToast;
42
+ } else {
43
+ return newToast;
44
+ }
45
+ });
46
+ };
47
+
48
+ export const showTextInToast = (message: string) => {
49
+ showComponentInToast({
50
+ componentAndProps: {
51
+ component: DefaultToastBody,
52
+ props: {
53
+ message
54
+ }
55
+ }
56
+ });
57
+ };
@@ -0,0 +1 @@
1
+ <h1>Testing bad custom card</h1>
@@ -0,0 +1,6 @@
1
+ import TestCustomToast from "./TestCustomToast.svelte";
2
+
3
+ export interface TestCustomToastPair {
4
+ component: typeof TestCustomToast;
5
+ props: undefined;
6
+ }
package/src/lib/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { default as PineappleLayoutBase } from "./components/layouts/PineappleBaseLayout.svelte";
1
+ export { default as PineappleLayoutBase } from "./components/pineapple/PineappleBaseLayout.svelte";
2
2
  export { default as SeaweedTemplate } from "./template/SeaweedTemplate.svelte";
3
3
  export { default as LazyAsset } from "./components/LazyAsset.svelte";
4
4