coveragebook_components 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/build/coco/app.css +27 -20
  3. data/app/assets/build/coco/app.js +136 -309
  4. data/app/assets/build/coco/book.css +11 -7
  5. data/app/assets/build/coco/book.js +143 -185
  6. data/app/assets/js/coco.js +2 -30
  7. data/app/assets/js/{base/mixins → libs/alpine/directives}/undo.js +12 -13
  8. data/app/assets/js/libs/alpine/index.js +15 -13
  9. data/app/components/coco/app/blocks/slide_editor/slide_editor.html.erb +2 -1
  10. data/app/components/coco/app/blocks/slide_editor/slide_editor.js +0 -3
  11. data/app/components/coco/app/elements/{form_button/form_button.css → button_to/button_to.css} +1 -1
  12. data/app/components/coco/app/elements/{form_button/form_button.rb → button_to/button_to.rb} +1 -1
  13. data/app/components/coco/app/elements/color_picker/color_picker.js +0 -2
  14. data/app/components/coco/app/elements/image_picker/image_picker.js +0 -2
  15. data/app/components/coco/app/elements/link/link.css +11 -1
  16. data/app/components/coco/app/elements/link/link.rb +1 -0
  17. data/app/components/coco/app/elements/menu/menu.css +1 -1
  18. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.html.erb +2 -1
  19. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.js +1 -7
  20. data/app/components/coco/app/elements/snackbar/snackbar.js +0 -2
  21. data/app/components/coco/app/elements/toast/toast.css +1 -1
  22. data/app/components/coco/app/elements/toast/toast.js +0 -2
  23. data/app/components/coco/base/button/button.js +0 -2
  24. data/app/components/coco/base/button/button.rb +3 -9
  25. data/app/components/coco/base/dropdown/dropdown.js +1 -3
  26. data/app/components/coco/base/link/link.css +11 -11
  27. data/app/components/coco/base/link/link.rb +1 -5
  28. data/app/components/coco/base/modal_dialog/modal_dialog.css +1 -1
  29. data/app/components/coco/concerns/acts_as_button_group.rb +1 -1
  30. data/app/helpers/coco/app_helper.rb +60 -37
  31. data/app/helpers/coco/base_helper.rb +2 -2
  32. data/app/helpers/coco/url_helper.rb +6 -12
  33. data/lib/coco.rb +1 -1
  34. metadata +12 -21
  35. data/app/assets/js/app/mixins/dropdown.js +0 -18
  36. data/app/assets/js/app/mixins/index.js +0 -3
  37. data/app/assets/js/base/mixins/attr-observer.js +0 -54
  38. data/app/assets/js/base/mixins/attrs.js +0 -58
  39. data/app/assets/js/base/mixins/dropdown.js +0 -69
  40. data/app/assets/js/base/mixins/index.js +0 -17
  41. data/app/assets/js/base/mixins/options.js +0 -76
  42. data/app/assets/js/base/mixins/size-observer.js +0 -34
  43. data/app/assets/js/base/mixins/tooltip.js +0 -81
  44. /data/app/assets/js/libs/alpine/{plugins → directives}/destroy.js +0 -0
  45. /data/app/assets/js/libs/alpine/{plugins → directives}/dimensions.js +0 -0
  46. /data/app/assets/js/libs/alpine/{plugins → directives}/dropdown.js +0 -0
  47. /data/app/assets/js/libs/alpine/{plugins → directives}/notification.js +0 -0
  48. /data/app/assets/js/libs/alpine/{plugins → directives}/options.js +0 -0
  49. /data/app/assets/js/libs/alpine/{plugins → directives}/tooltip.js +0 -0
  50. /data/app/components/coco/app/elements/{form_button/form_button.html.erb → button_to/button_to.html.erb} +0 -0
@@ -4591,7 +4591,7 @@
4591
4591
  }
4592
4592
  return baseRandom(lower, upper);
4593
4593
  }
