ember-safe-button 3.2.1 → 4.0.0

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.
package/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2019
3
+ Copyright (c) 2025
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
6
 
package/README.md CHANGED
@@ -1,17 +1,18 @@
1
- ![ember-safe-button logo](/docs/public/logo.png)
1
+ ![ember-safe-button logo](./public/logo.png)
2
2
 
3
3
  # ember-safe-button
4
4
 
5
- Provides a button for potentially dangerous actions that better needs a confirmation. First click unlocks the fuse, second then confirms. If no confirmation is provided, then the button returns the safety in place after a timeout.
5
+ Provides a button for potentially dangerous actions that need confirmation. The
6
+ first click unlocks the safety, the second confirms. If no confirmation is
7
+ provided, the safety rolls back after a timeout.
6
8
 
9
+ - Demo page: https://ember-safe-button.netlify.app
7
10
 
8
- Compatibility
9
- ------------------------------------------------------------------------------
10
-
11
- * Ember.js v3.20 or above
12
- * Ember CLI v3.20 or above
13
- * Node.js v12 or above
11
+ ## Compatibility
14
12
 
13
+ - Ember.js v5.8 or above
14
+ - Embroider or ember-auto-import v2
15
+ - Node.js v20.19+ for local development/builds
15
16
 
16
17
  ## Installation
17
18
 
@@ -19,40 +20,39 @@ Compatibility
19
20
  ember install ember-safe-button
20
21
  ```
21
22
 
22
-
23
23
  ## Usage
24
24
 
25
25
  Minimal example:
26
26
 
27
27
  ```hbs
28
- <SafeButton
29
- @message="delete"
30
- @onConfirm={{this.safeButtonClicked}}
31
- />
28
+ <SafeButton @message="delete" @onConfirm={{this.safeButtonClicked}} />
32
29
  ```
33
30
 
34
31
  Which will produce:
35
32
 
36
- ![Example](/docs/public/demo.gif)
33
+ ![Example](./public/demo.gif)
37
34
 
38
35
  ## Documentation
39
36
 
40
- For many more examples and API design see [ember-safe-button documentation website](https://ember-safe-button.netlify.com).
37
+ Run the demo app locally:
41
38
 
42
- ## Contributing
39
+ ```
40
+ pnpm start
41
+ ```
43
42
 
44
- See the [Contributing](CONTRIBUTING.md) guide for details.
43
+ Build the demo app for static hosting:
45
44
 
46
- ## Compatibility
45
+ ```
46
+ pnpm demo:build
47
+ ```
47
48
 
48
- * Ember.js v3.16 or above
49
- * Ember CLI v2.13 or above
50
- * Node.js v10 or above
49
+ ## Contributing
50
+
51
+ See the [Contributing](CONTRIBUTING.md) guide for details.
51
52
 
52
53
  ## Contributors
53
54
 
54
- - [Michal Bryxí](https://twitter.com/MichalBryxi)
55
- - [Karel Funda](https://twitter.com/fundix2)
55
+ - [Michal Bryxí](https://mastodon.world/deck/@MichalBryxi)
56
56
 
57
57
  ## License
58
58
 
@@ -60,5 +60,6 @@ This project is licensed under the [MIT License](LICENSE.md).
60
60
 
61
61
  ## Attribution
62
62
 
63
- <div>Icons made by <a href="https://www.flaticon.com/authors/dinosoftlabs" title="DinosoftLabs">DinosoftLabs</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a></div>
64
- <div>Icons made by <a href="https://www.flaticon.com/authors/surang" title="surang">surang</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a></div>
63
+ Icons made by [DinosoftLabs](https://www.flaticon.com/authors/dinosoftlabs) and
64
+ [surang](https://www.flaticon.com/authors/surang) from
65
+ [Flaticon](https://www.flaticon.com/).
package/addon-main.cjs CHANGED
@@ -1,6 +1,4 @@
1
1
  'use strict';
2
2
 
3
- const path = require('path');
4
3
  const { addonV1Shim } = require('@embroider/addon-shim');
5
-
6
4
  module.exports = addonV1Shim(__dirname);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/components/safe-button/animations/index.js"],"sourcesContent":["import poing from './poing';\nimport slide from './slide';\nimport roll from './roll';\nimport flip from './flip';\nimport liftBars from './lift-bars';\nimport zoom from './zoom';\n\nexport default {\n poing,\n slide,\n flip,\n zoom,\n roll,\n 'lift-bars': liftBars,\n};\n"],"names":["poing","slide","flip","zoom","roll","liftBars"],"mappings":";;;;;;;AAOA,yBAAe;EACbA,KAAK;EACLC,KAAK;EACLC,IAAI;EACJC,IAAI;EACJC,IAAI;AACJ,EAAA,WAAW,EAAEC;AACf,CAAC;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/components/safe-button/animations/index.js"],"sourcesContent":["import poing from './poing.js';\nimport slide from './slide.js';\nimport roll from './roll.js';\nimport flip from './flip.js';\nimport liftBars from './lift-bars.js';\nimport zoom from './zoom.js';\n\nexport default {\n poing,\n slide,\n flip,\n zoom,\n roll,\n 'lift-bars': liftBars,\n};\n"],"names":["poing","slide","flip","zoom","roll","liftBars"],"mappings":";;;;;;;AAOA,yBAAe;EACbA,KAAK;EACLC,KAAK;EACLC,IAAI;EACJC,IAAI;EACJC,IAAI;AACJ,EAAA,WAAW,EAAEC;AACf,CAAC;;;;"}
@@ -1,21 +1,22 @@
1
- import { _ as _applyDecoratedDescriptor, a as _initializerDefineProperty, b as _defineProperty } from '../../_rollupPluginBabelHelpers-hULyhLkN.js';
2
1
  import { buildTask } from 'ember-concurrency/async-arrow-runtime';
3
2
  import Component from '@glimmer/component';
4
3
  import { tracked } from '@glimmer/tracking';
5
4
  import { assert } from '@ember/debug';
6
5
  import { action } from '@ember/object';
6
+ import { hash } from '@ember/helper';
7
7
  import { timeout } from 'ember-concurrency';
8
8
  import embeddedAnimations from './animations/index.js';
9
+ import SafetyComponent from '../safety/index.js';
10
+ import TriggerComponent from '../trigger/index.js';
9
11
  import { SAFETY_STATUS } from '../../states.js';
10
12
  import { precompileTemplate } from '@ember/template-compilation';
11
13
  import { setComponentTemplate } from '@ember/component';
14
+ import templateOnly from '@ember/component/template-only';
15
+ import { g, i, n } from 'decorator-transforms/runtime-esm';
12
16
 
13
- var TEMPLATE = precompileTemplate("<div class=\'ember-safe-button\' ...attributes>\n {{#if (has-block)}}\n {{yield\n (hash\n trigger=(component\n \'trigger\'\n onClick=@onConfirm\n safetyStatus=this.safetyStatus\n displayedMessage=this.displayedMessage\n webAnimations=this.webAnimations.trigger\n )\n safety=(component\n \'safety\'\n onClick=this.onUnlocking\n safetyStatus=this.safetyStatus\n displayedMessage=this.displayedMessage\n webAnimations=this.webAnimations.safety\n )\n )\n }}\n {{else}}\n <Trigger\n @onClick={{@onConfirm}}\n @safetyStatus={{this.safetyStatus}}\n @displayedMessage={{this.displayedMessage}}\n @webAnimations={{this.webAnimations.trigger}}\n />\n <Safety\n @onClick={{this.onUnlocking}}\n @safetyStatus={{this.safetyStatus}}\n @displayedMessage={{this.displayedMessage}}\n @webAnimations={{this.webAnimations.safety}}\n />\n {{/if}}\n</div>");
14
-
15
- var _class, _descriptor;
16
17
  const DEFAULT_TIMEOUT = 3000;
17
- const DEFAULT_ANIMATION = "slide";
18
-
18
+ const DEFAULT_ANIMATION = 'slide';
19
+ console.log('DEBUG: ember-safe-button loaded');
19
20
  /**
20
21
  Main component.
21
22
 
@@ -95,22 +96,20 @@ const DEFAULT_ANIMATION = "slide";
95
96
  }
96
97
  ```
