From 315e27b22bc690e997040ecd2ee65b6d61b8c7cc Mon Sep 17 00:00:00 2001 From: cesarParra Date: Sat, 1 Jun 2024 08:37:19 -0400 Subject: [PATCH] temp --- README.md | 99 +++++++++++++++++-- .../lwc/demoSignals/shopping-cart.js | 24 ++--- .../default/staticresources/tw/css/main.css | 2 +- .../shoppingCartDetails.js | 18 +++- .../lwc/shoppingCartDetails/states/ready.html | 53 ++++++++-- 5 files changed, 170 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index d34217e..60c3436 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A simple yet powerful reactive state management solution for Lightning Web Compo Inspired by the Signals technology behind SolidJs, Preact, Svelte 5 Runes and the Vue 3 Composition API, LWC Signals is a -reactive signals for Lightning Web Components that allows you to create reactive data signalss +reactive signals for Lightning Web Components that allows you to create reactive data signals that can be used to share state between components. It features: @@ -203,7 +203,7 @@ export default class ContactInfoForm extends LightningElement { } ``` -**You can create a computed value that depends on both signalss** +**You can create a computed value that depends on both signals** ```html @@ -221,7 +221,7 @@ export default class ContactInfoForm extends LightningElement { // businessCard.js import { LightningElement } from "lwc"; import { $computed } from "c/signals"; -import { accountName, contactName } from "c/demoSignalss"; +import { accountName, contactName } from "c/demoSignals"; export default class BusinessCard extends LightningElement { contactInfo = $computed( @@ -421,7 +421,7 @@ Let's now create our picklist component that allows the user to select an accoun // accountPicker.js import { LightningElement, track, wire } from "lwc"; import getAccounts from "@salesforce/apex/ResourceController.getAccounts"; -import { selectedAccountId } from "c/demoSignalss"; +import { selectedAccountId } from "c/demoSignals"; export default class AccountPicker extends LightningElement { @track accounts = []; @@ -486,7 +486,7 @@ Now, let's create the component that displays the details of the selected accoun // accountDetails.js import { LightningElement } from "lwc"; import { $computed } from "c/signals"; -import { getAccount } from "c/demoSignalss"; +import { getAccount } from "c/demoSignals"; export default class AccountDetails extends LightningElement { account = $computed(() => (this.account = getAccount.value)).value; @@ -550,6 +550,93 @@ export default class ContactList extends LightningElement { } ``` +### Mutating `$resource` data + +Besides `refetch`, the `$resource` function also returns a `mutate` function that allows you to mutate the data. + +`mutate` is useful when you want to update the data without refetching it (and avoid a trip to the server). + +It receives a single value, which will be set as the new value of the data. The `resource` value will be updated +immediately, the `.loading` property will be set to `false`, and the `.error` property will be set to `null`. + +```javascript +import { $resource } from "c/signals"; + +const { data, mutate } = $resource(asyncFunction); + +mutate("new value"); +``` + +#### Reacting to mutated values + +When using the `mutate` function, you might want to react to the changes in the data. For example, you might now +want to call an Apex function to save the new value to the server, and make sure the data is synced. + +For this, you can provide a function through the options object's `onMutate`. + +The function you provide can receive 3 arguments: + +- The new value +- The old value +- A `mutate` function that you can use the update the data again. This can be used for when you want to update the data + based on what was returned from the server, but you don't want to refetch the data. You SHOULD use this mutate + function over the one returned when creating the `$resource` because this will not trigger `onMutate` once again. + +```javascript +import { $resource } from "c/signals"; +import getContacts from "@salesforce/apex/ContactController.getContacts"; +import saveContacts from "@salesforce/apex/ContactController.saveContacts"; + +const { data, mutate } = $resource( + getContacts, + {}, + { + onMutate: async (newValue, oldValue, mutate) => { + await saveContacts({ contacts: newValue }); + mutate(newValue); + } + } +); +``` + +In the case an error occurs on your server call, the `mutate` you can pass an error object as the second argument to +the `mutate` function. This will set the `.error` property of the `resource` to the error object. + +```javascript +import { $resource } from "c/signals"; + +const { data, mutate } = $resource(asyncFunction); + +try { + await saveContacts({ contacts: newValue }); + mutate(newValue); +} catch (error) { + mutate(null, error); +} +``` + +#### Optimistic updating + +When you mutate a `resource` as exemplified above, you can achieve the concept of optimistic updating. This is when you +update the value immediately before the server responds, and then update the value again when the server responds. + +Optimistically updating the value can provide a better user experience by making the UI feel more responsive, but it +can also lead to inconsistencies if the server responds with an error. So if you wish to turn this off, and +manage updating the value yourself, either by `refetching` or by using an `onMutate` function, you can set the +`optimisticMutate` option to `false`. + +```javascript +import { $resource } from "c/signals"; + +const { data, refetch, mutate } = $resource( + asyncFunction, + {}, + { + optimisticMutate: false + } +); +``` + ## Storage By default, any created signal is stored in memory and will be lost when the component is destroyed. This behavior @@ -568,7 +655,7 @@ The following storage helpers are available by default: - `useCookies(key: string, expires?: Date)`: Stores the signal in a cookie with the given key. You can also pass an optional `expires` parameter to set the expiration date of the cookie -### Creating custom storage +### Creating a custom storage The `storage` option receives a function that defines the behavior for where the data should be stored. This means you can create your own custom storage solution by passing a function with the following diff --git a/examples/demo-signals/lwc/demoSignals/shopping-cart.js b/examples/demo-signals/lwc/demoSignals/shopping-cart.js index c3e32b2..31cd018 100644 --- a/examples/demo-signals/lwc/demoSignals/shopping-cart.js +++ b/examples/demo-signals/lwc/demoSignals/shopping-cart.js @@ -1,4 +1,4 @@ -import { $resource } from "c/signals"; +import { $signal, $resource } from "c/signals"; import getShoppingCart from "@salesforce/apex/ShoppingCartController.getShoppingCart"; import updateShoppingCart from "@salesforce/apex/ShoppingCartController.updateShoppingCart"; @@ -19,17 +19,19 @@ import updateShoppingCart from "@salesforce/apex/ShoppingCartController.updateSh */ // Store each state change in the cart history -const cartHistory = []; +export const cartHistory = $signal([]); let isUndoing = false; -export function undoCartChange() { +export const undoCartChange = () => { isUndoing = true; - const lastState = cartHistory.pop(); + const lastState = cartHistory.value[cartHistory.value.length - 1]; + // Remove the last state from the history + cartHistory.value = cartHistory.value.slice(0, -1); if (lastState) { updateCart(lastState); } isUndoing = false; -} +}; /** * Updates the cart on the server @@ -39,6 +41,10 @@ export function undoCartChange() { */ async function updateCartOnTheServer(newCart, previousValue, mutate) { try { + // Keep track of the isUndoing value before making any async changes + // to ensure we don't update the history when undoing, even after + // an async operation. + const shouldUpdateHistory = !isUndoing; // Update the cart on the server const updatedShoppingCart = await updateShoppingCart({ newItems: newCart.items @@ -48,18 +54,14 @@ async function updateCartOnTheServer(newCart, previousValue, mutate) { mutate(updatedShoppingCart); // Store the previous value in the history - if (!isUndoing) { - cartHistory.push(previousValue); + if (shouldUpdateHistory) { + cartHistory.value = [...cartHistory.value, previousValue]; } } catch (error) { mutate(null, error); } } -// TODO: When we document this, remember there are 2 options. We can either -// do the update the same way we are doing it here (through onMutate) or -// it can be done in the component (or anywhere really) and then `refetch` can be called. - export const { data: shoppingCart, mutate: updateCart } = $resource( getShoppingCart, {}, diff --git a/examples/main/default/staticresources/tw/css/main.css b/examples/main/default/staticresources/tw/css/main.css index 2b989f4..a4cf594 100644 --- a/examples/main/default/staticresources/tw/css/main.css +++ b/examples/main/default/staticresources/tw/css/main.css @@ -1 +1 @@ -/*! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border-width:0!important}.relative{position:relative!important}.-my-4{margin-top:-1rem!important;margin-bottom:-1rem!important}.mx-auto{margin-left:auto!important;margin-right:auto!important}.mt-10{margin-top:2.5rem!important}.mt-6{margin-top:1.5rem!important}.ml-4{margin-left:1rem!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-4{margin-top:1rem!important}.block{display:block!important}.flex{display:flex!important}.flow-root{display:flow-root!important}.h-12{height:3rem!important}.h-24{height:6rem!important}.h-5{height:1.25rem!important}.w-full{width:100%!important}.w-12{width:3rem!important}.w-24{width:6rem!important}.w-5{width:1.25rem!important}.max-w-4xl{max-width:56rem!important}.flex-1{flex:1 1 0%!important}.flex-shrink-0{flex-shrink:0!important}.flex-col{flex-direction:column!important}.items-center{align-items:center!important}.justify-center{justify-content:center!important}.justify-between{justify-content:space-between!important}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0!important;margin-right:calc(.5rem*var(--tw-space-x-reverse))!important;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))!important}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0!important;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))!important;border-bottom-width:calc(1px*var(--tw-divide-y-reverse))!important}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1!important;border-color:rgb(229 231 235/var(--tw-divide-opacity))!important}.rounded-lg{border-radius:.5rem!important}.rounded-md{border-radius:.375rem!important}.border{border-width:1px!important}.border-2{border-width:2px!important}.border-b{border-bottom-width:1px!important}.border-t{border-top-width:1px!important}.border-dashed{border-style:dashed!important}.border-transparent{border-color:#0000!important}.border-gray-200{--tw-border-opacity:1!important;border-color:rgb(229 231 235/var(--tw-border-opacity))!important}.border-gray-300{--tw-border-opacity:1!important;border-color:rgb(209 213 219/var(--tw-border-opacity))!important}.bg-gray-50{--tw-bg-opacity:1!important;background-color:rgb(249 250 251/var(--tw-bg-opacity))!important}.bg-indigo-600{--tw-bg-opacity:1!important;background-color:rgb(79 70 229/var(--tw-bg-opacity))!important}.object-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-center{-o-object-position:center!important;object-position:center!important}.p-12{padding:3rem!important}.px-4{padding-left:1rem!important;padding-right:1rem!important}.py-3{padding-top:.75rem!important;padding-bottom:.75rem!important}.py-4{padding-top:1rem!important;padding-bottom:1rem!important}.py-6{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.pb-16{padding-bottom:4rem!important}.pr-6{padding-right:1.5rem!important}.text-center{text-align:center!important}.text-right{text-align:right!important}.text-base{font-size:1rem!important;line-height:1.5rem!important}.text-sm{font-size:.875rem!important;line-height:1.25rem!important}.text-3xl{font-size:1.875rem!important;line-height:2.25rem!important}.font-medium{font-weight:500!important}.font-bold{font-weight:700!important}.font-semibold{font-weight:600!important}.tracking-tight{letter-spacing:-.025em!important}.text-gray-500{--tw-text-opacity:1!important;color:rgb(107 114 128/var(--tw-text-opacity))!important}.text-gray-600{--tw-text-opacity:1!important;color:rgb(75 85 99/var(--tw-text-opacity))!important}.text-gray-900{--tw-text-opacity:1!important;color:rgb(17 24 39/var(--tw-text-opacity))!important}.text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.text-white{color:rgb(255 255 255/var(--tw-text-opacity))!important}.text-gray-400,.text-white{--tw-text-opacity:1!important}.text-gray-400{color:rgb(156 163 175/var(--tw-text-opacity))!important}.text-gray-700{--tw-text-opacity:1!important;color:rgb(55 65 81/var(--tw-text-opacity))!important}.text-green-500{--tw-text-opacity:1!important;color:rgb(34 197 94/var(--tw-text-opacity))!important}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d!important;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)!important;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.hover\:border-gray-400:hover{--tw-border-opacity:1!important;border-color:rgb(156 163 175/var(--tw-border-opacity))!important}.hover\:bg-indigo-700:hover{--tw-bg-opacity:1!important;background-color:rgb(67 56 202/var(--tw-bg-opacity))!important}.hover\:text-indigo-500:hover{--tw-text-opacity:1!important;color:rgb(99 102 241/var(--tw-text-opacity))!important}.hover\:text-gray-800:hover{--tw-text-opacity:1!important;color:rgb(31 41 55/var(--tw-text-opacity))!important}.focus\:outline-none:focus{outline:2px solid #0000!important;outline-offset:2px!important}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)!important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1!important;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))!important}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px!important}.focus\:ring-offset-gray-50:focus{--tw-ring-offset-color:#f9fafb!important}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed!important}.disabled\:opacity-50:disabled{opacity:.5!important}@media (min-width:640px){.sm\:absolute{position:absolute!important}.sm\:left-1\/2{left:50%!important}.sm\:top-0{top:0!important}.sm\:ml-0{margin-left:0!important}.sm\:ml-6{margin-left:1.5rem!important}.sm\:mt-0{margin-top:0!important}.sm\:mt-3{margin-top:.75rem!important}.sm\:block{display:block!important}.sm\:grid{display:grid!important}.sm\:h-32{height:8rem!important}.sm\:w-32{width:8rem!important}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.sm\:p-6{padding:1.5rem!important}.sm\:py-10{padding-top:2.5rem!important;padding-bottom:2.5rem!important}}@media (min-width:1024px){.lg\:p-8{padding:2rem!important}} \ No newline at end of file +/*! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border-width:0!important}.pointer-events-none{pointer-events:none!important}.pointer-events-auto{pointer-events:auto!important}.fixed{position:fixed!important}.relative{position:relative!important}.inset-0{inset:0!important}.-my-4{margin-top:-1rem!important;margin-bottom:-1rem!important}.mx-auto{margin-left:auto!important;margin-right:auto!important}.ml-3{margin-left:.75rem!important}.ml-4{margin-left:1rem!important}.mt-1{margin-top:.25rem!important}.mt-10{margin-top:2.5rem!important}.mt-2{margin-top:.5rem!important}.mt-4{margin-top:1rem!important}.mt-6{margin-top:1.5rem!important}.block{display:block!important}.flex{display:flex!important}.inline-flex{display:inline-flex!important}.flow-root{display:flow-root!important}.h-12{height:3rem!important}.h-24{height:6rem!important}.h-5{height:1.25rem!important}.w-0{width:0!important}.w-12{width:3rem!important}.w-24{width:6rem!important}.w-5{width:1.25rem!important}.w-full{width:100%!important}.max-w-4xl{max-width:56rem!important}.max-w-sm{max-width:24rem!important}.flex-1{flex:1 1 0%!important}.flex-shrink-0{flex-shrink:0!important}.translate-y-0{--tw-translate-y:0px!important}.translate-y-0,.translate-y-2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.translate-y-2{--tw-translate-y:0.5rem!important}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.flex-col{flex-direction:column!important}.items-end{align-items:flex-end!important}.items-center{align-items:center!important}.justify-center{justify-content:center!important}.justify-between{justify-content:space-between!important}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0!important;margin-right:calc(.5rem*var(--tw-space-x-reverse))!important;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))!important}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0!important;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))!important;margin-bottom:calc(1rem*var(--tw-space-y-reverse))!important}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0!important;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))!important;border-bottom-width:calc(1px*var(--tw-divide-y-reverse))!important}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1!important;border-color:rgb(229 231 235/var(--tw-divide-opacity))!important}.overflow-hidden{overflow:hidden!important}.rounded-lg{border-radius:.5rem!important}.rounded-md{border-radius:.375rem!important}.border{border-width:1px!important}.border-2{border-width:2px!important}.border-b{border-bottom-width:1px!important}.border-t{border-top-width:1px!important}.border-dashed{border-style:dashed!important}.border-gray-200{--tw-border-opacity:1!important;border-color:rgb(229 231 235/var(--tw-border-opacity))!important}.border-gray-300{--tw-border-opacity:1!important;border-color:rgb(209 213 219/var(--tw-border-opacity))!important}.border-transparent{border-color:#0000!important}.bg-gray-50{--tw-bg-opacity:1!important;background-color:rgb(249 250 251/var(--tw-bg-opacity))!important}.bg-indigo-600{background-color:rgb(79 70 229/var(--tw-bg-opacity))!important}.bg-indigo-600,.bg-white{--tw-bg-opacity:1!important}.bg-white{background-color:rgb(255 255 255/var(--tw-bg-opacity))!important}.object-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-center{-o-object-position:center!important;object-position:center!important}.p-12{padding:3rem!important}.p-4{padding:1rem!important}.px-4{padding-left:1rem!important;padding-right:1rem!important}.py-3{padding-top:.75rem!important;padding-bottom:.75rem!important}.py-4{padding-top:1rem!important;padding-bottom:1rem!important}.py-6{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.pb-16{padding-bottom:4rem!important}.pr-6{padding-right:1.5rem!important}.text-center{text-align:center!important}.text-right{text-align:right!important}.text-3xl{font-size:1.875rem!important;line-height:2.25rem!important}.text-base{font-size:1rem!important;line-height:1.5rem!important}.text-sm{font-size:.875rem!important;line-height:1.25rem!important}.font-bold{font-weight:700!important}.font-medium{font-weight:500!important}.font-semibold{font-weight:600!important}.tracking-tight{letter-spacing:-.025em!important}.text-gray-400{--tw-text-opacity:1!important;color:rgb(156 163 175/var(--tw-text-opacity))!important}.text-gray-500{--tw-text-opacity:1!important;color:rgb(107 114 128/var(--tw-text-opacity))!important}.text-gray-600{--tw-text-opacity:1!important;color:rgb(75 85 99/var(--tw-text-opacity))!important}.text-gray-700{--tw-text-opacity:1!important;color:rgb(55 65 81/var(--tw-text-opacity))!important}.text-gray-900{--tw-text-opacity:1!important;color:rgb(17 24 39/var(--tw-text-opacity))!important}.text-green-500{--tw-text-opacity:1!important;color:rgb(34 197 94/var(--tw-text-opacity))!important}.text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.text-white{--tw-text-opacity:1!important;color:rgb(255 255 255/var(--tw-text-opacity))!important}.opacity-0{opacity:0!important}.opacity-100{opacity:1!important}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a!important;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)!important}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)!important}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d!important;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)!important}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)!important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important}.ring-black{--tw-ring-opacity:1!important;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))!important}.ring-opacity-5{--tw-ring-opacity:0.05!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;transition-duration:.15s!important}.duration-100{transition-duration:.1s!important}.duration-300{transition-duration:.3s!important}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)!important}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)!important}.hover\:border-gray-400:hover{--tw-border-opacity:1!important;border-color:rgb(156 163 175/var(--tw-border-opacity))!important}.hover\:bg-indigo-700:hover{--tw-bg-opacity:1!important;background-color:rgb(67 56 202/var(--tw-bg-opacity))!important}.hover\:text-gray-500:hover{--tw-text-opacity:1!important;color:rgb(107 114 128/var(--tw-text-opacity))!important}.hover\:text-gray-800:hover{--tw-text-opacity:1!important;color:rgb(31 41 55/var(--tw-text-opacity))!important}.hover\:text-indigo-500:hover{--tw-text-opacity:1!important;color:rgb(99 102 241/var(--tw-text-opacity))!important}.focus\:outline-none:focus{outline:2px solid #0000!important;outline-offset:2px!important}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)!important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1!important;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))!important}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px!important}.focus\:ring-offset-gray-50:focus{--tw-ring-offset-color:#f9fafb!important}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed!important}.disabled\:opacity-50:disabled{opacity:.5!important}@media (min-width:640px){.sm\:absolute{position:absolute!important}.sm\:left-1\/2{left:50%!important}.sm\:top-0{top:0!important}.sm\:ml-0{margin-left:0!important}.sm\:ml-6{margin-left:1.5rem!important}.sm\:mt-0{margin-top:0!important}.sm\:mt-3{margin-top:.75rem!important}.sm\:block{display:block!important}.sm\:grid{display:grid!important}.sm\:h-32{height:8rem!important}.sm\:w-32{width:8rem!important}.sm\:translate-x-0{--tw-translate-x:0px!important}.sm\:translate-x-0,.sm\:translate-x-2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.sm\:translate-x-2{--tw-translate-x:0.5rem!important}.sm\:translate-y-0{--tw-translate-y:0px!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.sm\:items-start{align-items:flex-start!important}.sm\:items-end{align-items:flex-end!important}.sm\:p-6{padding:1.5rem!important}.sm\:py-10{padding-top:2.5rem!important;padding-bottom:2.5rem!important}}@media (min-width:1024px){.lg\:p-8{padding:2rem!important}} \ No newline at end of file diff --git a/examples/shopping-cart/lwc/shoppingCartDetails/shoppingCartDetails.js b/examples/shopping-cart/lwc/shoppingCartDetails/shoppingCartDetails.js index ae03e42..adbd133 100644 --- a/examples/shopping-cart/lwc/shoppingCartDetails/shoppingCartDetails.js +++ b/examples/shopping-cart/lwc/shoppingCartDetails/shoppingCartDetails.js @@ -1,6 +1,11 @@ import TwElement from "c/twElement"; import { $computed } from "c/signals"; -import { shoppingCart, updateCart, undoCartChange } from "c/demoSignals"; +import { + shoppingCart, + updateCart, + cartHistory, + undoCartChange +} from "c/demoSignals"; // States import ready from "./states/ready.html"; @@ -9,6 +14,13 @@ import empty from "./states/empty.html"; export default class ShoppingCartDetails extends TwElement { itemData = $computed(() => (this.itemData = shoppingCart.value)).value; + cartHistoryLength = $computed( + () => (this.cartHistoryLength = cartHistory.value.length) + ).value; + + connectedCallback() { + super.connectedCallback(); + } render() { return this.itemData.loading @@ -56,6 +68,10 @@ export default class ShoppingCartDetails extends TwElement { updateCart({ items: newItems }); } + get displayUndoPanel() { + return this.cartHistoryLength > 0; + } + undo() { undoCartChange(); } diff --git a/examples/shopping-cart/lwc/shoppingCartDetails/states/ready.html b/examples/shopping-cart/lwc/shoppingCartDetails/states/ready.html index 33ab927..98e77ff 100644 --- a/examples/shopping-cart/lwc/shoppingCartDetails/states/ready.html +++ b/examples/shopping-cart/lwc/shoppingCartDetails/states/ready.html @@ -5,13 +5,6 @@

Items in your shopping cart

-
+
+
+
+
+
+
+

+ Changes have been made to the cart +

+ +
+
+ +
+
+
+
+
+