@pure-ds/storybook 0.4.24 → 0.4.26

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-01-16T09:55:44.354Z",
2
+ "generatedAt": "2026-01-16T15:44:04.960Z",
3
3
  "sources": {
4
4
  "customElements": "custom-elements.json",
5
5
  "ontology": "src\\js\\pds-core\\pds-ontology.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pure-ds/storybook",
3
- "version": "0.4.24",
3
+ "version": "0.4.26",
4
4
  "description": "Storybook showcase for Pure Design System with live configuration",
5
5
  "type": "module",
6
6
  "private": false,
@@ -24,20 +24,20 @@
24
24
  ],
25
25
  "scripts": {
26
26
  "generate-stories": "node scripts/generate-stories.js",
27
- "build-reference": "node scripts/build-pds-reference.mjs",
27
+ "build-reference": "node --no-warnings=ExperimentalWarning scripts/build-pds-reference.mjs",
28
28
  "package-build": "node scripts/package-build.js",
29
29
  "package-clean": "node -e \"const fs=require('fs'); fs.rmSync('src',{recursive:true,force:true}); fs.rmSync('public/assets',{recursive:true,force:true}); console.log('Cleaned package artifacts')\"",
30
30
  "prepack": "npm run package-build",
31
31
  "postpack": "npm run package-clean",
32
32
  "storybook": "npm run build-reference && storybook dev -p 6006",
33
- "build-storybook": "npm run build-reference && storybook build --output-dir storybook-static",
33
+ "build-storybook": "npm run build-reference && storybook build --output-dir storybook-static --quiet",
34
34
  "storybook:dev": "npm run storybook",
35
35
  "storybook:build": "npm run build-storybook",
36
36
  "pds:export": "pds-export",
37
37
  "pds:build-icons": "pds-build-icons"
38
38
  },
39
39
  "peerDependencies": {
40
- "@pure-ds/core": "^0.4.24"
40
+ "@pure-ds/core": "^0.4.26"
41
41
  },