97
98
  */
98
- let SafeButtonComponent = (_class = class SafeButtonComponent extends Component {
99
+ class SafeButtonComponent extends Component {
99
100
  constructor() {
100
101
  super(...arguments);
101
- _initializerDefineProperty(this, "safetyStatus", _descriptor, this);
102
- _defineProperty(this, "triggerSafety", buildTask(() => ({
103
- context: this,
104
- generator: function* () {
105
- yield timeout(this.timeout);
106
- this.safetyStatus = SAFETY_STATUS.LOCKING;
107
- }
108
- }), null, "triggerSafety", null));
109
- assert("@onConfirm attribute must be set", typeof this.args.onConfirm === "function");
102
+ assert('@onConfirm attribute must be set', typeof this.args.onConfirm === 'function');
103
+ }
104
+ static {
105
+ g(this.prototype, "safetyStatus", [tracked], function () {
106
+ return SAFETY_STATUS.LOCKED;
107
+ });
110
108
  }
109
+ #safetyStatus = (i(this, "safetyStatus"), void 0);
111
110
  get activeAnimation() {
112
111
  const animation = this.args.animation || DEFAULT_ANIMATION;
113
- return typeof animation === "string" ? embeddedAnimations[animation] : animation;
112
+ return typeof animation === 'string' ? embeddedAnimations[animation] : animation;
114
113
  }
115
114
  get webAnimations() {
116
115
  const {
@@ -151,27 +150,59 @@ let SafeButtonComponent = (_class = class SafeButtonComponent extends Component
151
150
  return this.args.timeout || DEFAULT_TIMEOUT;
152
151
  }
153
152
  get displayedMessage() {
154
- return this.args.message || "delete";
153
+ return this.args.message || 'delete';
155
154
  }
156
155
  onUnlocking() {
157
156
  this.safetyStatus = SAFETY_STATUS.UNLOCKING;
158
157
  }
158
+ static {
159
+ n(this.prototype, "onUnlocking", [action]);
160
+ }
159
161
  onUnlocked() {
160
162
  this.safetyStatus = SAFETY_STATUS.UNLOCKED;
161
163
  this.triggerSafety.perform();
162
164
  }
165
+ static {
166
+ n(this.prototype, "onUnlocked", [action]);
167
+ }
163
168
  onLocked() {
164
169
  this.safetyStatus = SAFETY_STATUS.LOCKED;
165
170
  }
166
- }, _descriptor = _applyDecoratedDescriptor(_class.prototype, "safetyStatus", [tracked], {
167
- configurable: true,
168
- enumerable: true,
169
- writable: true,
170
- initializer: function () {
171
- return SAFETY_STATUS.LOCKED;
171
+ static {
172
+ n(this.prototype, "onLocked", [action]);
173
+ }
174
+ triggerSafety = buildTask(() => ({
175
+ context: this,
176
+ generator: function* () {
177
+ yield timeout(this.timeout);
178
+ this.safetyStatus = SAFETY_STATUS.LOCKING;
179
+ }
180
+ }), null, "triggerSafety", null);
181
+ triggerInstance = setComponentTemplate(precompileTemplate("<Trigger @onClick={{@onConfirm}} @safetyStatus={{this.safetyStatus}} @displayedMessage={{this.displayedMessage}} @webAnimations={{this.webAnimations.trigger}} />", {
182
+ strictMode: true,
183
+ scope: () => ({
184
+ Trigger: TriggerComponent,
185
+ this: this
186
+ })
187
+ }), templateOnly());
188
+ safetyInstance = setComponentTemplate(precompileTemplate("<Safety @onClick={{this.onUnlocking}} @safetyStatus={{this.safetyStatus}} @displayedMessage={{this.displayedMessage}} @webAnimations={{this.webAnimations.safety}} />", {
189
+ strictMode: true,
190
+ scope: () => ({
191
+ Safety: SafetyComponent,
192
+ this: this
193
+ })
194
+ }), templateOnly());
195
+ static {
196
+ setComponentTemplate(precompileTemplate("<div class=\"ember-safe-button\" ...attributes>\n {{#if (has-block)}}\n {{yield (hash trigger=(component Trigger onClick=@onConfirm safetyStatus=this.safetyStatus displayedMessage=this.displayedMessage webAnimations=this.webAnimations.trigger) safety=(component Safety onClick=this.onUnlocking safetyStatus=this.safetyStatus displayedMessage=this.displayedMessage webAnimations=this.webAnimations.safety))}}\n {{else}}\n <Trigger @onClick={{@onConfirm}} @safetyStatus={{this.safetyStatus}} @displayedMessage={{this.displayedMessage}} @webAnimations={{this.webAnimations.trigger}} />\n <Safety @onClick={{this.onUnlocking}} @safetyStatus={{this.safetyStatus}} @displayedMessage={{this.displayedMessage}} @webAnimations={{this.webAnimations.safety}} />\n {{/if}}\n</div>", {
197
+ strictMode: true,
198
+ scope: () => ({
199
+ hash,
200
+ Trigger: TriggerComponent,
201
+ Safety: SafetyComponent
202
+ })
203
+ }), this);
172
204
  }
173
- }), _applyDecoratedDescriptor(_class.prototype, "onUnlocking", [action], Object.getOwnPropertyDescriptor(_class.prototype, "onUnlocking"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "onUnlocked", [action], Object.getOwnPropertyDescriptor(_class.prototype, "onUnlocked"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "onLocked", [action], Object.getOwnPropertyDescriptor(_class.prototype, "onLocked"), _class.prototype), _class);
174
- setComponentTemplate(TEMPLATE, SafeButtonComponent);
205
+ }
175
206
 
176
207
  export { SafeButtonComponent as default };
177
208
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/safe-button/index.js"],"sourcesContent":["import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { assert } from \"@ember/debug\";\nimport { action } from \"@ember/object\";\n\nimport { timeout } from \"ember-concurrency\";\nimport { task } from \"ember-concurrency\";\n\nimport embeddedAnimations from \"./animations/index\";\nimport { SAFETY_STATUS } from \"../../states\";\n\nconst DEFAULT_TIMEOUT = 3000;\nconst DEFAULT_ANIMATION = \"slide\";\n\n/**\n Main component.\n\n ```hbs\n <SafeButton\n class=\"border-black\"\n @animation={{this.animation}}\n @message=\"Delete me\"\n @timeout={{2000}}\n @onConfirm={{action safeButtonClicked}} />\n ```\n\n ```\n @class SafeButtonComponent\n @param {string} [class=\"ember-safe-button\"] CSS class to be applied on the wrapping element.\n @param {object} [animation=\"slid\"] Definition of animation; It can either be string and point to one of the pre-defined animations; Or it can be an object describing Web Animation API\n @param {number} [timeout=3000] Number of millisenconds after which the safety rolls back over the trigger.\n @param {string} [message=\"delete\"] Text to be printed on the buttons in case block is not provided.\n @param {function} onConfirm - Action to trigger whenever user clicks the trigger.\n ```\n\n When trying to construct your own `@animation` object you can take inspiration from the simple `slide` animation. The thing to keep in mind is that these go directly as parameters to [element.animate()](https://developer.mozilla.org/en-US/docs/Web/API/Element/animate) and that there are in fact four animations of which always two fire at the same time:\n\n - `safety.unlocking` and `trigger.unlocking`\n - `safety.locking` and `trigger.locking`\n\n ```js\n {\n safety: {\n unlocking: {\n keyframes: [\n { transform: 'translateX(0)' },\n { transform: 'translateX(-100%)' },\n ],\n options: {\n duration: 1000,\n fill: 'both',\n easing: 'ease',\n },\n },\n locking: {\n keyframes: [\n { transform: 'translateX(-100%)' },\n { transform: 'translateX(0)' },\n ],\n options: {\n duration: 1000,\n fill: 'both',\n easing: 'ease',\n },\n },\n },\n trigger: {\n unlocking: {\n keyframes: [\n { transform: 'translateX(100%)' },\n { transform: 'translateX(0)' },\n ],\n options: {\n duration: 1000,\n fill: 'both',\n easing: 'ease',\n },\n },\n locking: {\n keyframes: [\n { transform: 'translateX(0)' },\n { transform: 'translateX(100%)' },\n ],\n options: {\n duration: 1000,\n fill: 'both',\n easing: 'ease',\n },\n },\n },\n }\n ```\n */\nexport default class SafeButtonComponent extends Component {\n constructor() {\n super(...arguments);\n\n assert(\n \"@onConfirm attribute must be set\",\n typeof this.args.onConfirm === \"function\"\n );\n }\n\n @tracked safetyStatus = SAFETY_STATUS.LOCKED;\n\n get activeAnimation() {\n const animation = this.args.animation || DEFAULT_ANIMATION;\n\n return typeof animation === \"string\"\n ? embeddedAnimations[animation]\n : animation;\n }\n\n get webAnimations() {\n const { activeAnimation, onUnlocked, onLocked } = this;\n\n return {\n safety: {\n unlocking: ({ element }) => {\n let animation = element.animate(\n activeAnimation.safety.unlocking.keyframes,\n activeAnimation.safety.unlocking.options\n );\n animation.onfinish = onUnlocked;\n },\n locking: ({ element }) => {\n let animation = element.animate(\n activeAnimation.safety.locking.keyframes,\n activeAnimation.safety.locking.options\n );\n animation.onfinish = onLocked;\n },\n },\n trigger: {\n unlocking({ element }) {\n element.animate(\n activeAnimation.trigger.unlocking.keyframes,\n activeAnimation.trigger.unlocking.options\n );\n },\n locking({ element }) {\n element.animate(\n activeAnimation.trigger.locking.keyframes,\n activeAnimation.trigger.locking.options\n );\n },\n },\n };\n }\n\n get timeout() {\n return this.args.timeout || DEFAULT_TIMEOUT;\n }\n\n get displayedMessage() {\n return this.args.message || \"delete\";\n }\n\n @action\n onUnlocking() {\n this.safetyStatus = SAFETY_STATUS.UNLOCKING;\n }\n\n @action\n onUnlocked() {\n this.safetyStatus = SAFETY_STATUS.UNLOCKED;\n this.triggerSafety.perform();\n }\n\n @action\n onLocked() {\n this.safetyStatus = SAFETY_STATUS.LOCKED;\n }\n\n triggerSafety = task(async () => {\n await timeout(this.timeout);\n this.safetyStatus = SAFETY_STATUS.LOCKING;\n });\n}\n"],"names":["DEFAULT_TIMEOUT","DEFAULT_ANIMATION","SafeButtonComponent","_class","Component","constructor","arguments","_initializerDefineProperty","_descriptor","_defineProperty","_buildTask","context","generator","timeout","safetyStatus","SAFETY_STATUS","LOCKING","assert","args","onConfirm","activeAnimation","animation","embeddedAnimations","webAnimations","onUnlocked","onLocked","safety","unlocking","element","animate","keyframes","options","onfinish","locking","trigger","displayedMessage","message","onUnlocking","UNLOCKING","UNLOCKED","triggerSafety","perform","LOCKED","_applyDecoratedDescriptor","prototype","tracked","configurable","enumerable","writable","initializer","action","Object","getOwnPropertyDescriptor","setComponentTemplate","TEMPLATE"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/safe-button/index.gjs"],"sourcesContent":["import Component from '@glimmer/component';\nimport { tracked } from '@glimmer/tracking';\nimport { assert } from '@ember/debug';\nimport { action } from '@ember/object';\nimport { hash } from '@ember/helper';\n\nimport { timeout } from 'ember-concurrency';\nimport { task } from 'ember-concurrency';\n\nimport embeddedAnimations from './animations/index.js';\nimport Safety from '../safety/index.gjs';\nimport Trigger from '../trigger/index.gjs';\nimport { SAFETY_STATUS } from '../../states.js';\n\nconst DEFAULT_TIMEOUT = 3000;\nconst DEFAULT_ANIMATION = 'slide';\nconsole.log('DEBUG: ember-safe-button loaded');\n\n/**\n Main component.\n\n ```hbs\n <SafeButton\n class=\"border-black\"\n @animation={{this.animation}}\n @message=\"Delete me\"\n @timeout={{2000}}\n @onConfirm={{action safeButtonClicked}} />\n ```\n\n ```\n @class SafeButtonComponent\n @param {string} [class=\"ember-safe-button\"] CSS class to be applied on the wrapping element.\n @param {object} [animation=\"slid\"] Definition of animation; It can either be string and point to one of the pre-defined animations; Or it can be an object describing Web Animation API\n @param {number} [timeout=3000] Number of millisenconds after which the safety rolls back over the trigger.\n @param {string} [message=\"delete\"] Text to be printed on the buttons in case block is not provided.\n @param {function} onConfirm - Action to trigger whenever user clicks the trigger.\n ```\n\n When trying to construct your own `@animation` object you can take inspiration from the simple `slide` animation. The thing to keep in mind is that these go directly as parameters to [element.animate()](https://developer.mozilla.org/en-US/docs/Web/API/Element/animate) and that there are in fact four animations of which always two fire at the same time:\n\n - `safety.unlocking` and `trigger.unlocking`\n - `safety.locking` and `trigger.locking`\n\n ```js\n {\n safety: {\n unlocking: {\n keyframes: [\n { transform: 'translateX(0)' },\n { transform: 'translateX(-100%)' },\n ],\n options: {\n duration: 1000,\n fill: 'both',\n easing: 'ease',\n },\n },\n locking: {\n keyframes: [\n { transform: 'translateX(-100%)' },\n { transform: 'translateX(0)' },\n ],\n options: {\n duration: 1000,\n fill: 'both',\n easing: 'ease',\n },\n },\n },\n trigger: {\n unlocking: {\n keyframes: [\n { transform: 'translateX(100%)' },\n { transform: 'translateX(0)' },\n ],\n options: {\n duration: 1000,\n fill: 'both',\n easing: 'ease',\n },\n },\n locking: {\n keyframes: [\n { transform: 'translateX(0)' },\n { transform: 'translateX(100%)' },\n ],\n options: {\n duration: 1000,\n fill: 'both',\n easing: 'ease',\n },\n },\n },\n }\n ```\n */\nexport default class SafeButtonComponent extends Component {\n constructor() {\n super(...arguments);\n\n assert(\n '@onConfirm attribute must be set',\n typeof this.args.onConfirm === 'function',\n );\n }\n\n @tracked safetyStatus = SAFETY_STATUS.LOCKED;\n\n get activeAnimation() {\n const animation = this.args.animation || DEFAULT_ANIMATION;\n\n return typeof animation === 'string'\n ? embeddedAnimations[animation]\n : animation;\n }\n\n get webAnimations() {\n const { activeAnimation, onUnlocked, onLocked } = this;\n\n return {\n safety: {\n unlocking: ({ element }) => {\n let animation = element.animate(\n activeAnimation.safety.unlocking.keyframes,\n activeAnimation.safety.unlocking.options,\n );\n animation.onfinish = onUnlocked;\n },\n locking: ({ element }) => {\n let animation = element.animate(\n activeAnimation.safety.locking.keyframes,\n activeAnimation.safety.locking.options,\n );\n animation.onfinish = onLocked;\n },\n },\n trigger: {\n unlocking({ element }) {\n element.animate(\n activeAnimation.trigger.unlocking.keyframes,\n activeAnimation.trigger.unlocking.options,\n );\n },\n locking({ element }) {\n element.animate(\n activeAnimation.trigger.locking.keyframes,\n activeAnimation.trigger.locking.options,\n );\n },\n },\n };\n }\n\n get timeout() {\n return this.args.timeout || DEFAULT_TIMEOUT;\n }\n\n get displayedMessage() {\n return this.args.message || 'delete';\n }\n\n @action\n onUnlocking() {\n this.safetyStatus = SAFETY_STATUS.UNLOCKING;\n }\n\n @action\n onUnlocked() {\n this.safetyStatus = SAFETY_STATUS.UNLOCKED;\n this.triggerSafety.perform();\n }\n\n @action\n onLocked() {\n this.safetyStatus = SAFETY_STATUS.LOCKED;\n }\n\n triggerSafety = task(async () => {\n await timeout(this.timeout);\n this.safetyStatus = SAFETY_STATUS.LOCKING;\n });\n\n triggerInstance = <template>\n <Trigger\n @onClick={{@onConfirm}}\n @safetyStatus={{this.safetyStatus}}\n @displayedMessage={{this.displayedMessage}}\n @webAnimations={{this.webAnimations.trigger}}\n />\n </template>;\n\n safetyInstance = <template>\n <Safety\n @onClick={{this.onUnlocking}}\n @safetyStatus={{this.safetyStatus}}\n @displayedMessage={{this.displayedMessage}}\n @webAnimations={{this.webAnimations.safety}}\n />\n </template>;\n\n <template>\n <div class=\"ember-safe-button\" ...attributes>\n {{#if (has-block)}}\n {{yield\n (hash\n trigger=(component\n Trigger\n onClick=@onConfirm\n safetyStatus=this.safetyStatus\n displayedMessage=this.displayedMessage\n webAnimations=this.webAnimations.trigger\n )\n safety=(component\n Safety\n onClick=this.onUnlocking\n safetyStatus=this.safetyStatus\n displayedMessage=this.displayedMessage\n webAnimations=this.webAnimations.safety\n )\n )\n }}\n {{else}}\n <Trigger\n @onClick={{@onConfirm}}\n @safetyStatus={{this.safetyStatus}}\n @displayedMessage={{this.displayedMessage}}\n @webAnimations={{this.webAnimations.trigger}}\n />\n <Safety\n @onClick={{this.onUnlocking}}\n @safetyStatus={{this.safetyStatus}}\n @displayedMessage={{this.displayedMessage}}\n @webAnimations={{this.webAnimations.safety}}\n />\n {{/if}}\n </div>\n </template>\n}\n"],"names":["DEFAULT_TIMEOUT","DEFAULT_ANIMATION","console","log","SafeButtonComponent","Component","constructor","arguments","assert","args","onConfirm","g","prototype","tracked","SAFETY_STATUS","LOCKED","i","activeAnimation","animation","embeddedAnimations","webAnimations","onUnlocked","onLocked","safety","unlocking","element","animate","keyframes","options","onfinish","locking","trigger","timeout","displayedMessage","message","onUnlocking","safetyStatus","UNLOCKING","n","action","UNLOCKED","triggerSafety","perform","_buildTask","context","generator","LOCKING","triggerInstance","setComponentTemplate","precompileTemplate","strictMode","scope","Trigger","this","templateOnly","safetyInstance","Safety","hash"],"mappings":"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"}
@@ -1,11 +1,10 @@
1
- import { b as _defineProperty } from '../../_rollupPluginBabelHelpers-hULyhLkN.js';
2
1
  import Component from '@glimmer/component';
2
+ import { on } from '@ember/modifier';
3
+ import animateMe from '../../modifiers/animate-me.js';
3
4
  import { SAFETY_STATUS } from '../../states.js';
4
5
  import { precompileTemplate } from '@ember/template-compilation';
5
6
  import { setComponentTemplate } from '@ember/component';
6
7
 
7
- var TEMPLATE = precompileTemplate("<button\n class={{this.BASIC_CLASS}}\n disabled={{this.isDisabled}}\n aria-label={{this.ariaLabel}}\n type=\'button\'\n tabindex={{if this.isDisabled \'-1\' \'0\'}}\n ...attributes\n {{on \'click\' @onClick}}\n {{animate-me\n safetyStatus=@safetyStatus\n webAnimations=@webAnimations\n focusMe=this.focusMe\n }}\n>\n {{#if (has-block)}}\n {{yield}}\n {{else}}\n {{@displayedMessage}}\n {{/if}}\n</button>");
8
-
9
8
  const BASIC_CLASS = 'ember-safe-button-safety';
10
9
  const ARIA_LABELS = {
11
10
  [SAFETY_STATUS.LOCKED]: 'Deactivate safety',
@@ -13,7 +12,6 @@ const ARIA_LABELS = {
13
12
  [SAFETY_STATUS.UNLOCKED]: 'Safety not active',
14
13
  [SAFETY_STATUS.LOCKING]: 'Activating safety'
15
14
  };
16
-
17
15
  /**
18
16
  Safety is a component that covers the trigger.
19
17
 
@@ -30,10 +28,7 @@ const ARIA_LABELS = {
30
28
  @class SafetyComponent
31
29
  */
32
30
  class SafetyComponent extends Component {
33
- constructor(...args) {
34
- super(...args);
35
- _defineProperty(this, "BASIC_CLASS", BASIC_CLASS);
36
- }
31
+ BASIC_CLASS = BASIC_CLASS;
37
32
  get isDisabled() {
38
33
  return this.args.safetyStatus === SAFETY_STATUS.UNLOCKED;
39
34
  }
@@ -43,8 +38,16 @@ class SafetyComponent extends Component {
43
38
  get ariaLabel() {
44
39
  return ARIA_LABELS[this.args.safetyStatus];
45
40
  }
41
+ static {
42
+ setComponentTemplate(precompileTemplate("<button class={{this.BASIC_CLASS}} disabled={{this.isDisabled}} aria-label={{this.ariaLabel}} type=\"button\" tabindex={{if this.isDisabled \"-1\" \"0\"}} ...attributes {{on \"click\" @onClick}} {{animateMe safetyStatus=@safetyStatus webAnimations=@webAnimations focusMe=this.focusMe}}>\n {{#if (has-block)}}\n {{yield}}\n {{else}}\n {{@displayedMessage}}\n {{/if}}\n</button>", {
43
+ strictMode: true,
44
+ scope: () => ({
45
+ on,
46
+ animateMe
47
+ })
48
+ }), this);
49
+ }
46
50
  }
47
- setComponentTemplate(TEMPLATE, SafetyComponent);
48
51
 
49
52
  export { ARIA_LABELS, BASIC_CLASS, SafetyComponent as default };
50
53
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/safety/index.js"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport { SAFETY_STATUS } from '../../states';\n\nexport const BASIC_CLASS = 'ember-safe-button-safety';\nexport const ARIA_LABELS = {\n [SAFETY_STATUS.LOCKED]: 'Deactivate safety',\n [SAFETY_STATUS.UNLOCKING]: 'Deactivating safety',\n [SAFETY_STATUS.UNLOCKED]: 'Safety not active',\n [SAFETY_STATUS.LOCKING]: 'Activating safety',\n};\n\n/**\n Safety is a component that covers the trigger.\n\n ```hbs\n <SafeButton\n @onConfirm={{action safeButtonClicked}} as |button|\n >\n <button.safety class=\"bg-grey-light\">\n This is safety\n </button.safety>\n </SafeButton>\n ```\n\n @class SafetyComponent\n */\nexport default class SafetyComponent extends Component {\n BASIC_CLASS = BASIC_CLASS;\n\n get isDisabled() {\n return this.args.safetyStatus === SAFETY_STATUS.UNLOCKED;\n }\n\n get focusMe() {\n return !this.isDisabled;\n }\n\n get ariaLabel() {\n return ARIA_LABELS[this.args.safetyStatus];\n }\n}\n"],"names":["BASIC_CLASS","ARIA_LABELS","SAFETY_STATUS","LOCKED","UNLOCKING","UNLOCKED","LOCKING","SafetyComponent","Component","constructor","args","_defineProperty","isDisabled","safetyStatus","focusMe","ariaLabel","setComponentTemplate","TEMPLATE"],"mappings":";;;;;;;;AAIO,MAAMA,WAAW,GAAG;AACpB,MAAMC,WAAW,GAAG;AACzB,EAAA,CAACC,aAAa,CAACC,MAAM,GAAG,mBAAmB;AAC3C,EAAA,CAACD,aAAa,CAACE,SAAS,GAAG,qBAAqB;AAChD,EAAA,CAACF,aAAa,CAACG,QAAQ,GAAG,mBAAmB;EAC7C,CAACH,aAAa,CAACI,OAAO,GAAG;AAC3B;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACe,MAAMC,eAAe,SAASC,SAAS,CAAC;AAAAC,EAAAA,WAAAA,CAAA,GAAAC,IAAA,EAAA;AAAA,IAAA,KAAA,CAAA,GAAAA,IAAA,CAAA;AAAAC,IAAAA,eAAA,sBACvCX,WAAW,CAAA;AAAA,EAAA;EAEzB,IAAIY,UAAUA,GAAG;IACf,OAAO,IAAI,CAACF,IAAI,CAACG,YAAY,KAAKX,aAAa,CAACG,QAAQ;AAC1D,EAAA;EAEA,IAAIS,OAAOA,GAAG;IACZ,OAAO,CAAC,IAAI,CAACF,UAAU;AACzB,EAAA;EAEA,IAAIG,SAASA,GAAG;AACd,IAAA,OAAOd,WAAW,CAAC,IAAI,CAACS,IAAI,CAACG,YAAY,CAAC;AAC5C,EAAA;AACF;AAACG,oBAAA,CAAAC,QAAA,EAdoBV,eAAe,CAAA;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/safety/index.gjs"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport { on } from '@ember/modifier';\nimport animateMe from '../../modifiers/animate-me.js';\nimport { SAFETY_STATUS } from '../../states.js';\n\nexport const BASIC_CLASS = 'ember-safe-button-safety';\nexport const ARIA_LABELS = {\n [SAFETY_STATUS.LOCKED]: 'Deactivate safety',\n [SAFETY_STATUS.UNLOCKING]: 'Deactivating safety',\n [SAFETY_STATUS.UNLOCKED]: 'Safety not active',\n [SAFETY_STATUS.LOCKING]: 'Activating safety',\n};\n\n/**\n Safety is a component that covers the trigger.\n\n ```hbs\n <SafeButton\n @onConfirm={{action safeButtonClicked}} as |button|\n >\n <button.safety class=\"bg-grey-light\">\n This is safety\n </button.safety>\n </SafeButton>\n ```\n\n @class SafetyComponent\n */\nexport default class SafetyComponent extends Component {\n BASIC_CLASS = BASIC_CLASS;\n\n get isDisabled() {\n return this.args.safetyStatus === SAFETY_STATUS.UNLOCKED;\n }\n\n get focusMe() {\n return !this.isDisabled;\n }\n\n get ariaLabel() {\n return ARIA_LABELS[this.args.safetyStatus];\n }\n\n <template>\n <button\n class={{this.BASIC_CLASS}}\n disabled={{this.isDisabled}}\n aria-label={{this.ariaLabel}}\n type=\"button\"\n tabindex={{if this.isDisabled \"-1\" \"0\"}}\n ...attributes\n {{on \"click\" @onClick}}\n {{animateMe\n safetyStatus=@safetyStatus\n webAnimations=@webAnimations\n focusMe=this.focusMe\n }}\n >\n {{#if (has-block)}}\n {{yield}}\n {{else}}\n {{@displayedMessage}}\n {{/if}}\n </button>\n </template>\n}\n"],"names":["BASIC_CLASS","ARIA_LABELS","SAFETY_STATUS","LOCKED","UNLOCKING","UNLOCKED","LOCKING","SafetyComponent","Component","isDisabled","args","safetyStatus","focusMe","ariaLabel","setComponentTemplate","precompileTemplate","strictMode","scope","on","animateMe"],"mappings":";;;;;;;AAMO,MAAMA,cAAc;AACpB,MAAMC,WAAA,GAAc;AACzB,EAAA,CAACC,aAAA,CAAcC,MAAM,GAAG,mBAAA;AACxB,EAAA,CAACD,aAAA,CAAcE,SAAS,GAAG,qBAAA;AAC3B,EAAA,CAACF,aAAA,CAAcG,QAAQ,GAAG,mBAAA;EAC1B,CAACH,aAAA,CAAcI,OAAO,GAAG;AAC3B;AAEA;;;;;;;;;;;;;;;AAee,MAAMC,eAAA,SAAwBC,SAAA,CAAA;AAC3CR,EAAAA,WAAA,GAAcA,WAAA;EAEd,IAAIS,UAAAA,GAAa;IACf,OAAO,IAAI,CAACC,IAAI,CAACC,YAAY,KAAKT,cAAcG,QAAQ;AAC1D,EAAA;EAEA,IAAIO,OAAAA,GAAU;IACZ,OAAO,CAAC,IAAI,CAACH,UAAU;AACzB,EAAA;EAEA,IAAII,SAAAA,GAAY;AACd,IAAA,OAAOZ,WAAW,CAAC,IAAI,CAACS,IAAI,CAACC,YAAY,CAAC;AAC5C,EAAA;AAEA,EAAA;IAAAG,oBAAA,CAAAC,kBAAA,CAAA,mYAAA,EAqBA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
@@ -1,13 +1,11 @@
1
- import { b as _defineProperty } from '../../_rollupPluginBabelHelpers-hULyhLkN.js';
2
1
  import Component from '@glimmer/component';
2
+ import { on } from '@ember/modifier';
3
+ import animateMe from '../../modifiers/animate-me.js';
3
4
  import { SAFETY_STATUS } from '../../states.js';
4
5
  import { precompileTemplate } from '@ember/template-compilation';
5
6
  import { setComponentTemplate } from '@ember/component';
6
7
 
7
- var TEMPLATE = precompileTemplate("<button\n class={{this.BASIC_CLASS}}\n disabled={{this.isDisabled}}\n aria-label=\'Confirm action\'\n type=\'button\'\n tabindex={{if this.isDisabled \'-1\' \'0\'}}\n ...attributes\n {{on \'click\' @onClick}}\n {{animate-me\n safetyStatus=@safetyStatus\n webAnimations=@webAnimations\n focusMe=this.focusMe\n }}\n>\n {{#if (has-block)}}\n {{yield}}\n {{else}}\n {{@displayedMessage}}\n {{/if}}\n</button>");
8
-
9
8
  const BASIC_CLASS = 'ember-safe-button-trigger';
10
-
11
9
  /**
12
10
  Trigger is a component that is by default covered by safety and triggers the action passed to <SafeButton> when clicked.
13
11
 
@@ -24,18 +22,23 @@ const BASIC_CLASS = 'ember-safe-button-trigger';
24
22
  @class SafetyComponent
25
23
  */
26
24
  class TriggerComponent extends Component {
27
- constructor(...args) {
28
- super(...args);
29
- _defineProperty(this, "BASIC_CLASS", BASIC_CLASS);
30
- }
25
+ BASIC_CLASS = BASIC_CLASS;
31
26
  get isDisabled() {
32
27
  return this.args.safetyStatus !== SAFETY_STATUS.UNLOCKED;
33
28
  }
34
29
  get focusMe() {
35
30
  return !this.isDisabled;
36
31
  }
32
+ static {
33
+ setComponentTemplate(precompileTemplate("<button class={{this.BASIC_CLASS}} disabled={{this.isDisabled}} aria-label=\"Confirm action\" type=\"button\" tabindex={{if this.isDisabled \"-1\" \"0\"}} ...attributes {{on \"click\" @onClick}} {{animateMe safetyStatus=@safetyStatus webAnimations=@webAnimations focusMe=this.focusMe}}>\n {{#if (has-block)}}\n {{yield}}\n {{else}}\n {{@displayedMessage}}\n {{/if}}\n</button>", {
34
+ strictMode: true,
35
+ scope: () => ({
36
+ on,
37
+ animateMe
38
+ })
39
+ }), this);
40
+ }
37
41
  }
38
- setComponentTemplate(TEMPLATE, TriggerComponent);
39
42
 
40
43
  export { BASIC_CLASS, TriggerComponent as default };
41
44
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/trigger/index.js"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport { SAFETY_STATUS } from '../../states';\n\nexport const BASIC_CLASS = 'ember-safe-button-trigger';\n\n/**\n Trigger is a component that is by default covered by safety and triggers the action passed to <SafeButton> when clicked.\n\n ```hbs\n <SafeButton\n @onConfirm={{action safeButtonClicked}} as |button|\n >\n <button.trigger class=\"bg-red-dark\">\n Trigger\n </button.trigger>\n </SafeButton>\n ```\n\n @class SafetyComponent\n */\nexport default class TriggerComponent extends Component {\n BASIC_CLASS = BASIC_CLASS;\n\n get isDisabled() {\n return this.args.safetyStatus !== SAFETY_STATUS.UNLOCKED;\n }\n\n get focusMe() {\n return !this.isDisabled;\n }\n}\n"],"names":["BASIC_CLASS","TriggerComponent","Component","constructor","args","_defineProperty","isDisabled","safetyStatus","SAFETY_STATUS","UNLOCKED","focusMe","setComponentTemplate","TEMPLATE"],"mappings":";;;;;;;;AAIO,MAAMA,WAAW,GAAG;;AAE3B;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACe,MAAMC,gBAAgB,SAASC,SAAS,CAAC;AAAAC,EAAAA,WAAAA,CAAA,GAAAC,IAAA,EAAA;AAAA,IAAA,KAAA,CAAA,GAAAA,IAAA,CAAA;AAAAC,IAAAA,eAAA,sBACxCL,WAAW,CAAA;AAAA,EAAA;EAEzB,IAAIM,UAAUA,GAAG;IACf,OAAO,IAAI,CAACF,IAAI,CAACG,YAAY,KAAKC,aAAa,CAACC,QAAQ;AAC1D,EAAA;EAEA,IAAIC,OAAOA,GAAG;IACZ,OAAO,CAAC,IAAI,CAACJ,UAAU;AACzB,EAAA;AACF;AAACK,oBAAA,CAAAC,QAAA,EAVoBX,gBAAgB,CAAA;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/trigger/index.gjs"],"sourcesContent":["import Component from '@glimmer/component';\nimport { on } from '@ember/modifier';\nimport animateMe from '../../modifiers/animate-me.js';\n\nimport { SAFETY_STATUS } from '../../states.js';\n\nexport const BASIC_CLASS = 'ember-safe-button-trigger';\n\n/**\n Trigger is a component that is by default covered by safety and triggers the action passed to <SafeButton> when clicked.\n\n ```hbs\n <SafeButton\n @onConfirm={{action safeButtonClicked}} as |button|\n >\n <button.trigger class=\"bg-red-dark\">\n Trigger\n </button.trigger>\n </SafeButton>\n ```\n\n @class SafetyComponent\n */\nexport default class TriggerComponent extends Component {\n BASIC_CLASS = BASIC_CLASS;\n\n get isDisabled() {\n return this.args.safetyStatus !== SAFETY_STATUS.UNLOCKED;\n }\n\n get focusMe() {\n return !this.isDisabled;\n }\n\n <template>\n <button\n class={{this.BASIC_CLASS}}\n disabled={{this.isDisabled}}\n aria-label=\"Confirm action\"\n type=\"button\"\n tabindex={{if this.isDisabled \"-1\" \"0\"}}\n ...attributes\n {{on \"click\" @onClick}}\n {{animateMe\n safetyStatus=@safetyStatus\n webAnimations=@webAnimations\n focusMe=this.focusMe\n }}\n >\n {{#if (has-block)}}\n {{yield}}\n {{else}}\n {{@displayedMessage}}\n {{/if}}\n </button>\n </template>\n}\n"],"names":["BASIC_CLASS","TriggerComponent","Component","isDisabled","args","safetyStatus","SAFETY_STATUS","UNLOCKED","focusMe","setComponentTemplate","precompileTemplate","strictMode","scope","on","animateMe"],"mappings":";;;;;;;AAMO,MAAMA,cAAc;AAE3B;;;;;;;;;;;;;;;AAee,MAAMC,gBAAA,SAAyBC,SAAA,CAAA;AAC5CF,EAAAA,WAAA,GAAcA,WAAA;EAEd,IAAIG,UAAAA,GAAa;IACf,OAAO,IAAI,CAACC,IAAI,CAACC,YAAY,KAAKC,cAAcC,QAAQ;AAC1D,EAAA;EAEA,IAAIC,OAAAA,GAAU;IACZ,OAAO,CAAC,IAAI,CAACL,UAAU;AACzB,EAAA;AAEA,EAAA;IAAAM,oBAAA,CAAAC,kBAAA,CAAA,mYAAA,EAqBA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
package/dist/index.js CHANGED
@@ -1,2 +1,4 @@
1
-
1
+ export { default as Trigger } from './components/trigger/index.js';
2
+ export { default as Safety } from './components/safety/index.js';
3
+ export { default as SafeButton } from './components/safe-button/index.js';
2
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -1 +1 @@
1
- {"version":3,"file":"animate-me.js","sources":["../../src/modifiers/animate-me.js"],"sourcesContent":["import { modifier } from 'ember-modifier';\n\nimport { SAFETY_STATUS } from '../states';\n\nexport default modifier(\n (element, _, named) => {\n if (named.focusMe) {\n element.focus();\n }\n\n if (named.safetyStatus === SAFETY_STATUS.UNLOCKING) {\n named.webAnimations.unlocking({\n element,\n });\n } else if (named.safetyStatus === SAFETY_STATUS.LOCKING) {\n named.webAnimations.locking({\n element,\n });\n }\n },\n { eager: false }\n);\n"],"names":["modifier","element","_","named","focusMe","focus","safetyStatus","SAFETY_STATUS","UNLOCKING","webAnimations","unlocking","LOCKING","locking","eager"],"mappings":";;;AAIA,gBAAeA,QAAQ,CACrB,CAACC,OAAO,EAAEC,CAAC,EAAEC,KAAK,KAAK;EACrB,IAAIA,KAAK,CAACC,OAAO,EAAE;IACjBH,OAAO,CAACI,KAAK,EAAE;AACjB,EAAA;AAEA,EAAA,IAAIF,KAAK,CAACG,YAAY,KAAKC,aAAa,CAACC,SAAS,EAAE;AAClDL,IAAAA,KAAK,CAACM,aAAa,CAACC,SAAS,CAAC;AAC5BT,MAAAA;AACF,KAAC,CAAC;EACJ,CAAC,MAAM,IAAIE,KAAK,CAACG,YAAY,KAAKC,aAAa,CAACI,OAAO,EAAE;AACvDR,IAAAA,KAAK,CAACM,aAAa,CAACG,OAAO,CAAC;AAC1BX,MAAAA;AACF,KAAC,CAAC;AACJ,EAAA;AACF,CAAC,EACD;AAAEY,EAAAA,KAAK,EAAE;AAAM,CACjB,CAAC;;;;"}
1
+ {"version":3,"file":"animate-me.js","sources":["../../src/modifiers/animate-me.js"],"sourcesContent":["import { modifier } from 'ember-modifier';\n\nimport { SAFETY_STATUS } from '../states.js';\n\nexport default modifier(\n (element, _, named) => {\n if (named.focusMe) {\n element.focus();\n }\n\n if (named.safetyStatus === SAFETY_STATUS.UNLOCKING) {\n named.webAnimations.unlocking({\n element,\n });\n } else if (named.safetyStatus === SAFETY_STATUS.LOCKING) {\n named.webAnimations.locking({\n element,\n });\n }\n },\n { eager: false },\n);\n"],"names":["modifier","element","_","named","focusMe","focus","safetyStatus","SAFETY_STATUS","UNLOCKING","webAnimations","unlocking","LOCKING","locking","eager"],"mappings":";;;AAIA,gBAAeA,QAAQ,CACrB,CAACC,OAAO,EAAEC,CAAC,EAAEC,KAAK,KAAK;EACrB,IAAIA,KAAK,CAACC,OAAO,EAAE;IACjBH,OAAO,CAACI,KAAK,EAAE;AACjB,EAAA;AAEA,EAAA,IAAIF,KAAK,CAACG,YAAY,KAAKC,aAAa,CAACC,SAAS,EAAE;AAClDL,IAAAA,KAAK,CAACM,aAAa,CAACC,SAAS,CAAC;AAC5BT,MAAAA;AACF,KAAC,CAAC;EACJ,CAAC,MAAM,IAAIE,KAAK,CAACG,YAAY,KAAKC,aAAa,CAACI,OAAO,EAAE;AACvDR,IAAAA,KAAK,CAACM,aAAa,CAACG,OAAO,CAAC;AAC1BX,MAAAA;AACF,KAAC,CAAC;AACJ,EAAA;AACF,CAAC,EACD;AAAEY,EAAAA,KAAK,EAAE;AAAM,CACjB,CAAC;;;;"}
package/package.json CHANGED
@@ -1,64 +1,90 @@
1
1
  {
2
2
  "name": "ember-safe-button",
3
- "version": "3.2.1",
3
+ "version": "4.0.0",
4
4
  "description": "For potentially dangerous actions that better needs a confirmation.",
5
+ "keywords": [
6
+ "ember-addon"
7
+ ],
5
8
  "repository": "https://gitlab.com/michal-bryxi/open-source/ember-safe-button",
6
9
  "license": "MIT",
7
10
  "author": "",
11
+ "imports": {
12
+ "#src/*": "./src/*"
13
+ },
8
14
  "exports": {
9
- ".": "./dist/index.js",
10
- "./*": "./dist/*",
11
- "./addon-main.js": "./addon-main.js"
15
+ ".": {
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./addon-main.js": "./addon-main.cjs",
19
+ "./*.css": "./dist/*.css",
20
+ "./*": {
21
+ "default": "./dist/*.js"
22
+ }
12
23
  },
13
24
  "files": [
14
- "dist",
15
25
  "addon-main.cjs",
16
- "CHANGELOG.md",
17
- "README.md"
26
+ "declarations",
27
+ "dist",
28
+ "src"
18
29
  ],
19
30
  "dependencies": {
20
- "@ember/render-modifiers": "2.1.0",
21
- "@embroider/addon-shim": "1.10.2",
22
- "ember-cli-htmlbars": "^6.2.0",
31
+ "@ember/render-modifiers": "3.0.0",
32
+ "@embroider/addon-shim": "^1.10.2",
33
+ "decorator-transforms": "^2.2.2",
23
34
  "ember-concurrency": "5.1.0",
24
- "ember-modifier": "4.1.0"
35
+ "ember-modifier": "4.2.2"
25
36
  },
26
37
  "devDependencies": {
27
- "@babel/core": "7.23.2",
28
- "@babel/plugin-proposal-class-properties": "7.18.6",
29
- "@babel/plugin-proposal-decorators": "7.23.2",
30
- "@babel/plugin-syntax-decorators": "7.22.10",
31
- "@embroider/addon-dev": "8.1.2",
32
- "@nullvoxpopuli/eslint-configs": "2.3.5",
33
- "@rollup/plugin-babel": "6.0.4",
34
- "babel-eslint": "10.1.0",
35
- "concurrently": "7.6.0",
36
- "eslint": "^7.0.0",
37
- "eslint-config-prettier": "8.5.0",
38
- "eslint-plugin-decorator-position": "4.0.1",
39
- "eslint-plugin-ember": "10.6.1",
40
- "eslint-plugin-import": "2.26.0",
41
- "eslint-plugin-json": "3.1.0",
42
- "eslint-plugin-node": "11.1.0",
43
- "@ember/string": "3.1.1",
44
- "eslint-plugin-prettier": "4.2.1",
45
- "webpack": "^5.88.1",
46
- "eslint-plugin-simple-import-sort": "7.0.0",
47
- "rollup": "4.54.0"
48
- },
49
- "publishConfig": {
50
- "registry": "https://registry.npmjs.org"
38
+ "@babel/core": "^7.25.2",
39
+ "@babel/eslint-parser": "^7.25.1",
40
+ "@babel/runtime": "^7.25.6",
41
+ "@ember/app-tsconfig": "^1.0.3",
42
+ "@ember/library-tsconfig": "^1.0.0",
43
+ "@ember/test-helpers": "^5.2.1",
44
+ "@ember/test-waiters": "^4.1.1",
45
+ "@embroider/addon-dev": "^8.1.0",
46
+ "@embroider/compat": "^4.1.0",
47
+ "@embroider/core": "^4.1.0",
48
+ "@embroider/macros": "^1.18.0",
49
+ "@embroider/vite": "^1.1.5",
50
+ "@eslint/js": "^9.17.0",
51
+ "@glimmer/component": "^2.0.0",
52
+ "@rollup/plugin-babel": "^6.0.4",
53
+ "@tailwindcss/vite": "^4.0.0",
54
+ "babel-plugin-ember-template-compilation": "^2.2.5",
55
+ "concurrently": "^9.0.1",
56
+ "ember-concurrency": "5.1.0",
57
+ "ember-modifier": "4.2.2",
58
+ "ember-page-title": "^9.0.3",
59
+ "ember-qunit": "^9.0.2",
60
+ "ember-source": "^6.7.0",
61
+ "ember-strict-application-resolver": "^0.1.0",
62
+ "ember-template-lint": "^7.9.0",
63
+ "eslint": "^9.17.0",
64
+ "eslint-config-prettier": "^10.1.5",
65
+ "eslint-plugin-ember": "^12.3.3",
66
+ "eslint-plugin-import": "^2.31.0",
67
+ "eslint-plugin-n": "^17.15.1",
68
+ "globals": "^16.1.0",
69
+ "netlify-cli": "^23.13.0",
70
+ "node-gyp": "^12.1.0",
71
+ "prettier": "^3.4.2",
72
+ "prettier-plugin-ember-template-tag": "^2.0.4",
73
+ "qunit": "^2.24.1",
74
+ "qunit-dom": "^3.4.0",
75
+ "rollup": "^4.22.5",
76
+ "sinon": "^21.0.1",
77
+ "tailwindcss": "^4.0.0",
78
+ "testem": "^3.15.1",
79
+ "vite": "^7.1.9"
51
80
  },
52
81
  "ember": {
53
82
  "edition": "octane"
54
83
  },
55
- "keywords": [
56
- "ember-addon"
57
- ],
58
84
  "ember-addon": {
59
85
  "version": 2,
60
86
  "type": "addon",
61
- "main": "./addon-main.cjs",
87
+ "main": "addon-main.cjs",
62
88
  "app-js": {
63
89
  "./components/safe-button/animations/flip.js": "./dist/_app_/components/safe-button/animations/flip.js",
64
90
  "./components/safe-button/animations/index.js": "./dist/_app_/components/safe-button/animations/index.js",
@@ -73,22 +99,18 @@
73
99
  "./modifiers/animate-me.js": "./dist/_app_/modifiers/animate-me.js"
74
100
  }
75
101
  },
76
- "engines": {
77
- "node": "12.* || 14.* || >= 16"
78
- },
79
- "volta": {
80
- "extends": "../package.json"
81
- },
82
102
  "scripts": {
83
- "start": "concurrently 'npm:watch:*'",
84
- "build": "concurrently 'npm:build:*'",
85
- "build:js": "rollup -c ./rollup.config.mjs",
86
- "build:docs": "cp ../README.md ./README.md",
87
- "watch:js": "rollup -c --watch --no-watch.clearScreen",
88
- "lint": "concurrently 'npm:lint:js'",
89
- "lint:fix": "concurrently 'npm:lint:js:fix'",
103
+ "build": "rollup --config",
104
+ "format": "prettier . --cache --write",
105
+ "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto",
106
+ "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\" --prefixColors auto && pnpm run format",
107
+ "lint:format": "prettier . --cache --check",
108
+ "lint:hbs": "ember-template-lint . --no-error-on-unmatched-pattern",
109
+ "lint:hbs:fix": "ember-template-lint . --fix --no-error-on-unmatched-pattern",
90
110
  "lint:js": "eslint . --cache",
91
111
  "lint:js:fix": "eslint . --fix",
92
- "test": "echo 'Addon does not have tests, run tests in test-app'"
112
+ "start": "vite dev",
113
+ "demo:build": "pnpm build && VITE_BUILD_TARGET=demo vite build --out-dir dist-demo --emptyOutDir",
114
+ "test": "vite build --mode=development --out-dir dist-tests && testem --file testem.cjs ci --port 0"
93
115
  }
94
116
  }