smeditor 0.2.5 → 0.3.4
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/CHANGELOG.md +34 -0
- data/README.md +3 -0
- data/app/assets/javascripts/smeditor.js +571 -135
- data/app/assets/stylesheets/smeditor.css +146 -37
- data/lib/generators/smeditor/templates/initializer.rb +3 -0
- data/lib/smeditor/rails/config.rb +3 -0
- data/lib/smeditor/rails/form_helper.rb +26 -10
- data/lib/smeditor/rails/sanitizer.rb +2 -2
- data/lib/smeditor/rails/version.rb +1 -1
- metadata +2 -2
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
/* SMEditor default theme tokens.
|
|
2
2
|
*
|
|
3
|
-
*
|
|
3
|
+
* Theme selection:
|
|
4
4
|
*
|
|
5
|
-
* 1. Do nothing →
|
|
6
|
-
* 2. Set `data-theme="light"` or `data-theme="dark"` on
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Why both: developers want auto for new users (matches the rest of
|
|
11
|
-
* their app), but a theme toggle in product UI needs explicit control
|
|
12
|
-
* that survives reloads. Supporting both lets either pattern work.
|
|
5
|
+
* 1. Do nothing → light theme.
|
|
6
|
+
* 2. Set `data-theme="light"` or `data-theme="dark"` on the editor or
|
|
7
|
+
* on an ancestor. SMEditor sets `data-theme="light"` by default when
|
|
8
|
+
* initialized without a theme option.
|
|
13
9
|
*/
|
|
14
10
|
|
|
15
11
|
/* ---------------------------------------------------------------------------
|
|
@@ -87,8 +83,7 @@
|
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
/* ---------------------------------------------------------------------------
|
|
90
|
-
* Dark theme — applied when explicitly tagged
|
|
91
|
-
* no explicit theme is set on the document.
|
|
86
|
+
* Dark theme — applied only when explicitly tagged.
|
|
92
87
|
* ------------------------------------------------------------------------ */
|
|
93
88
|
:host([data-theme="dark"]),
|
|
94
89
|
[data-theme="dark"] {
|
|
@@ -110,31 +105,6 @@
|
|
|
110
105
|
0 10px 20px -15px rgba(0, 0, 0, 0.45);
|
|
111
106
|
}
|
|
112
107
|
|
|
113
|
-
/* Auto-dark: only when the document root has no explicit override. The
|
|
114
|
-
* `:not([data-theme])` guard means a `data-theme="light"` setting on
|
|
115
|
-
* any ancestor wins, even at night. */
|
|
116
|
-
@media (prefers-color-scheme: dark) {
|
|
117
|
-
:root:not([data-theme="light"]),
|
|
118
|
-
:host(:not([data-theme="light"])) {
|
|
119
|
-
--sme-color-surface: #0f1115;
|
|
120
|
-
--sme-color-surface-muted: #1a1d23;
|
|
121
|
-
--sme-color-border: #2a2d35;
|
|
122
|
-
--sme-color-border-strong: #3a3f49;
|
|
123
|
-
--sme-color-text: #e7e9ee;
|
|
124
|
-
--sme-color-text-muted: #9aa0aa;
|
|
125
|
-
--sme-color-placeholder: #5c606b;
|
|
126
|
-
--sme-color-accent: #a78bfa;
|
|
127
|
-
--sme-color-accent-soft: #2a1d54;
|
|
128
|
-
--sme-color-primary: var(--sme-color-accent);
|
|
129
|
-
--sme-color-focus: #c4b5fd;
|
|
130
|
-
--sme-color-danger: #fca5a5;
|
|
131
|
-
--sme-color-danger-soft: #451a1a;
|
|
132
|
-
--sme-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
|
|
133
|
-
--sme-shadow-md: 0 10px 38px -10px rgba(0, 0, 0, 0.5),
|
|
134
|
-
0 10px 20px -15px rgba(0, 0, 0, 0.45);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
108
|
/* SMEditor default theme.
|
|
139
109
|
*
|
|
140
110
|
* Imports tokens, then styles the structural classes set by core
|
|
@@ -340,7 +310,7 @@
|
|
|
340
310
|
/* Color swatch grid inside the color-picker dropdowns */
|
|
341
311
|
.smeditor-color-grid {
|
|
342
312
|
display: grid;
|
|
343
|
-
grid-template-columns: repeat(
|
|
313
|
+
grid-template-columns: repeat(10, 22px);
|
|
344
314
|
gap: 4px;
|
|
345
315
|
padding: 6px;
|
|
346
316
|
}
|
|
@@ -1232,6 +1202,145 @@
|
|
|
1232
1202
|
}
|
|
1233
1203
|
}
|
|
1234
1204
|
|
|
1205
|
+
/* ===========================================================================
|
|
1206
|
+
* Colour pickers — current-colour indicator and labelled panel
|
|
1207
|
+
* ======================================================================== */
|
|
1208
|
+
|
|
1209
|
+
.smeditor-color-trigger {
|
|
1210
|
+
display: inline-flex;
|
|
1211
|
+
flex-direction: column;
|
|
1212
|
+
align-items: center;
|
|
1213
|
+
gap: 1px;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
.smeditor-color-trigger__bar {
|
|
1217
|
+
display: block;
|
|
1218
|
+
width: 14px;
|
|
1219
|
+
height: 3px;
|
|
1220
|
+
border-radius: 1px;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
.smeditor-color-trigger__bar[data-empty="true"] {
|
|
1224
|
+
background: transparent;
|
|
1225
|
+
box-shadow: inset 0 0 0 1px var(--sme-color-border-strong);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
.smeditor-color-trigger__bar[data-empty="false"] {
|
|
1229
|
+
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
.smeditor-dropdown--color .smeditor-dropdown__menu {
|
|
1233
|
+
min-width: 0;
|
|
1234
|
+
max-width: none;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
.smeditor-color-panel {
|
|
1238
|
+
display: flex;
|
|
1239
|
+
flex-direction: column;
|
|
1240
|
+
gap: 6px;
|
|
1241
|
+
padding: 4px;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
.smeditor-color-panel .smeditor-color-grid {
|
|
1245
|
+
padding: 0;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
.smeditor-color-panel__title {
|
|
1249
|
+
font-size: 11px;
|
|
1250
|
+
font-weight: 600;
|
|
1251
|
+
letter-spacing: 0.04em;
|
|
1252
|
+
text-transform: uppercase;
|
|
1253
|
+
color: var(--sme-color-text-muted);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.smeditor-color-panel__widths {
|
|
1257
|
+
display: flex;
|
|
1258
|
+
gap: 4px;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
.smeditor-color-panel__chip,
|
|
1262
|
+
.smeditor-color-panel__clear {
|
|
1263
|
+
appearance: none;
|
|
1264
|
+
font: inherit;
|
|
1265
|
+
font-size: var(--sme-text-sm);
|
|
1266
|
+
color: var(--sme-color-text);
|
|
1267
|
+
background: transparent;
|
|
1268
|
+
border: 1px solid var(--sme-color-border);
|
|
1269
|
+
border-radius: var(--sme-radius-sm);
|
|
1270
|
+
padding: 5px 8px;
|
|
1271
|
+
cursor: pointer;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
.smeditor-color-panel__chip {
|
|
1275
|
+
flex: 1 1 0;
|
|
1276
|
+
white-space: nowrap;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
.smeditor-color-panel__chip:hover,
|
|
1280
|
+
.smeditor-color-panel__clear:hover {
|
|
1281
|
+
background: var(--sme-color-surface-muted);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
.smeditor-color-panel__chip.is-active {
|
|
1285
|
+
color: var(--sme-color-accent);
|
|
1286
|
+
background: var(--sme-color-accent-soft);
|
|
1287
|
+
border-color: var(--sme-color-accent);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
.smeditor-color-panel__clear:disabled {
|
|
1291
|
+
opacity: 0.45;
|
|
1292
|
+
cursor: not-allowed;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
.smeditor-color-panel__clear:disabled:hover {
|
|
1296
|
+
background: transparent;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
/* Value pickers show the current value (font / size / spacing) */
|
|
1300
|
+
.smeditor-dropdown__label--text {
|
|
1301
|
+
max-width: 96px;
|
|
1302
|
+
overflow: hidden;
|
|
1303
|
+
text-overflow: ellipsis;
|
|
1304
|
+
white-space: nowrap;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/* Bubble menu — one row, compact dividers */
|
|
1308
|
+
.smeditor-bubble-menu {
|
|
1309
|
+
flex-wrap: nowrap;
|
|
1310
|
+
white-space: nowrap;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
.smeditor-bubble-menu .smeditor-toolbar__divider {
|
|
1314
|
+
height: 18px;
|
|
1315
|
+
margin: 0 2px;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
/* Filled text (extension-background-color). `.smeditor-text-outline` is
|
|
1319
|
+
* the class the 0.3.2 Rails build wrote for the same thing. */
|
|
1320
|
+
.smeditor-editor :where(.smeditor-fill, .smeditor-text-outline),
|
|
1321
|
+
.smeditor-content :where(.smeditor-fill, .smeditor-text-outline) {
|
|
1322
|
+
padding: 0.04em 0.18em;
|
|
1323
|
+
border-radius: 0.22em;
|
|
1324
|
+
-webkit-box-decoration-break: clone;
|
|
1325
|
+
box-decoration-break: clone;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/* Colour dot on the "text in outline" chips */
|
|
1329
|
+
.smeditor-color-panel__chip {
|
|
1330
|
+
display: inline-flex;
|
|
1331
|
+
align-items: center;
|
|
1332
|
+
justify-content: center;
|
|
1333
|
+
gap: 6px;
|
|
1334
|
+
}
|
|
1335
|
+
.smeditor-color-panel__chip::before {
|
|
1336
|
+
content: "";
|
|
1337
|
+
width: 10px;
|
|
1338
|
+
height: 10px;
|
|
1339
|
+
border-radius: 50%;
|
|
1340
|
+
background: var(--sme-chip-swatch, conic-gradient(#1a1a1f 0 50%, #ffffff 0));
|
|
1341
|
+
box-shadow: inset 0 0 0 1px var(--sme-color-border-strong);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1235
1344
|
|
|
1236
1345
|
/* Rails self-contained adapter / style isolation */
|
|
1237
1346
|
:host {
|
|
@@ -14,6 +14,9 @@ SMEditor.configure do |config|
|
|
|
14
14
|
# "starter" gives the MVP toolbar; "full" enables the larger extension set.
|
|
15
15
|
config.default_kit = "starter"
|
|
16
16
|
|
|
17
|
+
# "light" is the default. Set "dark" globally or pass theme: per editor.
|
|
18
|
+
config.default_theme = "light"
|
|
19
|
+
|
|
17
20
|
# Responsive minimum editor height. Numeric values are pixels. You can also
|
|
18
21
|
# use CSS lengths such as "24rem" or "45vh".
|
|
19
22
|
config.min_height = 320
|
|
@@ -18,6 +18,8 @@ module SMEditor
|
|
|
18
18
|
attr_accessor :allowed_tags
|
|
19
19
|
# Which bundle the form helper boots: "starter" or "full".
|
|
20
20
|
attr_accessor :default_kit
|
|
21
|
+
# Default visual theme: "light" or "dark".
|
|
22
|
+
attr_accessor :default_theme
|
|
21
23
|
# Upload endpoint used by the JavaScript adapter when uploads are enabled.
|
|
22
24
|
attr_accessor :upload_path
|
|
23
25
|
# Include the packaged JS/CSS automatically with the first editor field.
|
|
@@ -47,6 +49,7 @@ module SMEditor
|
|
|
47
49
|
table thead tbody tr td th
|
|
48
50
|
]
|
|
49
51
|
@default_kit = "starter"
|
|
52
|
+
@default_theme = "light"
|
|
50
53
|
@upload_path = "/smeditor/uploads"
|
|
51
54
|
@auto_include_assets = true
|
|
52
55
|
@min_height = "320px"
|
|
@@ -11,24 +11,28 @@ module SMEditor
|
|
|
11
11
|
# include assets in <head> can call `smeditor_assets` there and disable
|
|
12
12
|
# auto inclusion with config.auto_include_assets = false.
|
|
13
13
|
def smeditor_assets
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
if defined?(@_smeditor_assets_rendered) && @_smeditor_assets_rendered
|
|
15
|
+
"".html_safe
|
|
16
|
+
else
|
|
17
|
+
@_smeditor_assets_rendered = true
|
|
18
|
+
safe_join([
|
|
19
|
+
stylesheet_link_tag("smeditor", "data-turbo-track": "reload"),
|
|
20
|
+
javascript_include_tag("smeditor", defer: true, "data-turbo-track": "reload"),
|
|
21
|
+
])
|
|
22
|
+
end
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
# form - a Rails FormBuilder
|
|
24
26
|
# method - the model attribute (stored as an HTML string)
|
|
25
27
|
# options - :kit ("starter" | "full"), :class, :placeholder,
|
|
26
|
-
# :
|
|
28
|
+
# :theme ("light" | "dark"), :upload_url, :label,
|
|
29
|
+
# :include_assets, :min_height,
|
|
27
30
|
# :tablet_min_height, :mobile_min_height
|
|
28
31
|
def smeditor_editor(form, method, options = {})
|
|
29
32
|
object = form.object
|
|
30
33
|
current = object.respond_to?(method) ? object.public_send(method) : nil
|
|
31
34
|
kit = (options[:kit] || SMEditor.config.default_kit).to_s
|
|
35
|
+
theme = smeditor_theme(options.fetch(:theme, SMEditor.config.default_theme))
|
|
32
36
|
upload_url = options.fetch(:upload_url, smeditor_upload_url)
|
|
33
37
|
include_assets = options.fetch(:include_assets, SMEditor.config.auto_include_assets)
|
|
34
38
|
stylesheet_url = asset_path("smeditor.css")
|
|
@@ -49,6 +53,7 @@ module SMEditor
|
|
|
49
53
|
data: {
|
|
50
54
|
smeditor: true,
|
|
51
55
|
smeditor_kit: kit,
|
|
56
|
+
smeditor_theme: theme,
|
|
52
57
|
smeditor_placeholder: options[:placeholder],
|
|
53
58
|
smeditor_upload_url: upload_url,
|
|
54
59
|
smeditor_label: options[:label],
|
|
@@ -71,9 +76,20 @@ module SMEditor
|
|
|
71
76
|
|
|
72
77
|
def smeditor_css_length(value, option_name)
|
|
73
78
|
normalized = value.is_a?(Numeric) ? "#{value}px" : value.to_s.strip
|
|
74
|
-
|
|
79
|
+
if normalized.match?(CSS_LENGTH)
|
|
80
|
+
normalized
|
|
81
|
+
else
|
|
82
|
+
raise ArgumentError, "#{option_name} must be a number (pixels) or a CSS length such as 320px, 24rem, or 45vh"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def smeditor_theme(value)
|
|
87
|
+
theme = value.to_s
|
|
88
|
+
unless %w[light dark].include?(theme)
|
|
89
|
+
raise ArgumentError, 'theme must be "light" or "dark"'
|
|
90
|
+
end
|
|
75
91
|
|
|
76
|
-
|
|
92
|
+
theme
|
|
77
93
|
end
|
|
78
94
|
|
|
79
95
|
def smeditor_upload_url
|
|
@@ -13,7 +13,7 @@ module SMEditor
|
|
|
13
13
|
module_function
|
|
14
14
|
|
|
15
15
|
# Attributes kept on surviving tags. `style` is included because
|
|
16
|
-
# SMEditor serializes color, background, highlight, text stroke,
|
|
16
|
+
# SMEditor serializes color, background, highlight, underline colour, text stroke,
|
|
17
17
|
# font size, font family and alignment as constrained inline CSS.
|
|
18
18
|
ALLOWED_ATTRS = %w[
|
|
19
19
|
href src alt title class style target rel
|
|
@@ -111,7 +111,7 @@ module SMEditor
|
|
|
111
111
|
case name
|
|
112
112
|
when "text-align"
|
|
113
113
|
sanitize_text_align(value)
|
|
114
|
-
when "color", "background-color", "background"
|
|
114
|
+
when "color", "background-color", "background", "text-decoration-color"
|
|
115
115
|
sanitize_color(value)
|
|
116
116
|
when "font-family"
|
|
117
117
|
sanitize_font_family(value)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smeditor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SMEditor contributors
|
|
@@ -96,7 +96,7 @@ metadata:
|
|
|
96
96
|
source_code_uri: https://github.com/sCruze/smeditor/tree/main/gems/smeditor
|
|
97
97
|
changelog_uri: https://github.com/sCruze/smeditor/blob/main/gems/smeditor/CHANGELOG.md
|
|
98
98
|
bug_tracker_uri: https://github.com/sCruze/smeditor/issues
|
|
99
|
-
documentation_uri: https://rubydoc.info/gems/smeditor/0.
|
|
99
|
+
documentation_uri: https://rubydoc.info/gems/smeditor/0.3.4
|
|
100
100
|
rubygems_mfa_required: 'true'
|
|
101
101
|
rdoc_options: []
|
|
102
102
|
require_paths:
|