42
42
  "dependencies": {
43
43
  "@custom-elements-manifest/analyzer": "^0.11.0",
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Toast notification utilities for PDS
3
+ * Ensures pds-toaster component is properly initialized before use
4
+ */
5
+
6
+ /**
7
+ * Ensures pds-toaster exists in DOM and is fully loaded
8
+ * @returns {Promise<HTMLElement>} The pds-toaster element
9
+ * @private
10
+ */
11
+ async function ensureToaster() {
12
+ let toaster = document.querySelector('pds-toaster');
13
+ if (!toaster) {
14
+ toaster = document.createElement('pds-toaster');
15
+ document.body.appendChild(toaster);
16
+ await customElements.whenDefined('pds-toaster');
17
+ }
18
+ return toaster;
19
+ }
20
+
21
+ /**
22
+ * Display a toast notification
23
+ *
24
+ * This method automatically ensures the pds-toaster component exists and is loaded
25
+ * before displaying the notification. The toaster element is appended to document.body
26
+ * if not already present.
27
+ *
28
+ * @param {string} message - The message to display
29
+ * @param {Object} [options={}] - Toast configuration
30
+ * @param {"information"|"success"|"warning"|"error"} [options.type="information"] - Toast type/severity
31
+ * @param {number} [options.duration] - Duration in milliseconds (auto-calculated if not provided based on message length)
32
+ * @param {boolean} [options.closable=true] - Whether the toast can be manually closed
33
+ * @param {boolean} [options.persistent=false] - If true, toast won't auto-dismiss (requires manual close)
34
+ * @returns {Promise<string>} Toast ID (can be used to dismiss programmatically)
35
+ *
36
+ * @example
37
+ * // Simple success toast
38
+ * await PDS.toast('Changes saved successfully!', { type: 'success' });
39
+ *
40
+ * @example
41
+ * // Error with custom duration
42
+ * await PDS.toast('Failed to save changes', {
43
+ * type: 'error',
44
+ * duration: 8000
45
+ * });
46
+ *
47
+ * @example
48
+ * // Persistent warning (must be manually closed)
49
+ * await PDS.toast('This action cannot be undone', {
50
+ * type: 'warning',
51
+ * persistent: true
52
+ * });
53
+ *
54
+ * @example
55
+ * // Get toast ID to dismiss later
56
+ * const toastId = await PDS.toast('Processing...', { persistent: true });
57
+ * // ... later
58
+ * const toaster = document.querySelector('pds-toaster');
59
+ * toaster.dismissToast(toastId);
60
+ */
61
+ export async function toast(message, options = {}) {
62
+ const toaster = await ensureToaster();
63
+ return toaster.toast(message, options);
64
+ }
65
+
66
+ /**
67
+ * Display a success toast (convenience method)
68
+ *
69
+ * @param {string} message - The success message
70
+ * @param {Object} [options={}] - Additional toast options (type is preset to 'success')
71
+ * @returns {Promise<string>} Toast ID
72
+ *
73
+ * @example
74
+ * await PDS.toast.success('Profile updated!');
75
+ */
76
+ toast.success = async function(message, options = {}) {
77
+ return toast(message, { ...options, type: 'success' });
78
+ };
79
+
80
+ /**
81
+ * Display an error toast (convenience method)
82
+ *
83
+ * @param {string} message - The error message
84
+ * @param {Object} [options={}] - Additional toast options (type is preset to 'error')
85
+ * @returns {Promise<string>} Toast ID
86
+ *
87
+ * @example
88
+ * await PDS.toast.error('Failed to connect to server');
89
+ */
90
+ toast.error = async function(message, options = {}) {
91
+ return toast(message, { ...options, type: 'error' });
92
+ };
93
+
94
+ /**
95
+ * Display a warning toast (convenience method)
96
+ *
97
+ * @param {string} message - The warning message
98
+ * @param {Object} [options={}] - Additional toast options (type is preset to 'warning')
99
+ * @returns {Promise<string>} Toast ID
100
+ *
101
+ * @example
102
+ * await PDS.toast.warning('Session will expire in 5 minutes');
103
+ */
104
+ toast.warning = async function(message, options = {}) {
105
+ return toast(message, { ...options, type: 'warning' });
106
+ };
107
+
108
+ /**
109
+ * Display an information toast (convenience method)
110
+ *
111
+ * @param {string} message - The information message
112
+ * @param {Object} [options={}] - Additional toast options (type is preset to 'information')
113
+ * @returns {Promise<string>} Toast ID
114
+ *
115
+ * @example
116
+ * await PDS.toast.info('New features available!');
117
+ */
118
+ toast.info = async function(message, options = {}) {
119
+ return toast(message, { ...options, type: 'information' });
120
+ };
@@ -9,20 +9,6 @@ import { AutoComplete } from "pure-web/ac";
9
9
  import { figmafyTokens } from "./figma-export.js";
10
10
  const STORAGE_KEY = "pure-ds-config";
11
11
 
12
-
13
-
14
- async function toast(message, options = {}) {
15
- let toaster = document.querySelector("#global-toaster");
16
- if (!toaster) {
17
- toaster = document.createElement("pds-toaster");
18
- toaster.id = "global-toaster";
19
- document.body.appendChild(toaster);
20
- await customElements.whenDefined("pds-toaster");
21
- }
22
-
23
- return toaster.toast(message, options);
24
- }
25
-
26
12
  customElements.define(
27
13
  "pds-config-form",
28
14
  class extends LitElement {
@@ -325,7 +311,7 @@ customElements.define(
325
311
  .slice(0, 3)
326
312
  .map((i) => `• ${i.message}`)
327
313
  .join("\n");
328
- this._validationToastId = toast(
314
+ this._validationToastId = await PDS.toast(
329
315
  `Design has accessibility issues. Fix before applying.\n${summary}`,
330
316
  { type: "error", persistent: true }
331
317
  );
@@ -408,11 +394,11 @@ customElements.define(
408
394
  }
409
395
  }
410
396
 
411
- toggleInspectorMode() {
397
+ async toggleInspectorMode() {
412
398
  this.inspectorMode = !this.inspectorMode;
413
399
  this.dispatchInspectorModeChange();
414
400
 
415
- toast(
401
+ await PDS.toast(
416
402
  this.inspectorMode
417
403
  ? "Code Inspector active - click any element in the showcase to view its code"
418
404
  : "Code Inspector deactivated",
@@ -485,7 +471,7 @@ customElements.define(
485
471
  return filtered;
486
472
  }
487
473
 
488
- handleFormChange = (event) => {
474
+ handleFormChange = async (event) => {
489
475
  // Get values from the pds-form's serialize method or from event detail
490
476
  let values;
491
477
  let changedField = null;
@@ -564,7 +550,7 @@ customElements.define(
564
550
  .slice(0, 3)
565
551
  .map((i) => `• ${i.message}`)
566
552
  .join("\n");
567
- this._validationToastId = toast(
553
+ this._validationToastId = await PDS.toast(
568
554
  `Design has accessibility issues. Fix before saving.\n${summary}`,
569
555
  { type: "error" }
570
556
  );
@@ -635,7 +621,7 @@ customElements.define(
635
621
  this.saveConfig();
636
622
  this.applyStyles(true);
637
623
 
638
- toast("Configuration reset to defaults", {
624
+ await PDS.toast("Configuration reset to defaults", {
639
625
  type: "info",
640
626
  duration: 2000,
641
627
  });
@@ -658,7 +644,7 @@ customElements.define(
658
644
  .slice(0, 3)
659
645
  .map((i) => `• ${i.message}`)
660
646
  .join("\n");
661
- this._validationToastId = toast(
647
+ this._validationToastId = await PDS.toast(
662
648
  `Preset "${preset.name}" has accessibility issues — not applied.\n${summary}`,
663
649
  { type: "error", persistent: true }
664
650
  );
@@ -685,14 +671,14 @@ customElements.define(
685
671
  this.saveConfig();
686
672
  this.applyStyles(true);
687
673
 
688
- toast(`"${preset.name}" preset loaded successfully!`, {
674
+ await PDS.toast(`"${preset.name}" preset loaded successfully!`, {
689
675
  type: "success",
690
676
  duration: 3000,
691
677
  });
692
678
  }
693
679
  };
694
680
 
695
- handleThemeChange(e) {
681
+ async handleThemeChange(e) {
696
682
  try {
697
683
  const value = e.target.value;
698
684
  // Update centralized theme via PDS (this persists + applies + sets up listeners)
@@ -701,7 +687,7 @@ customElements.define(
701
687
  // Apply immediately and emit styles using the user config (keep config separate)
702
688
  this.applyStyles(true);
703
689
 
704
- toast(`Theme set to ${value}`, { type: "info", duration: 1200 });
690
+ await PDS.toast(`Theme set to ${value}`, { type: "info", duration: 1200 });
705
691
  } catch (ex) {
706
692
  console.warn("Failed to change theme:", ex);
707
693
  }
@@ -750,9 +736,9 @@ export const pdsConfig = ${JSON.stringify(this.config, null, 2)};
750
736
  if (!this.schema) {
751
737
  if (!this._loadingToastShown) {
752
738
  this._loadingToastShown = true;
753
- setTimeout(() => {
739
+ setTimeout(async () => {
754
740
  try {
755
- toast("Loading schema...", { duration: 1000 });
741
+ await PDS.toast("Loading schema...", { duration: 1000 });
756
742
  } catch {}
757
743
  }, 250);
758
744
  }
@@ -3,11 +3,6 @@ import { PDS } from "../../../src/js/pds.js";
3
3
 
4
4
  import { AutoComplete } from "pure-web/ac";
5
5
 
6
- const toast = (message, options) => {
7
- const toaster = document.getElementById("global-toaster");
8
- toaster.toast(message, options);
9
- };
10
-
11
6
  customElements.define(
12
7
  "pds-demo",
13
8
  class extends LitElement {
@@ -3414,12 +3409,12 @@ customElements.define(
3414
3409
  } catch (err) {
3415
3410
  console.error("Error fetching README:", err);
3416
3411
  const toaster = document.getElementById("global-toaster");
3417
- toaster.toast("Error loading docs. See console.", { type: "danger" });
3412
+ await PDS.toast("Error loading docs. See console.", { type: "error" });
3418
3413
  }
3419
3414
  }
3420
3415
 
3421
3416
  handleTabChange(event) {
3422
- toast(`Switched to tab: ${event.detail.newTab}`, { type: "info" });
3417
+ await PDS.toast(`Switched to tab: ${event.detail.newTab}`, { type: "info" });
3423
3418
  }
3424
3419
 
3425
3420
  openDrawer() {
@@ -3502,42 +3497,42 @@ customElements.define(
3502
3497
  }
3503
3498
 
3504
3499
  // Toast handler methods
3505
- showSuccessToast() {
3506
- toast("Your changes have been saved successfully!", {
3500
+ async showSuccessToast() {
3501
+ await PDS.toast("Your changes have been saved successfully!", {
3507
3502
  type: "success",
3508
3503
  });
3509
3504
  }
3510
3505
 
3511
- showInfoToast() {
3512
- toast("This is an informational message with helpful context.", {
3513
- type: "info",
3506
+ async showInfoToast() {
3507
+ await PDS.toast("This is an informational message with helpful context.", {
3508
+ type: "information",
3514
3509
  });
3515
3510
  }
3516
3511
 
3517
- showWarningToast() {
3518
- toast("Warning: This action cannot be undone!", {
3512
+ async showWarningToast() {
3513
+ await PDS.toast("Warning: This action cannot be undone!", {
3519
3514
  type: "warning",
3520
3515
  });
3521
3516
  }
3522
3517
 
3523
- showErrorToast() {
3524
- toast("Error: Something went wrong. Please try again.", {
3518
+ async showErrorToast() {
3519
+ await PDS.toast("Error: Something went wrong. Please try again.", {
3525
3520
  type: "error",
3526
3521
  });
3527
3522
  }
3528
3523
 
3529
- showLongToast() {
3530
- toast(
3524
+ async showLongToast() {
3525
+ await PDS.toast(
3531
3526
  "This is a longer toast notification message that demonstrates how the duration is automatically calculated based on the message length. The toast will stay visible longer to give you enough time to read the entire message.",
3532
- { type: "info" }
3527
+ { type: "information" }
3533
3528
  );
3534
3529
  }
3535
3530
 
3536
- showPersistentToast() {
3537
- toast(
3531
+ async showPersistentToast() {
3532
+ await PDS.toast(
3538
3533
  "This is a persistent toast that won't auto-dismiss. Click the × to close it.",
3539
3534
  {
3540
- type: "info",
3535
+ type: "information",
3541
3536
  persistent: true,
3542
3537
  }
3543
3538
  );
package/src/js/pds.d.ts CHANGED
@@ -154,6 +154,68 @@ export class PDS extends EventTarget {
154
154
  static validateDesign: (designConfig: any, options?: { minContrast?: number }) => { ok: boolean; issues: Array<{ path: string; message: string; ratio: number; min: number; context?: string }> };
155
155
  static validateDesigns: (designs: Array<any> | Record<string, any>, options?: { minContrast?: number }) => { ok: boolean; results: Array<{ name?: string; ok: boolean; issues: Array<{ path: string; message: string; ratio: number; min: number; context?: string }> }> };
156
156
 
157
+ /**
158
+ * Display a toast notification.
159
+ *
160
+ * Automatically ensures the pds-toaster component exists and is loaded before displaying.
161
+ *
162
+ * @param message - The message to display
163
+ * @param options - Toast configuration
164
+ * @param options.type - Toast type/severity ("information" | "success" | "warning" | "error")
165
+ * @param options.duration - Duration in milliseconds (auto-calculated if not provided)
166
+ * @param options.closable - Whether toast can be manually closed (default: true)
167
+ * @param options.persistent - If true, toast won't auto-dismiss (default: false)
168
+ * @returns Toast ID (can be used to dismiss programmatically)
169
+ *
170
+ * @example
171
+ * await PDS.toast('Changes saved!', { type: 'success' });
172
+ *
173
+ * @example
174
+ * await PDS.toast('Error occurred', { type: 'error', persistent: true });
175
+ */
176
+ static toast(
177
+ message: string,
178
+ options?: {
179
+ type?: 'information' | 'success' | 'warning' | 'error';
180
+ duration?: number;
181
+ closable?: boolean;
182
+ persistent?: boolean;
183
+ }
184
+ ): Promise<string>;
185
+
186
+ /**
187
+ * Display a success toast (convenience method).
188
+ *
189
+ * @param message - The success message
190
+ * @param options - Additional toast options (type is preset to 'success')
191
+ * @returns Toast ID
192
+ *
193
+ * @example
194
+ * await PDS.toast.success('Profile updated!');
195
+ */
196
+ static toast: {
197
+ (message: string, options?: { type?: 'information' | 'success' | 'warning' | 'error'; duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
198
+ success(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
199
+ error(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
200
+ warning(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
201
+ info(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
202
+ };
203
+
204
+ /**
205
+ * Display a modal dialog for user input or confirmation.
206
+ *
207
+ * @param message - The message or prompt to display
208
+ * @param options - Dialog configuration
209
+ * @returns User's response (string for prompt, boolean for confirm, true for alert)
210
+ *
211
+ * @example
212
+ * const confirmed = await PDS.ask('Delete this item?', { type: 'confirm' });
213
+ *
214
+ * @example
215
+ * const name = await PDS.ask('Enter your name:', { type: 'prompt' });
216
+ */
217
+ static ask(message: string, options?: any): Promise<any>;
218
+
157
219
  /**
158
220
  * Current configuration after PDS.start() completes - read-only, frozen after initialization.
159
221
  * Contains the complete configuration used to initialize PDS, including mode, design, preset, and theme.
package/src/js/pds.js CHANGED
@@ -56,6 +56,7 @@ import { findComponentForElement } from "./pds-core/pds-ontology.js";
56
56
  import { presets, defaultLog } from "./pds-core/pds-config.js";
57
57
  import { enums } from "./pds-core/pds-enums.js";
58
58
  import { ask } from "./common/ask.js";
59
+ import { toast } from "./common/toast.js";
59
60
  import { PDSQuery } from "./pds-core/pds-query.js";
60
61
  import * as common from "./common/common.js";
61
62
  import { defaultPDSEnhancers } from "./pds-core/pds-enhancers.js";
@@ -87,6 +88,7 @@ PDS.isLiveMode = () => registry.isLive;
87
88
  PDS.enums = enums;
88
89
 
89
90
  PDS.ask = ask;
91
+ PDS.toast = toast;
90
92
 
91
93
  // Expose common utilities (deepMerge, isObject, etc.)
92
94
  PDS.common = common;
@@ -691,15 +691,33 @@ const simpleSchema = {
691
691
  type: "string",
692
692
  format: "date",
693
693
  title: "Date of Birth",
694
+ examples: ["1990-01-15"],
694
695
  },
695
696
  newsletter: {
696
697
  type: "boolean",
697
698
  title: "Subscribe to newsletter",
699
+ default: false,
698
700
  },
699
701
  },
700
702
  required: ["name", "email"],
701
703
  };
702
704
 
705
+ // UI Schema with icons for better UX
706
+ const simpleUiSchema = {
707
+ "/name": {
708
+ "ui:icon": "user",
709
+ "ui:iconPosition": "start",
710
+ },
711
+ "/email": {
712
+ "ui:icon": "envelope",
713
+ "ui:iconPosition": "start",
714
+ },
715
+ "/dateOfBirth": {
716
+ "ui:icon": "calendar",
717
+ "ui:iconPosition": "start",
718
+ },
719
+ };
720
+
703
721
  const complexSchema = {
704
722
  type: "object",
705
723
  properties: {
@@ -744,17 +762,52 @@ const complexSchema = {
744
762
 
745
763
  export const SimpleForm = {
746
764
  render: () => {
765
+ const handleSubmit = async (e) => {
766
+ // Add btn-working class to show loading state
767
+ const submitBtn = e.target.querySelector('button[type="submit"]');
768
+ submitBtn?.classList.add('btn-working');
769
+
770
+ try {
771
+ // Simulate async submission (2 second delay)
772
+ await new Promise(resolve => setTimeout(resolve, 2000));
773
+ await toastFormData(e.detail);
774
+ } finally {
775
+ submitBtn?.classList.remove('btn-working');
776
+ }
777
+ };
778
+
747
779
  return html`
748
780
  <pds-form
749
781
  data-required
750
782
  .jsonSchema=${simpleSchema}
751
- @pw:submit=${(e) => toastFormData(e.detail)}
783
+ .uiSchema=${simpleUiSchema}
784
+ @pw:submit=${handleSubmit}
752
785
  ></pds-form>
753
786
  `;
754
787
  },
755
788
  };
756
789
 
757
790
  const complexUiSchema = {
791
+ "/personalInfo/firstName": {
792
+ "ui:icon": "user",
793
+ "ui:iconPosition": "start",
794
+ },
795
+ "/personalInfo/lastName": {
796
+ "ui:icon": "user",
797
+ "ui:iconPosition": "start",
798
+ },
799
+ "/personalInfo/dateOfBirth": {
800
+ "ui:icon": "calendar",
801
+ "ui:iconPosition": "start",
802
+ },
803
+ "/address/street": {
804
+ "ui:icon": "map-pin",
805
+ "ui:iconPosition": "start",
806
+ },
807
+ "/address/city": {
808
+ "ui:icon": "building",
809
+ "ui:iconPosition": "start",
810
+ },
758
811
  "address/country": {
759
812
  "ui:class": "buttons"
760
813
  },
@@ -765,11 +818,23 @@ const complexUiSchema = {
765
818
 
766
819
  export const ComplexForm = {
767
820
  render: () => {
821
+ const handleSubmit = async (e) => {
822
+ const submitBtn = e.target.querySelector('button[type="submit"]');
823
+ submitBtn?.classList.add('btn-working');
824
+
825
+ try {
826
+ await new Promise(resolve => setTimeout(resolve, 2000));
827
+ await toastFormData(e.detail);
828
+ } finally {
829
+ submitBtn?.classList.remove('btn-working');
830
+ }
831
+ };
832
+
768
833
  return html`
769
834
  <pds-form
770
835
  .jsonSchema=${complexSchema}
771
836
  .uiSchema=${complexUiSchema}
772
- @pw:submit=${(e) => toastFormData(e.detail)}
837
+ @pw:submit=${handleSubmit}
773
838
  ></pds-form>
774
839
  `;
775
840
  },
@@ -790,13 +855,26 @@ export const WithInitialData = {
790
855
  newsletter: true,
791
856
  };
792
857
 
858
+ const handleSubmit = async (e) => {
859
+ const submitBtn = e.target.querySelector('button[type="submit"]');
860
+ submitBtn?.classList.add('btn-working');
861
+
862
+ try {
863
+ await new Promise(resolve => setTimeout(resolve, 2000));
864
+ await toastFormData(e.detail);
865
+ } finally {
866
+ submitBtn?.classList.remove('btn-working');
867
+ }
868
+ };
869
+
793
870
  return html`
794
871
  <pds-form
795
872
  .jsonSchema=${simpleSchema}
873
+ .uiSchema=${simpleUiSchema}
796
874
  .values=${initialValues}
797
875
  .options=${options}
798
876
  @pw:value-change=${(e) => console.log("?? Value changed:", e.detail)}
799
- @pw:submit=${(e) => toastFormData(e.detail)}
877
+ @pw:submit=${handleSubmit}
800
878
  ></pds-form>
801
879
  `;
802
880
  },
@@ -123,17 +123,8 @@ export async function toastFormData(data) {
123
123
  const truncated = truncateData(obj);
124
124
  const formatted = JSON.stringify(truncated, null, 2);
125
125
 
126
- // Ensure pds-toaster exists and show the toast
127
- let toaster = document.querySelector('pds-toaster');
128
- if (!toaster) {
129
- toaster = document.createElement('pds-toaster');
130
- document.body.appendChild(toaster);
131
- }
132
-
133
- // Wait for the custom element to be defined before calling methods
134
- await customElements.whenDefined('pds-toaster');
135
-
136
- toaster.toast(formatted, {
126
+ // Use PDS.toast() to display the formatted data
127
+ await PDS.toast(formatted, {
137
128
  type: 'success',
138
129
  duration: 5000
139
130
  });