4594
- var camelCase6 = createCompounder(function(result2, word, index) {
4594
+ var camelCase5 = createCompounder(function(result2, word, index) {
4595
4595
  word = word.toLowerCase();
4596
4596
  return result2 + (index ? capitalize2(word) : word);
4597
4597
  });
@@ -4619,7 +4619,7 @@
4619
4619
  string = toString(string);
4620
4620
  return string && reHasRegExpChar.test(string) ? string.replace(reRegExpChar, "\\$&") : string;
4621
4621
  }
4622
- var kebabCase5 = createCompounder(function(result2, word, index) {
4622
+ var kebabCase4 = createCompounder(function(result2, word, index) {
4623
4623
  return result2 + (index ? "-" : "") + word.toLowerCase();
4624
4624
  });
4625
4625
  var lowerCase = createCompounder(function(result2, word, index) {
@@ -5203,7 +5203,7 @@
5203
5203
  mixin(lodash, lodash);
5204
5204
  lodash.add = add2;
5205
5205
  lodash.attempt = attempt;
5206
- lodash.camelCase = camelCase6;
5206
+ lodash.camelCase = camelCase5;
5207
5207
  lodash.capitalize = capitalize2;
5208
5208
  lodash.ceil = ceil;
5209
5209
  lodash.clamp = clamp;
@@ -5282,7 +5282,7 @@
5282
5282
  lodash.isWeakMap = isWeakMap;
5283
5283
  lodash.isWeakSet = isWeakSet;
5284
5284
  lodash.join = join;
5285
- lodash.kebabCase = kebabCase5;
5285
+ lodash.kebabCase = kebabCase4;
5286
5286
  lodash.last = last;
5287
5287
  lodash.lastIndexOf = lastIndexOf;
5288
5288
  lodash.lowerCase = lowerCase;
@@ -11474,8 +11474,25 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
11474
11474
  }
11475
11475
  var M2 = l2;
11476
11476
 
11477
- // libs/alpine/plugins/options.js
11478
- var import_lodash = __toESM(require_lodash(), 1);
11477
+ // helpers/alpine.js
11478
+ function registerComponents(components) {
11479
+ document.addEventListener("alpine:init", () => {
11480
+ components.forEach((module) => {
11481
+ if (module.default && module.default.component === true) {
11482
+ Alpine.data(module.default.name, module.default);
11483
+ }
11484
+ });
11485
+ });
11486
+ return components;
11487
+ }
11488
+ function getData(el) {
11489
+ const root = Alpine.closestRoot(el);
11490
+ return root ? Alpine.$data(root) : null;
11491
+ }
11492
+ function setData(el, newData) {
11493
+ const data2 = getData(el);
11494
+ return data2 ? Object.assign(data2, newData) : null;
11495
+ }
11479
11496
 
11480
11497
  // helpers/lang.js
11481
11498
  function nameFunction(name, body) {
@@ -11494,6 +11511,100 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
11494
11511
  return !isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
11495
11512
  !isNaN(parseFloat(str));
11496
11513
  }
11514
+ function roughSizeOfObject(object) {
11515
+ const objectList = [];
11516
+ const stack = [object];
11517
+ let bytes = 0;
11518
+ while (stack.length) {
11519
+ const value = stack.pop();
11520
+ if (typeof value === "boolean") {
11521
+ bytes += 4;
11522
+ } else if (typeof value === "string") {
11523
+ bytes += value.length * 2;
11524
+ } else if (typeof value === "number") {
11525
+ bytes += 8;
11526
+ } else if (typeof value === "object" && objectList.indexOf(value) === -1) {
11527
+ objectList.push(value);
11528
+ for (var i2 in value) {
11529
+ stack.push(value[i2]);
11530
+ }
11531
+ }
11532
+ }
11533
+ return bytes;
11534
+ }
11535
+
11536
+ // libs/alpine/directives/undo.js
11537
+ function undo_default(Alpine3) {
11538
+ const maxHistorySize = 100;
11539
+ Alpine3.directive("undo", (el, { expression }, { evaluate: evaluate2 }) => {
11540
+ const data2 = getData(el);
11541
+ const history = Alpine3.reactive({
11542
+ stack: [],
11543
+ stackPos: -1,
11544
+ adding: false,
11545
+ clear() {
11546
+ history.stack.length = 0;
11547
+ history.stackPos = -1;
11548
+ },
11549
+ add(name, newValue, oldValue) {
11550
+ if (!history.adding && newValue !== oldValue) {
11551
+ if (history.stackPos < history.stackSize - 1) {
11552
+ const stack = Alpine3.raw(history.stack);
11553
+ history.stack = stack.slice(0, history.stackPos + 1);
11554
+ }
11555
+ history.stack.push({ name, newValue, oldValue });
11556
+ if (history.stackSize > maxHistorySize) {
11557
+ history.stack.pop();
11558
+ } else {
11559
+ history.stackPos++;
11560
+ }
11561
+ }
11562
+ },
11563
+ undo() {
11564
+ if (!data2.undo) {
11565
+ console.error("Missing `undo` method");
11566
+ return;
11567
+ }
11568
+ if (history.undoable) {
11569
+ history.adding = true;
11570
+ const entry = history.stack[history.stackPos];
11571
+ data2.undo(entry.name, entry.oldValue);
11572
+ history.stackPos--;
11573
+ this.$nextTick(() => history.adding = false);
11574
+ }
11575
+ },
11576
+ redo() {
11577
+ if (!data2.redo) {
11578
+ console.error("Missing `redo` method");
11579
+ return;
11580
+ }
11581
+ if (history.redoable) {
11582
+ history.adding = true;
11583
+ history.stackPos++;
11584
+ const entry = history.stack[history.stackPos];
11585
+ data2.redo(entry.name, entry.newValue);
11586
+ this.$nextTick(() => history.adding = false);
11587
+ }
11588
+ },
11589
+ get undoable() {
11590
+ return history.stackPos >= 0;
11591
+ },
11592
+ get redoable() {
11593
+ return history.stackPos < history.stackSize - 1;
11594
+ },
11595
+ get stackSize() {
11596
+ return history.stack.length;
11597
+ },
11598
+ get stackMemoryUsage() {
11599
+ return roughSizeOfObject(history.stack);
11600
+ }
11601
+ });
11602
+ setData(el, { history });
11603
+ });
11604
+ }
11605
+
11606
+ // libs/alpine/directives/options.js
11607
+ var import_lodash = __toESM(require_lodash(), 1);
11497
11608
 
11498
11609
  // helpers/dom.js
11499
11610
  function castAttributeValue(value) {
@@ -11512,27 +11623,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
11512
11623
  return typeof Node === "object" ? o2 instanceof Node : o2 && typeof o2 === "object" && typeof o2.nodeType === "number" && typeof o2.nodeName === "string";
11513
11624
  }
11514
11625
 
11515
- // helpers/alpine.js
11516
- function registerComponents(components) {
11517
- document.addEventListener("alpine:init", () => {
11518
- components.forEach((module) => {
11519
- if (module.default && module.default.component === true) {
11520
- Alpine.data(module.default.name, module.default);
11521
- }
11522
- });
11523
- });
11524
- return components;
11525
- }
11526
- function getData(el) {
11527
- const root = Alpine.closestRoot(el);
11528
- return root ? Alpine.$data(root) : null;
11529
- }
11530
- function setData(el, newData) {
11531
- const data2 = getData(el);
11532
- return data2 ? Object.assign(data2, newData) : null;
11533
- }
11534
-
11535
- // libs/alpine/plugins/options.js
11626
+ // libs/alpine/directives/options.js
11536
11627
  function options_default(Alpine3) {
11537
11628
  Alpine3.directive(
11538
11629
  "options",
@@ -14541,7 +14632,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
14541
14632
  return config;
14542
14633
  }
14543
14634
 
14544
- // libs/alpine/plugins/tooltip.js
14635
+ // libs/alpine/directives/tooltip.js
14545
14636
  function tooltip_default(Alpine3) {
14546
14637
  Alpine3.directive(
14547
14638
  "tooltip",
@@ -14597,7 +14688,18 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
14597
14688
  ).before("bind");
14598
14689
  }
14599
14690
 
14600
- // libs/alpine/plugins/dropdown.js
14691
+ // libs/alpine/directives/destroy.js
14692
+ function destroy_default(Alpine3) {
14693
+ Alpine3.directive(
14694
+ "destroy",
14695
+ (el, { expression }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
14696
+ const clean2 = evaluateLater2(expression);
14697
+ cleanup2(() => clean2());
14698
+ }
14699
+ );
14700
+ }
14701
+
14702
+ // libs/alpine/directives/dropdown.js
14601
14703
  function dropdown_default(Alpine3) {
14602
14704
  Alpine3.directive(
14603
14705
  "dropdown",
@@ -14657,18 +14759,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
14657
14759
  ).before("bind");
14658
14760
  }
14659
14761
 
14660
- // libs/alpine/plugins/destroy.js
14661
- function destroy_default(Alpine3) {
14662
- Alpine3.directive(
14663
- "destroy",
14664
- (el, { expression }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
14665
- const clean2 = evaluateLater2(expression);
14666
- cleanup2(() => clean2());
14667
- }
14668
- );
14669
- }
14670
-
14671
- // libs/alpine/plugins/dimensions.js
14762
+ // libs/alpine/directives/dimensions.js
14672
14763
  function dimensions_default(Alpine3) {
14673
14764
  Alpine3.directive(
14674
14765
  "dimensions",
@@ -14705,7 +14796,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
14705
14796
  );
14706
14797
  }
14707
14798
 
14708
- // libs/alpine/plugins/notification.js
14799
+ // libs/alpine/directives/notification.js
14709
14800
  function notification_default(Alpine3) {
14710
14801
  let notificationId = 0;
14711
14802
  let queuePosition = 0;
@@ -14781,19 +14872,20 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
14781
14872
  }
14782
14873
 
14783
14874
  // libs/alpine/index.js
14784
- module_default.plugin(module_default2);
14875
+ window.Alpine = module_default;
14876
+ module_default.plugin(M2);
14785
14877
  module_default.plugin(module_default5);
14878
+ module_default.plugin(module_default6);
14879
+ module_default.plugin(module_default2);
14786
14880
  module_default.plugin(module_default3);
14787
14881
  module_default.plugin(module_default4);
14788
- module_default.plugin(module_default6);
14789
- module_default.plugin(M2);
14882
+ module_default.plugin(undo_default);
14790
14883
  module_default.plugin(options_default);
14791
14884
  module_default.plugin(tooltip_default);
14792
- module_default.plugin(dropdown_default);
14793
14885
  module_default.plugin(destroy_default);
14886
+ module_default.plugin(dropdown_default);
14794
14887
  module_default.plugin(dimensions_default);
14795
14888
  module_default.plugin(notification_default);
14796
- window.Alpine = module_default;
14797
14889
  var alpine_default2 = module_default;
14798
14890
 
14799
14891
  // ../../components/coco/base/button/button.js
@@ -14802,117 +14894,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
14802
14894
  default: () => button_default
14803
14895
  });
14804
14896
 
14805
- // base/mixins/attrs.js
14806
- function withAttrs(props = {}) {
14807
- return function(component) {
14808
- return Object.assign(
14809
- component,
14810
- Alpine.reactive({
14811
- getAttr(name) {
14812
- return this.$root.getAttribute(name);
14813
- },
14814
- setAttr(name, value) {
14815
- this.$root.setAttribute(name, value);
14816
- return this;
14817
- },
14818
- hasAttr(name) {
14819
- return this.$root.hasAttribute(name);
14820
- },
14821
- removeAttr(name) {
14822
- component.$root.removeAttribute(name);
14823
- return this;
14824
- },
14825
- assertAttr(name, testValue) {
14826
- return this.$root.getAttribute(name) === String(testValue);
14827
- },
14828
- refuteAttr(name, testValue) {
14829
- return !this.assertAttr(name, testValue);
14830
- },
14831
- getData(name) {
14832
- return this.getAttr(`data-${name}`);
14833
- },
14834
- setData(name, value) {
14835
- return this.setAttr(`data-${name}`, value);
14836
- },
14837
- hasData(name) {
14838
- return this.hasAttr(`data-${name}`);
14839
- },
14840
- removeData(name) {
14841
- return this.removeAttr(`data-${name}`);
14842
- },
14843
- assertData(name, testValue) {
14844
- return this.assertAttr(`data-${name}`, testValue);
14845
- },
14846
- refuteData(name, testValue) {
14847
- return !this.assertData(name, testValue);
14848
- }
14849
- })
14850
- );
14851
- };
14852
- }
14853
-
14854
- // base/mixins/options.js
14855
- var import_lodash2 = __toESM(require_lodash(), 1);
14856
- function withOptions(props = {}) {
14857
- return function(component) {
14858
- if (!component.options) {
14859
- return component;
14860
- }
14861
- const el = component.$root;
14862
- const oldDestroy = component.destroy;
14863
- const optionsProps = component.options || {};
14864
- const optionsAttrs = Object.keys(optionsProps).map(
14865
- (name) => `data-${(0, import_lodash2.kebabCase)(name)}`
14866
- );
14867
- Object.keys(optionsProps).forEach((name) => {
14868
- const attrName = `data-${(0, import_lodash2.kebabCase)(name)}`;
14869
- if (el.hasAttribute(attrName)) {
14870
- component.options[name] = el.getAttribute(attrName);
14871
- }
14872
- });
14873
- let attrObserver = new MutationObserver((mutationsList) => {
14874
- for (const mutation of mutationsList) {
14875
- if (mutation.type !== "attributes" || !optionsAttrs.includes(mutation.attributeName)) {
14876
- return;
14877
- }
14878
- const propName = (0, import_lodash2.camelCase)(mutation.attributeName.replace("data-", ""));
14879
- let value = mutation.target.getAttribute(mutation.attributeName);
14880
- switch (value) {
14881
- case "true":
14882
- value = true;
14883
- break;
14884
- case "false":
14885
- value = false;
14886
- break;
14887
- }
14888
- component.options[propName] = value;
14889
- }
14890
- });
14891
- Object.assign(
14892
- component,
14893
- Alpine.reactive({
14894
- destroy() {
14895
- attrObserver.disconnect();
14896
- attrObserver = null;
14897
- if (oldDestroy) {
14898
- oldDestroy.call(this);
14899
- }
14900
- }
14901
- })
14902
- );
14903
- attrObserver.observe(el, { attributes: true });
14904
- component.$watch("options", (options, oldOptions) => {
14905
- for (const [key, value] of Object.entries(options)) {
14906
- el.setAttribute(`data-${(0, import_lodash2.kebabCase)(key)}`, value);
14907
- if (component.onOptionChange) {
14908
- component.onOptionChange(key, value);
14909
- }
14910
- }
14911
- });
14912
- return component;
14913
- };
14914
- }
14915
-
14916
14897
  // coco.js
14917
14898
  function CocoComponent(name, fn3) {
14918
14899
  const func = nameFunction(name, (...args) => {
@@ -14925,37 +14906,16 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
14925
14906
  }
14926
14907
  });
14927
14908
  data2.$options = {};
14928
- if (data2.use === false)
14929
- return data2;
14930
- const originalInit = data2.init;
14931
- const mixins = [withAttrs(), withOptions(), ...data2.use || []];
14932
- mixins.forEach((mixin) => {
14933
- if (mixin.props) {
14934
- mixin.props.forEach((prop) => {
14935
- if (!data2[prop]) {
14936
- data2[prop] = null;
14937
- }
14938
- });
14939
- }
14940
- });
14941
- return Object.assign(data2, {
14942
- init() {
14943
- mixins.forEach((mixin) => mixin(this));
14944
- if (originalInit) {
14945
- originalInit.call(this);
14946
- }
14947
- }
14948
- });
14909
+ return data2;
14949
14910
  });
14950
14911
  func.component = true;
14951
14912
  return func;
14952
14913
  }
