trek 0.1.16 → 0.1.18

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc939de6d6cc669e8faa27134dd868b9523bafa0236e1cf025673e0abb84d833
4
- data.tar.gz: 6abb5b7e49ee16d38619f6831d62dda2a05871b09a81f9ea8d44021254a91774
3
+ metadata.gz: 1ea15f7b7f4c1da74cdda11ad651158cd69856a5a04490e417ed90c80886cc0a
4
+ data.tar.gz: e6bc99bac1a371ee03737a217f9f621e430f8db8ebb4dcaeba5494e224608970
5
5
  SHA512:
6
- metadata.gz: 69f4c70a9471eb21966c377cb419e98a314ecac4aeac31dc1f78501e8241450870de557ab77eadc2acc11fa5532193b8db3646406b8dbdee7780355c1e255483
7
- data.tar.gz: 0c71c307f076c1c601ac37a1b016cc2508fcc78b56fbd114fd7769dce928581ff0daffb22c2f62c011e85fb6a9b8cf6445616c7d47b793538af516f15d01f781
6
+ metadata.gz: 2742e782830532e67f36d3affb40723858b5533cf798cc4ea729028cda798b1a447cef87690d15ae148882048489d811a9df4cce3394c5bb1ade7d1aede7e64f
7
+ data.tar.gz: 19a823e2bbdf69d076aa82ec75db839dfe78668faad9c07a63cf08066465899f15033099629dcdb7740c4d789e63297ee7294c6760ba04a28dd889c52859a05c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- trek (0.1.16)
4
+ trek (0.1.18)
5
5
  action_policy (~> 0.6)
6
6
  actioncable
7
7
  acts_as_list (~> 1.1)
data/README.md CHANGED
@@ -192,7 +192,7 @@ Then, whenever you change the files in Trek, you'll have to restart the Rails se
192
192
 
193
193
  When you're done, disable the local version:
194
194
 
195
- bundle config --delete local.trek
195
+ bundle config unset --local local.trek
196
196
 
197
197
  Don't forget to revert to a released verion in your `Gemfile`:
198
198
 
