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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/app/components/trek/entries/item_component/item_component.css +1 -1
- data/app/components/trek/form/actions_component.rb +15 -0
- data/app/components/trek/form/content_editor_component/content_editor_component.html.slim +21 -22
- data/app/components/trek/form/content_editor_component/content_editor_component.js +56 -2
- data/app/components/trek/form/content_editor_component/extensions/image_block.js +8 -1
- data/app/components/trek/form/content_editor_component/extensions/prompts_block.js +8 -1
- data/app/components/trek/header_component.rb +0 -1
- data/app/components/trek/search_component/search_component.css +28 -3
- data/app/components/trek/search_component/search_component.html.slim +3 -0
- data/app/formatters/prosemirror_to_html/global_id_to_links_formatter.rb +0 -1
- data/config/locales/trek.en.yml +1 -0
- data/config/locales/trek.fr.yml +1 -0
- data/lib/generators/trek/templates/views/admin/pages/_form.html.slim +2 -14
- data/lib/generators/trek/templates/views/admin/scaffold/_form.html.slim.tt +2 -0
- data/lib/generators/trek/templates/views/admin/users/_form.html.slim +1 -10
- data/lib/trek/typography_formatter.rb +35 -11
- data/lib/trek/version.rb +1 -1
- data/package.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1ea15f7b7f4c1da74cdda11ad651158cd69856a5a04490e417ed90c80886cc0a
|
|
4
|
+
data.tar.gz: e6bc99bac1a371ee03737a217f9f621e430f8db8ebb4dcaeba5494e224608970
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2742e782830532e67f36d3affb40723858b5533cf798cc4ea729028cda798b1a447cef87690d15ae148882048489d811a9df4cce3394c5bb1ade7d1aede7e64f
|
|
7
|
+
data.tar.gz: 19a823e2bbdf69d076aa82ec75db839dfe78668faad9c07a63cf08066465899f15033099629dcdb7740c4d789e63297ee7294c6760ba04a28dd889c52859a05c
|
data/Gemfile.lock
CHANGED
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 --
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
|
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 ({
|
|
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 ({
|
|
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
|
|
|
@@ -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
|
-
|
|
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")
|
data/config/locales/trek.en.yml
CHANGED
data/config/locales/trek.fr.yml
CHANGED
|
@@ -101,17 +101,5 @@
|
|
|
101
101
|
text: t("admin.actions.validate"),
|
|
102
102
|
color: :grass,
|
|
103
103
|
)
|
|
104
|
-
|
|
105
|
-
|
|
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)
|
|
@@ -48,13 +48,4 @@
|
|
|
48
48
|
color: :grass,
|
|
49
49
|
)
|
|
50
50
|
|
|
51
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
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}?)
|
|
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
data/package.json
CHANGED