14953
14914
 
14954
14915
  // ../../components/coco/base/button/button.js
14955
- var import_lodash3 = __toESM(require_lodash(), 1);
14916
+ var import_lodash2 = __toESM(require_lodash(), 1);
14956
14917
  var button_default = CocoComponent("button", (data2 = {}) => {
14957
14918
  return __spreadProps(__spreadValues({
14958
- use: false,
14959
14919
  options: ["state", "confirm", "size", "disabled", "collapsible"],
14960
14920
  isCollapsed: false,
14961
14921
  approving: false,
@@ -15053,7 +15013,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
15053
15013
  },
15054
15014
  setState(name) {
15055
15015
  this.lastState = this.state;
15056
- this.$options.state = (0, import_lodash3.camelCase)(name);
15016
+ this.$options.state = (0, import_lodash2.camelCase)(name);
15057
15017
  },
15058
15018
  resetState() {
15059
15019
  this.$options.state = this.lastState || "default";
@@ -15107,9 +15067,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
15107
15067
  default: () => dropdown_default2
15108
15068
  });
15109
15069
  var dropdown_default2 = CocoComponent("dropdown", () => {
15110
- return {
15111
- use: false
15112
- };
15070
+ return {};
15113
15071
  });
15114
15072
 
15115
15073
  // ../../components/coco/base/icon/icon.js
@@ -1,7 +1,5 @@
1
1
  import { getData } from "@helpers/alpine";
2
-
3
- import { nameFunction } from "@helpers/lang.js";
4
- import { withAttrs, withOptions } from "@js/base/mixins";
2
+ import { nameFunction } from "@helpers/lang";
5
3
 
6
4
  function CocoComponent(name, fn) {
7
5
  const func = nameFunction(name, (...args) => {
@@ -17,36 +15,10 @@ function CocoComponent(name, fn) {
17
15
 
18
16
  data.$options = {};
19
17
 
20
- if (data.use === false) return data;
21
-
22
- // TODO - remove all mixin code below once directives are ready
23
-
24
- const originalInit = data.init;
25
- const mixins = [withAttrs(), withOptions(), ...(data.use || [])];
26
-
27
- mixins.forEach((mixin) => {
28
- if (mixin.props) {
29
- mixin.props.forEach((prop) => {
30
- if (!data[prop]) {
31
- data[prop] = null;
32
- }
33
- });
34
- }
35
- });
36
-
37
- return Object.assign(data, {
38
- init() {
39
- mixins.forEach((mixin) => mixin(this));
40
-
41
- if (originalInit) {
42
- originalInit.call(this);
43
- }
44
- },
45
- });
18
+ return data;
46
19
  });
47
20
 
48
21
  func.component = true;
49
-
50
22
  return func;
51
23
  }
52
24
 
@@ -1,8 +1,11 @@
1
+ import { getData, setData } from "@helpers/alpine";
1
2
  import { roughSizeOfObject } from "@helpers/lang";
2
3
 
3
- export default function withUndo(props = {}) {
4
- function withUndoMixin(component) {
5
- const maxHistorySize = props.maxEntries || 100;
4
+ export default function (Alpine) {
5
+ const maxHistorySize = 100;
6
+
7
+ Alpine.directive("undo", (el, { expression }, { evaluate }) => {
8
+ const data = getData(el);
6
9
 
7
10
  const history = Alpine.reactive({
8
11
  stack: [],
@@ -34,7 +37,7 @@ export default function withUndo(props = {}) {
34
37
  },
35
38
 
36
39
  undo() {
37
- if (!component.undo) {
40
+ if (!data.undo) {
38
41
  console.error("Missing `undo` method");
39
42
  return;
40
43
  }
@@ -42,14 +45,14 @@ export default function withUndo(props = {}) {
42
45
  if (history.undoable) {
43
46
  history.adding = true;
44
47
  const entry = history.stack[history.stackPos];
45
- component.undo(entry.name, entry.oldValue);
48
+ data.undo(entry.name, entry.oldValue);
46
49
  history.stackPos--;
47
50
  this.$nextTick(() => (history.adding = false));
48
51
  }
49
52
  },
50
53
 
51
54
  redo() {
52
- if (!component.redo) {
55
+ if (!data.redo) {
53
56
  console.error("Missing `redo` method");
54
57
  return;
55
58
  }
@@ -58,7 +61,7 @@ export default function withUndo(props = {}) {
58
61
  history.adding = true;
59
62
  history.stackPos++;
60
63
  const entry = history.stack[history.stackPos];
61
- component.redo(entry.name, entry.newValue);
64
+ data.redo(entry.name, entry.newValue);
62
65
  this.$nextTick(() => (history.adding = false));
63
66
  }
64
67
  },
@@ -80,10 +83,6 @@ export default function withUndo(props = {}) {
80
83
  },
81
84
  });
82
85
 
83
- return Object.assign(component, { history });
84
- }
85
-
86
- withUndoMixin.props = ["history"];
87
-
88
- return withUndoMixin;
86
+ setData(el, { history });
87
+ });
89
88
  }
@@ -7,31 +7,33 @@ import mask from "@alpinejs/mask";
7
7
  import focus from "@alpinejs/focus";
8
8
  import tash from "alpinejs-tash";
9
9
 
10
- import options from "@js/libs/alpine/plugins/options";
11
- import tooltip from "@js/libs/alpine/plugins/tooltip";
12
- import dropdown from "@js/libs/alpine/plugins/dropdown";
13
- import destroy from "@js/libs/alpine/plugins/destroy";
14
- import dimensions from "@js/libs/alpine/plugins/dimensions";
15
- import notification from "@js/libs/alpine/plugins/notification";
10
+ import undo from "@assets/js/libs/alpine/directives/undo";
11
+ import options from "@assets/js/libs/alpine/directives/options";
12
+ import tooltip from "@assets/js/libs/alpine/directives/tooltip";
13
+ import destroy from "@assets/js/libs/alpine/directives/destroy";
14
+ import dropdown from "@assets/js/libs/alpine/directives/dropdown";
15
+ import dimensions from "@assets/js/libs/alpine/directives/dimensions";
16
+ import notification from "@assets/js/libs/alpine/directives/notification";
17
+
18
+ window.Alpine = Alpine;
16
19
 
17
20
  // Third party plugins
18
21
 
19
- Alpine.plugin(morph);
22
+ Alpine.plugin(tash);
20
23
  Alpine.plugin(mask);
24
+ Alpine.plugin(focus);
25
+ Alpine.plugin(morph);
21
26
  Alpine.plugin(collapse);
22
27
  Alpine.plugin(intersect);
23
- Alpine.plugin(focus);
24
- Alpine.plugin(tash);
25
28
 
26
- // Bespoke plugins
29
+ // Coco directives
27
30
 
31
+ Alpine.plugin(undo);
28
32
  Alpine.plugin(options);
29
33
  Alpine.plugin(tooltip);
30
- Alpine.plugin(dropdown);
31
34
  Alpine.plugin(destroy);
35
+ Alpine.plugin(dropdown);
32
36
  Alpine.plugin(dimensions);
33
37
  Alpine.plugin(notification);
34
38
 
35
- window.Alpine = Alpine;
36
-
37
39
  export default Alpine;
@@ -1,6 +1,7 @@
1
1
  <%= render component_tag(x: {
2
2
  data: x_data("appSlideEditor", alpine_props),
3
- ":class": "{ready}"
3
+ ":class": "{ready}",
4
+ "undo": true
4
5
  }) do %>
5
6
  <div class="editor-toolbar">
6
7
  <%= coco_toolbar do |toolbar| %>
@@ -3,7 +3,6 @@ import { getData } from "@helpers/alpine";
3
3
  import { captureElementScreenshot } from "@helpers/screenshot";
4
4
  import { isDark } from "@helpers/color";
5
5
  import { wasSuccessful } from "@helpers/turbo_events";
6
- import { withUndo } from "@js/base/mixins";
7
6
 
8
7
  export default CocoComponent("appSlideEditor", (data) => {
9
8
  const initialData = {
@@ -28,8 +27,6 @@ export default CocoComponent("appSlideEditor", (data) => {
28
27
  };
29
28
 
30
29
  return {
31
- use: [withUndo()],
32
-
33
30
  ...initialData,
34
31
 
35
32
  saved: { ...initialData },
@@ -1,5 +1,5 @@
1
1
  @layer components {
2
- [data-coco][data-component="app-form-button"] {
2
+ [data-coco][data-component="app-button-to"] {
3
3
  @apply inline-flex;
4
4
  width: max-content;
5
5
 
@@ -1,7 +1,7 @@
1
1
  module Coco
2
2
  module App
3
3
  module Elements
4
- class FormButton < Coco::Component
4
+ class ButtonTo < Coco::Component
5
5
  include Concerns::WrapsComponent
6
6
 
7
7
  wraps_component :button do |args|
@@ -5,8 +5,6 @@ import { getComponent } from "@helpers/alpine";
5
5
 
6
6
  export default CocoComponent("appColorPicker", ({ selected }) => {
7
7
  return {
8
- use: false,
9
-
10
8
  selectedColor: selected,
11
9
  display: selected,
12
10
  updating: false,
@@ -4,8 +4,6 @@ import { basename } from "@helpers/path";
4
4
 
5
5
  export default CocoComponent("appImagePicker", ({ src }) => {
6
6
  return {
7
- use: [],
8
-
9
7
  image: {
10
8
  name: basename(src),
11
9
  file: null,
@@ -1,6 +1,16 @@
1
1
  @layer components {
2
2
  [data-coco][data-component="app-link"] {
3
- @apply no-underline hover:underline;
3
+ &[data-theme] {
4
+ @apply no-underline hover:underline;
5
+ }
6
+
7
+ &[data-underline="true"] {
8
+ @apply underline;
9
+ }
10
+
11
+ &[data-underline="false"] {
12
+ @apply no-underline;
13
+ }
4
14
 
5
15
  &[data-theme="primary"] {
6
16
  @apply app-link-primary;