@@ -33,12 +33,12 @@ table .root {
33
33
  --icon-size: 1.6rem;
34
34
 
35
35
  align-items: center;
36
+ aspect-ratio: 16/9;
36
37
  background-color: var(--slate-3);
37
38
  border: 1px solid var(--slate-a3);
38
39
  border-radius: 0.2rem;
39
40
  color: var(--slate-8);
40
41
  display: flex;
41
- aspect-ratio: 16/9;
42
42
  justify-content: center;
43
43
  margin-right: 2rem;
44
44
  overflow: hidden;
@@ -9,6 +9,21 @@ module Trek
9
9
  renders_many :buttons, Trek::ButtonComponent
10
10
  renders_many :links, "LinkComponent"
11
11
 
12
+ def with_delete_button(object:, model_collection:)
13
+ return unless object.persisted? && helpers.allowed_to?(:destroy?, object)
14
+
15
+ with_button(
16
+ text: helpers.t("admin.actions.delete"),
17
+ to: [:admin, object],
18
+ method: :delete,
19
+ data: {
20
+ turbo_confirm: helpers.t("admin.#{model_collection}.destroy.confirm")
21
+ },
22
+ color: :tomato,
23
+ position: :right,
24
+ )
25
+ end
26
+
12
27
  def classes
13
28
  class_names_for(
14
29
  root_class,
@@ -211,28 +211,27 @@ div(
211
211
  class=class_for("floating-menu")
212
212
  data=stimulus_target_hash("floatingMenu")
213
213
  )
214
- / TODO: re-enable floating buttons
215
- / button(
216
- / class=class_for("button")
217
- / type="button"
218
- / data-action=stimulus_action("togglePrompts")
219
- / )
220
- / = render Trek::IconComponent.new( \
221
- / "trek/cursor",
222
- / classnames: class_for("button-icon"),
223
- / )
224
- / = t(".prompt")
225
-
226
- / button(
227
- / class=class_for("button")
228
- / type="button"
229
- / data-action=stimulus_action("toggleImage")
230
- / )
231
- / = render Trek::IconComponent.new( \
232
- / "trek/pic",
233
- / classnames: class_for("button-icon"),
234
- / )
235
- / = t(".image")
214
+ button(
215
+ class=class_for("button")
216
+ type="button"
217
+ data-action=stimulus_action("togglePrompts")
218
+ )
219
+ = render Trek::IconComponent.new( \
220
+ "trek/cursor",
221
+ classnames: class_for("button-icon"),
222
+ )
223
+ = t(".prompt")
224
+
225
+ button(
226
+ class=class_for("button")
227
+ type="button"
228
+ data-action=stimulus_action("toggleImage")
229
+ )
230
+ = render Trek::IconComponent.new( \
231
+ "trek/pic",
232
+ classnames: class_for("button-icon"),
233
+ )
234
+ = t(".image")
236
235
 
237
236
  = link_to "",
238
237
  new_prompt_path,
@@ -259,12 +259,66 @@ export class Controller extends BaseController {
259
259
 
260
260
  contentInserterTargetConnected(t) {
261
261
  const content = t.innerHTML;
262
- this.editor.commands.insertContent(content);
262
+
263
+ if (this._editingImagePos != null) {
264
+ // Update existing image block in place
265
+ const pos = this._editingImagePos;
266
+ this._editingImagePos = null;
267
+
268
+ const parser = new DOMParser();
269
+ const doc = parser.parseFromString(content, "text/html");
270
+ const imageBlock = doc.querySelector("image-block");
271
+
272
+ if (imageBlock) {
273
+ const attrs = {};
274
+ for (const attr of imageBlock.attributes) {
275
+ attrs[attr.name] = attr.value;
276
+ }
277
+ const tr = this.editor.state.tr;
278
+ const node = this.editor.state.doc.nodeAt(pos);
279
+ if (node && node.type.name === "imageBlock") {
280
+ tr.setNodeMarkup(pos, undefined, attrs);
281
+ this.editor.view.dispatch(tr);
282
+ } else {
283
+ this.editor.commands.insertContent(content);
284
+ }
285
+ } else {
286
+ this.editor.commands.insertContent(content);
287
+ }
288
+ } else {
289
+ this.editor.commands.insertContent(content);
290
+ }
291
+
263
292
  t.parentNode.removeChild(t);
264
293
  }
265
294
 
266
295
  toggleImage(e) {
267
- const params = e.target.closest("button").dataset.params;
296
+ const button = e.target.closest("button");
297
+ const params = button.dataset.params;
298
+
299
+ // Check if clicking an existing image block in the editor
300
+ const imageBlockEl = button.closest("image-block");
301
+ if (imageBlockEl) {
302
+ const pos = this.editor.view.posAtDOM(imageBlockEl, 0);
303
+ const resolved = this.editor.state.doc.resolve(pos);
304
+ // Walk up to find the imageBlock node position
305
+ for (let d = resolved.depth; d >= 0; d--) {
306
+ if (resolved.node(d).type.name === "imageBlock") {
307
+ this._editingImagePos = d > 0 ? resolved.before(d) : pos;
308
+ break;
309
+ }
310
+ }
311
+ // Fallback: the pos itself might point at the imageBlock
312
+ if (this._editingImagePos == null) {
313
+ const node = this.editor.state.doc.nodeAt(pos);
314
+ if (node && node.type.name === "imageBlock") {
315
+ this._editingImagePos = pos;
316
+ }
317
+ }
318
+ } else {
319
+ this._editingImagePos = null;
320
+ }
321
+
268
322
  const url = this.hiddenImageButtonTarget.dataset.basePath;
269
323
  this.hiddenImageButtonTarget.href = url + (params ? "&" + params : "");
270
324
  this.hiddenImageButtonTarget.click();
@@ -48,7 +48,14 @@ export default Node.create({
48
48
  },
49
49
 
50
50
  addNodeView() {
51
- return ({ node }) => {
51
+ return ({
52
+ editor,
53
+ node,
54
+ getPos,
55
+ HTMLAttributes,
56
+ decorations,
57
+ extension,
58
+ }) => {
52
59
  const dom = document.createElement("image-block");
53
60
  const params = attributesToParams(node.attrs);
54
61
 
@@ -45,7 +45,14 @@ export default Node.create({
45
45
  },
46
46
 
47
47
  addNodeView() {
48
- return ({ node }) => {
48
+ return ({
49
+ editor,
50
+ node,
51
+ getPos,
52
+ HTMLAttributes,
53
+ decorations,
54
+ extension,
55
+ }) => {
49
56
  const dom = document.createElement("prompts-block");
50
57
  const params = attributesToParams(node.attrs);
51
58
 
@@ -3,7 +3,6 @@
3
3
  module Trek
4
4
  class HeaderComponent < Trek::Component
5
5
  extend Dry::Initializer
6
- include Trek::PagePathHelper if defined?(Page)
7
6
  include ActionPolicy::Behaviour
8
7
 
9
8
  option :current_object, optional: true
@@ -2,6 +2,7 @@
2
2
  .root {
3
3
  align-items: center;
4
4
  display: flex;
5
+ margin-bottom: 2.4rem;
5
6
  }
6
7
 
7
8
  .label {
@@ -14,6 +15,7 @@
14
15
  border-radius: 0.8rem;
15
16
  display: flex;
16
17
  transition: border-color 0.2s ease-in-out;
18
+ width: 36rem;
17
19
  }
18
20
 
19
21
  .wrapper:focus-within {
@@ -25,8 +27,10 @@
25
27
  background: transparent;
26
28
  border: 0;
27
29
  color: var(--slate-11);
30
+ flex-grow: 1;
28
31
  font-size: 1.6rem;
29
32
  font-weight: var(--regular);
33
+ margin-right: 1rem;
30
34
  padding: 1rem 2rem;
31
35
  transition: color 0.2s ease-in-out;
32
36
  }
@@ -42,15 +46,14 @@
42
46
  appearance: none;
43
47
  background-color: transparent;
44
48
  border: none;
45
- border-radius: 0.8rem;
49
+ border-radius: 0 0.8rem 0.8rem 0;
46
50
  color: var(--slate-11);
47
51
  cursor: pointer;
48
52
  display: inline-flex;
49
53
  font-size: 1.6rem;
50
54
  font-weight: var(--regular);
51
55
  line-height: 1.25;
52
- margin-left: 1rem;
53
- padding: 1rem 2rem;
56
+ padding: 1rem 1.4rem;
54
57
  text-decoration: none;
55
58
  transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out,
56
59
  color 0.2s ease-in-out;
@@ -59,4 +62,26 @@
59
62
  .button:hover {
60
63
  background-color: var(--accent-3);
61
64
  border-color: var(--accent-5);
65
+ color: var(--accent-11);
66
+ }
67
+
68
+ .reset {
69
+ --icon-size: 1.6rem;
70
+
71
+ align-items: center;
72
+ appearance: none;
73
+ background-color: transparent;
74
+ border: none;
75
+ border-radius: 0;
76
+ color: var(--slate-11);
77
+ cursor: pointer;
78
+ display: inline-flex;
79
+ padding: 1rem 1.4rem;
80
+ text-decoration: none;
81
+ transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
82
+ }
83
+
84
+ .reset:hover {
85
+ background-color: var(--accent-3);
86
+ color: var(--accent-11);
62
87
  }
@@ -7,5 +7,8 @@
7
7
  value=value
8
8
  class=class_for("input")
9
9
  )
10
+ - if value.present?
11
+ = link_to action, class: class_for("reset"), title: t(".reset")
12
+ = render Trek::IconComponent.new("trek/close")
10
13
  button(class=class_for("button") type="submit" title=t(".search"))
11
14
  = render Trek::IconComponent.new("trek/search")
@@ -1,7 +1,6 @@
1
1
  module ProsemirrorToHtml
2
2
  class GlobalIdToLinksFormatter
3
3
  include Rails.application.routes.url_helpers
4
- include Trek::PagePathHelper
5
4
 
6
5
  GLOBAL_ID_REGEX = %r{(gid://[[:alnum:]]+/[\w:]+/[\w-]+)}
7
6
 
@@ -229,6 +229,7 @@ en:
229
229
  list_component:
230
230
  empty: No item.
231
231
  search_component:
232
+ reset: Reset search
232
233
  search: Search
233
234
  user:
234
235
  locale:
@@ -233,6 +233,7 @@ fr:
233
233
  list_component:
234
234
  empty: Aucun élément
235
235
  search_component:
236
+ reset: Réinitialiser la recherche
236
237
  search: Rechercher
237
238
  user:
238
239
  locale:
@@ -101,17 +101,5 @@
101
101
  text: t("admin.actions.validate"),
102
102
  color: :grass,
103
103
  )
104
- if @object.persisted? && allowed_to?(:destroy, @object)
105
- c.with_button(
106
- text: t("admin.actions.delete"),
107
- to: [:admin, @object],
108
- method: :delete,
109
- data: {
110
- turbo_confirm: t(
111
- "admin.#{model_collection}.destroy.confirm"
112
- )
113
- },
114
- color: :tomato,
115
- position: :right,
116
- )
117
- end
104
+
105
+ c.with_delete_button(object: @object, model_collection: model_collection)
@@ -20,3 +20,5 @@
20
20
  text: t("admin.actions.validate"),
21
21
  color: :grass,
22
22
  )
23
+
24
+ c.with_delete_button(object: @object, model_collection: model_collection)
@@ -48,13 +48,4 @@
48
48
  color: :grass,
49
49
  )
50
50
 
51
- if @object.persisted? && allowed_to?(:destroy?, @object)
52
- c.with_button(
53
- text: t("admin.actions.delete"),
54
- to: [:admin, @object],
55
- method: "delete",
56
- data: { turbo_confirm: t("admin.#{model_collection}.destroy.confirm") },
57
- color: :tomato,
58
- position: :right,
59
- )
60
- end
51
+ c.with_delete_button(object: @object, model_collection: model_collection)
@@ -1,16 +1,18 @@
1
1
  module Trek
2
- # `TypographyFormatter` formats strings to enforce typography rules.
3
- # For now, only French typography rules about non-breaking spaces
4
- # and ellipsis are implemented.
2
+ # `TypographyFormatter` formats strings to enforce French typography rules.
5
3
  #
6
4
  # Supports both plain strings and ProseMirror JSONB hashes. When given
7
5
  # a Hash, it walks the node tree and formats only `text` node values
8
6
  # in place, leaving the structure intact.
9
7
  class TypographyFormatter
10
8
  NBSP = [160].pack("U*").freeze
11
- NBSP_BEFORE_CHARS = %w[: ; % \$ °C °F » ! \? –].freeze
12
- NBSP_AFTER_CHARS = %w[«].freeze
13
- NBSP_BEFORE_UNITS = %w[g h ha kg km Ko ko kW L l m min ml Mo mo s].freeze
9
+ NNBSP = [8239].pack("U*").freeze # U+202F narrow no-break space
10
+ SPACE_BEFORE_CHARS = [
11
+ [NBSP, %w[: % \$ » ]],
12
+ [NNBSP, %w[; ! \?]]
13
+ ].freeze
14
+ NBSP_AFTER_CHARS = %w[« – —].freeze
15
+ NBSP_BEFORE_UNITS = %w[°C °F g h ha kg km Ko ko kW L l m min ml Mo mo s].freeze
14
16
 
15
17
  def initialize(input)
16
18
  @source = input
@@ -36,6 +38,8 @@ module Trek
36
38
  remove_double_spaces
37
39
  add_nbsp
38
40
  replace_ellipsis
41
+ replace_apostrophes
42
+ remove_spaces_before_attached_punctuation
39
43
  end
40
44
 
41
45
  def format_prosemirror_node(node)
@@ -62,15 +66,19 @@ module Trek
62
66
  end
63
67
 
64
68
  def add_nbsp
65
- add_nbsp_before_chars
69
+ add_nbsp_after_numero
66
70
  add_nbsp_before_units
71
+ add_nbsp_before_degree
72
+ add_space_before_chars
67
73
  add_nbsp_after_chars
68
74
  add_nbsp_between_digits
69
75
  end
70
76
 
71
- def add_nbsp_before_chars
72
- regexp = /(?<=\S)(\p{Zs}?(#{NBSP_BEFORE_CHARS.join("|")}))/
73
- @output.gsub!(regexp, "#{NBSP}\\2")
77
+ def add_space_before_chars
78
+ SPACE_BEFORE_CHARS.each do |space, chars|
79
+ regexp = /(?<=\S)(\p{Zs}?(#{chars.join("|")}))/
80
+ @output.gsub!(regexp, "#{space}\\2")
81
+ end
74
82
  end
75
83
 
76
84
  def add_nbsp_before_units
@@ -79,10 +87,18 @@ module Trek
79
87
  end
80
88
 
81
89
  def add_nbsp_after_chars
82
- regexp = /((#{NBSP_AFTER_CHARS.join("|")})\p{Zs}?)\b/
90
+ regexp = /((#{NBSP_AFTER_CHARS.join("|")})\p{Zs}?)(?=\w)/
83
91
  @output.gsub!(regexp, "\\2#{NBSP}")
84
92
  end
85
93
 
94
+ def add_nbsp_before_degree
95
+ @output.gsub!(/(\d)\p{Zs}?(°)(?!\w)/, "\\1#{NBSP}\\2")
96
+ end
97
+
98
+ def add_nbsp_after_numero
99
+ @output.gsub!(/(?i)(n°)\p{Zs}?(?=\d)/, "\\1#{NBSP}")
100
+ end
101
+
86
102
  def add_nbsp_between_digits
87
103
  regexp = /(\d)\p{Zs}(\d)/
88
104
  @output.gsub!(regexp, "\\1#{NBSP}\\2")
@@ -91,5 +107,13 @@ module Trek
91
107
  def replace_ellipsis
92
108
  @output.gsub!("...", "…")
93
109
  end
110
+
111
+ def replace_apostrophes
112
+ @output.gsub!(/(?<=\w)'(?=\w)/, "\u2019")
113
+ end
114
+
115
+ def remove_spaces_before_attached_punctuation
116
+ @output.gsub!(/\p{Zs}+([.,])/, "\\1")
117
+ end
94
118
  end
95
119
  end
data/lib/trek/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Trek
4
- VERSION = "0.1.16"
4
+ VERSION = "0.1.18"
5
5
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etaminstudio/trek",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "A modern CMS for Ruby on Rails",
5
5
  "main": "app/javascript/trek.js",
6
6
  "repository": {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trek
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.16
4
+ version: 0.1.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohamed Bengrich