polivalente 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/app/assets/build/css/components/_bookmarks.scss +20 -0
- data/app/assets/build/css/components/_colors.scss +104 -0
- data/app/assets/build/css/components/_forms.scss +23 -0
- data/app/assets/build/css/components/_pagination.scss +75 -0
- data/app/assets/build/css/components/_player.scss +148 -0
- data/app/assets/build/css/components/_tags.scss +29 -0
- data/app/assets/build/css/components/_themes.scss +69 -0
- data/app/assets/build/css/components/_tribute.scss +25 -0
- data/app/assets/build/css/components/plyr.css +1 -0
- data/app/assets/build/css/components/trix/_clipboard.scss +27 -0
- data/app/assets/build/css/components/trix/_trix.scss +181 -0
- data/app/assets/build/css/index.css +19 -0
- data/app/assets/build/js/application.js +2 -0
- data/app/assets/build/js/controllers/accordion_controller.js +21 -0
- data/app/assets/build/js/controllers/dynamic_select_controller.js +22 -0
- data/app/assets/build/js/controllers/index.js +33 -0
- data/app/assets/build/js/controllers/offline_controller.js +19 -0
- data/app/assets/build/js/controllers/reactive_text_controller.js +15 -0
- data/app/assets/build/js/controllers/text_counter_controller.js +38 -0
- data/app/assets/build/js/controllers/theme_controller.js +36 -0
- data/app/assets/build/js/controllers/trix_attachment_blocker_controller.js +15 -0
- data/app/assets/build/js/controllers/trix_autocomplete_controller.js +86 -0
- data/app/assets/build/js/controllers/trix_blockcode_controller.js +41 -0
- data/app/assets/build/js/controllers/trix_clipboard_controller.js +50 -0
- data/app/assets/build/js/controllers/trix_color_controller.js +49 -0
- data/app/assets/build/js/controllers/trix_highlight_controller.js +55 -0
- data/app/assets/build/js/controllers/trix_plyr_controller.js +86 -0
- data/app/assets/build/js/controllers/trix_toolbar_controller.js +48 -0
- data/app/assets/config/polivalente_manifest.js +1 -0
- data/app/assets/javascripts/polivalente/application.js +173 -0
- data/app/assets/stylesheets/polivalente/application.css +3 -14
- data/app/assets/stylesheets/polivalente/styles.css +1928 -0
- data/app/controllers/polivalente/application_controller.rb +2 -0
- data/app/controllers/polivalente/archives_controller.rb +18 -0
- data/app/controllers/polivalente/autocomplete_controller.rb +2 -3
- data/app/controllers/polivalente/comments_controller.rb +27 -0
- data/app/controllers/polivalente/trashes_controller.rb +18 -0
- data/app/controllers/polivalente/users_controller.rb +42 -0
- data/app/helpers/polivalente/gravatar_helper.rb +0 -4
- data/app/models/polivalente/tag.rb +1 -0
- data/app/views/layouts/polivalente/application.html.erb +9 -3
- data/app/views/polivalente/archives/_archive.html.erb +2 -0
- data/app/views/polivalente/archives/index.html.erb +3 -0
- data/app/views/polivalente/archives/show.html.erb +3 -0
- data/app/views/polivalente/autocomplete/tags.json.jbuilder +1 -0
- data/app/views/polivalente/autocomplete/users.json.jbuilder +1 -0
- data/app/views/polivalente/shared/_notices.html.erb +10 -0
- data/app/views/polivalente/shared/_offline_indicator.html.erb +10 -0
- data/app/views/polivalente/shared/_theme_toggle.html.erb +1 -0
- data/app/views/polivalente/tags/_cloud.html.erb +7 -0
- data/app/views/polivalente/tags/_list.html.erb +5 -0
- data/app/views/polivalente/tags/_tag.html.erb +1 -0
- data/app/views/polivalente/tags/_tag.json.jbuilder +4 -0
- data/app/views/polivalente/trashes/_trash.html.erb +2 -0
- data/app/views/polivalente/trashes/index.html.erb +3 -0
- data/app/views/polivalente/trashes/show.html.erb +3 -0
- data/app/views/polivalente/users/_form.html.erb +38 -0
- data/app/views/polivalente/users/_user.html.erb +4 -0
- data/app/views/polivalente/users/_user.json.jbuilder +4 -0
- data/app/views/polivalente/users/activities/_activity.html.erb +12 -0
- data/app/views/polivalente/users/activities/_recent.html.erb +17 -0
- data/app/views/polivalente/users/edit.html.erb +13 -0
- data/app/views/polivalente/users/related/_user.html.erb +14 -0
- data/app/views/polivalente/users/related/_users.html.erb +14 -0
- data/app/views/polivalente/users/show.html.erb +180 -0
- data/config/locales/en.yml +72 -0
- data/config/locales/es.yml +72 -0
- data/config/locales/fr.yml +74 -0
- data/config/locales/pt.yml +72 -0
- data/config/routes.rb +6 -0
- data/lib/generators/polivalente/install/install_generator.rb +1 -1
- data/lib/generators/polivalente/locales/locales_generator.rb +11 -0
- data/lib/generators/polivalente/templates/user.rb +3 -0
- data/lib/polivalente/engine.rb +1 -0
- data/lib/polivalente/version.rb +1 -1
- data/lib/polivalente.rb +3 -0
- metadata +107 -2
@@ -0,0 +1,181 @@
|
|
1
|
+
/* Configuration and General Appearance */
|
2
|
+
|
3
|
+
trix-toolbar {
|
4
|
+
.trix-button--icon {
|
5
|
+
@apply text-primary;
|
6
|
+
@apply bg-gray-100;
|
7
|
+
}
|
8
|
+
|
9
|
+
@apply py-2 px-2 rounded-sm rounded-b-none;
|
10
|
+
}
|
11
|
+
|
12
|
+
/* Editor */
|
13
|
+
|
14
|
+
trix-editor {
|
15
|
+
@apply rounded shadow-sm;
|
16
|
+
@apply w-full overflow-x-auto overflow-y-scroll;
|
17
|
+
max-height: 500px;
|
18
|
+
|
19
|
+
code {
|
20
|
+
@apply dark:bg-gray-700 bg-gray-200;
|
21
|
+
@apply py-0.5 px-1 rounded-sm;
|
22
|
+
}
|
23
|
+
|
24
|
+
pre {
|
25
|
+
@apply border border-tertiary bg-tertiary;
|
26
|
+
@apply p-4 my-2 rounded;
|
27
|
+
}
|
28
|
+
|
29
|
+
figure {
|
30
|
+
all: initial;
|
31
|
+
line-height: 0;
|
32
|
+
color: inherit;
|
33
|
+
font-family: inherit;
|
34
|
+
|
35
|
+
.mention {
|
36
|
+
@apply text-white no-underline;
|
37
|
+
@apply inline-flex space-x-1 p-0.5 items-center rounded;
|
38
|
+
@apply bg-blue-600 hover:bg-blue-500;
|
39
|
+
@apply border border-blue-600 hover:border-blue-600;
|
40
|
+
|
41
|
+
& + figcaption, & .attachment__caption {
|
42
|
+
line-height: 0;
|
43
|
+
@apply hidden;
|
44
|
+
}
|
45
|
+
|
46
|
+
img {
|
47
|
+
@apply w-4 h-4 rounded-full border border-tertiary p-0 m-0;
|
48
|
+
line-height: 0;
|
49
|
+
}
|
50
|
+
|
51
|
+
p {
|
52
|
+
@apply p-0 m-0;
|
53
|
+
line-height: 0;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
.attachment__caption-editor {
|
59
|
+
height: 20px !important;
|
60
|
+
}
|
61
|
+
|
62
|
+
/* file attachment previews */
|
63
|
+
.attachment-gallery {
|
64
|
+
@apply grid grid-cols-4 gap-3;
|
65
|
+
|
66
|
+
[data-trix-cursor-target] { display: none !important; }
|
67
|
+
|
68
|
+
figcaption.attachment__caption { @apply text-xs; }
|
69
|
+
|
70
|
+
.attachment--preview {
|
71
|
+
@apply border border-gray-50;
|
72
|
+
img { @apply rounded-md shadow; }
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
/* Content Viewer */
|
78
|
+
|
79
|
+
.trix-content {
|
80
|
+
figure {
|
81
|
+
&.attachment.attachment--file {
|
82
|
+
@apply py-2 w-full border-none;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
code {
|
87
|
+
@apply rounded bg-tertiary text-primary p-0.5 font-mono font-thin text-sm;
|
88
|
+
}
|
89
|
+
|
90
|
+
.tooltip {
|
91
|
+
@apply relative py-1 rounded-sm w-7 h-5 inline-block;
|
92
|
+
|
93
|
+
.tooltip-text {
|
94
|
+
@apply absolute;
|
95
|
+
@apply text-center font-mono text-sm;
|
96
|
+
@apply p-1 right-0 rounded-l rounded-tr;
|
97
|
+
visibility: hidden;
|
98
|
+
bottom: 90%;
|
99
|
+
margin-left: -60px;
|
100
|
+
z-index: 1;
|
101
|
+
}
|
102
|
+
|
103
|
+
/* Show the tooltip text when you mouse over the tooltip container */
|
104
|
+
&:hover {
|
105
|
+
.tooltip-text {
|
106
|
+
visibility: visible;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
/* @mentions */
|
112
|
+
action-text-attachment {
|
113
|
+
all: initial;
|
114
|
+
font-family: inherit;
|
115
|
+
|
116
|
+
p, a, div, img {
|
117
|
+
line-height: 0;
|
118
|
+
padding: 0;
|
119
|
+
margin: 0;
|
120
|
+
}
|
121
|
+
|
122
|
+
.mention {
|
123
|
+
@apply text-white no-underline;
|
124
|
+
@apply inline-flex space-x-1 p-0.5 items-center rounded;
|
125
|
+
@apply bg-blue-600 hover:bg-blue-500;
|
126
|
+
@apply border border-blue-600 hover:border-blue-600;
|
127
|
+
|
128
|
+
img {
|
129
|
+
@apply w-4 h-4 rounded-full border border-tertiary;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
/* file attachment previews */
|
135
|
+
.attachment-gallery {
|
136
|
+
all: initial;
|
137
|
+
color: inherit;
|
138
|
+
* {
|
139
|
+
all: initial;
|
140
|
+
color: inherit;
|
141
|
+
font-family: inherit;
|
142
|
+
}
|
143
|
+
|
144
|
+
@apply grid gap-4 grid-cols-3 py-8 m-0;
|
145
|
+
|
146
|
+
.attachment {
|
147
|
+
@apply border border-gray-50;
|
148
|
+
|
149
|
+
img {
|
150
|
+
@apply w-full h-auto rounded-md;
|
151
|
+
@apply hover:scale-105 transform transition shadow;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
figure.attachment {
|
157
|
+
img {
|
158
|
+
@apply border border-tertiary rounded-md shadow object-cover;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
.attachment--preview {
|
163
|
+
img { @apply w-full h-full !important; }
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
trix-editor, .trix-content {
|
168
|
+
/* Media: Audio and Video */
|
169
|
+
.plyr {
|
170
|
+
@apply rounded-md;
|
171
|
+
}
|
172
|
+
|
173
|
+
.attachment {
|
174
|
+
&--mp3, &--mp4 {
|
175
|
+
img { @apply hidden; }
|
176
|
+
.attachment__caption {
|
177
|
+
@apply bg-inverted text-inverted border border-primary rounded p-1.5;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
@import "tailwindcss/base";
|
2
|
+
@import "tailwindcss/components";
|
3
|
+
@import "tailwindcss/utilities";
|
4
|
+
|
5
|
+
/*! purgecss start ignore */
|
6
|
+
@import "./components/_themes.scss";
|
7
|
+
@import "./components/_tribute.scss";
|
8
|
+
|
9
|
+
@import "./components/trix/_clipboard.scss";
|
10
|
+
@import "./components/trix/_trix.scss";
|
11
|
+
|
12
|
+
@import "./components/_player.scss";
|
13
|
+
@import "./components/_pagination.scss";
|
14
|
+
@import "./components/_forms.scss";
|
15
|
+
@import "./components/_colors.scss";
|
16
|
+
@import "./components/_tags.scss";
|
17
|
+
|
18
|
+
@import "./components/plyr.css";
|
19
|
+
/*! purgecss end ignore */
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
// Connects to data-controller="accordion"
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = []
|
6
|
+
|
7
|
+
toggle(event) {
|
8
|
+
let accordion = event.currentTarget.parentNode
|
9
|
+
let body = accordion.querySelectorAll('[data-role=description]')[0]
|
10
|
+
let icons = accordion.querySelectorAll('[data-role=icon]')
|
11
|
+
|
12
|
+
// Toggle body
|
13
|
+
body.classList.toggle('hidden')
|
14
|
+
|
15
|
+
// Toggle Icon
|
16
|
+
icons.forEach(icon => icon.classList.toggle('hidden'))
|
17
|
+
|
18
|
+
// Highlight
|
19
|
+
accordion.classList.toggle('border-l-2')
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import { get as request } from "@rails/request.js"
|
3
|
+
|
4
|
+
// Connects to data-controller="dynamic-select"
|
5
|
+
export default class extends Controller {
|
6
|
+
static targets = ['select']
|
7
|
+
|
8
|
+
static values = {
|
9
|
+
url: String,
|
10
|
+
param: String
|
11
|
+
}
|
12
|
+
|
13
|
+
change(event) {
|
14
|
+
let params = new URLSearchParams()
|
15
|
+
params.append(this.paramValue, event.target.selectedOptions[0].value)
|
16
|
+
params.append('target', this.selectTarget.id)
|
17
|
+
|
18
|
+
request(`${this.urlValue}?${params}`, {
|
19
|
+
responseKind: 'turbo-stream'
|
20
|
+
})
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { Application } from "@hotwired/stimulus"
|
2
|
+
window.Stimulus = Application.start()
|
3
|
+
|
4
|
+
// Application
|
5
|
+
import AccordionController from "./accordion_controller"
|
6
|
+
import DynamicSelectController from "./dynamic_select_controller"
|
7
|
+
import OfflineController from "./offline_controller"
|
8
|
+
import ReactiveTextController from "./reactive_text_controller"
|
9
|
+
import TextCounterController from "./text_counter_controller"
|
10
|
+
import ThemeController from "./theme_controller"
|
11
|
+
|
12
|
+
// Trix Text Editor (ActionText)
|
13
|
+
import TrixAttachmentBlockerController from "./trix_attachment_blocker_controller"
|
14
|
+
import TrixAutocompleteController from "./trix_autocomplete_controller"
|
15
|
+
import TrixBlockcodeController from "./trix_blockcode_controller"
|
16
|
+
import TrixClipboardController from "./trix_clipboard_controller"
|
17
|
+
import TrixColorController from "./trix_color_controller"
|
18
|
+
import TrixHighlightController from "./trix_highlight_controller"
|
19
|
+
|
20
|
+
Stimulus.register("accordion", AccordionController)
|
21
|
+
Stimulus.register("dynamic-select", DynamicSelectController)
|
22
|
+
Stimulus.register("offline", OfflineController)
|
23
|
+
Stimulus.register("reactive-text", ReactiveTextController)
|
24
|
+
Stimulus.register("text-counter", TextCounterController)
|
25
|
+
Stimulus.register("theme", ThemeController)
|
26
|
+
|
27
|
+
// Trix configuration and behavior
|
28
|
+
Stimulus.register("trix-attachment-blocker", TrixAttachmentBlockerController)
|
29
|
+
Stimulus.register("trix-autocomplete", TrixAutocompleteController)
|
30
|
+
Stimulus.register("trix-blockcode", TrixBlockcodeController)
|
31
|
+
Stimulus.register("trix-clipboard", TrixClipboardController)
|
32
|
+
Stimulus.register("trix-color", TrixColorController)
|
33
|
+
Stimulus.register("trix-highlight", TrixHighlightController)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
// Connects to data-controller="offline"
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = ['indicator']
|
6
|
+
|
7
|
+
connect() {
|
8
|
+
// This forces an offline message even if the browser does not fire an event.
|
9
|
+
// Like when the page is loaded from cache, for example.
|
10
|
+
this.setNetworkStatus()
|
11
|
+
|
12
|
+
window.addEventListener('offline', () => this.setNetworkStatus())
|
13
|
+
window.addEventListener('online', () => this.setNetworkStatus())
|
14
|
+
}
|
15
|
+
|
16
|
+
setNetworkStatus() {
|
17
|
+
this.indicatorTarget.classList.toggle('hidden', navigator.onLine)
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Connects to data-controller="reactive-text"
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = ["source", "output"]
|
6
|
+
|
7
|
+
connect() {
|
8
|
+
this.sourceTarget.addEventListener('input', this.sync.bind(this))
|
9
|
+
this.outputTargets.forEach(t => t.textContent = this.sourceTarget.value)
|
10
|
+
}
|
11
|
+
|
12
|
+
sync(event) {
|
13
|
+
this.outputTargets.forEach(t => t.textContent = event.target.value)
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import {Controller} from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
// Connects to data-controller="text-counter"
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = ["source", "output"]
|
6
|
+
static classes = ["error"]
|
7
|
+
static values = {
|
8
|
+
limit: Number,
|
9
|
+
wordMode: Boolean
|
10
|
+
}
|
11
|
+
|
12
|
+
connect() {
|
13
|
+
this.sourceTarget.addEventListener('input', this.sync.bind(this))
|
14
|
+
this.sync()
|
15
|
+
}
|
16
|
+
|
17
|
+
_contentLength(content, wordMode) {
|
18
|
+
if (wordMode) content = content.split(" ")
|
19
|
+
|
20
|
+
if (typeof content === "object" && content.length === 1 && content[0] === "")
|
21
|
+
return 0
|
22
|
+
|
23
|
+
return content.length
|
24
|
+
}
|
25
|
+
|
26
|
+
sync(_) {
|
27
|
+
this.outputTargets.forEach(t => {
|
28
|
+
let length = this._contentLength(this.sourceTarget.value, this.wordModeValue)
|
29
|
+
t.textContent = `${length} ${this.wordModeValue ? "words" : "characters"}`
|
30
|
+
|
31
|
+
if (this.limitValue)
|
32
|
+
if (length > this.limitValue)
|
33
|
+
t.classList.add(...this.errorClasses)
|
34
|
+
else
|
35
|
+
t.classList.remove(...this.errorClasses)
|
36
|
+
})
|
37
|
+
}
|
38
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import {Controller} from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["theme", "toggle"]
|
5
|
+
|
6
|
+
get shouldEnable() {
|
7
|
+
if (!this['hasToggleTarget']) {
|
8
|
+
console.debug('No toggle component found for theme controller');
|
9
|
+
return false;
|
10
|
+
}
|
11
|
+
|
12
|
+
return true;
|
13
|
+
}
|
14
|
+
|
15
|
+
connect() {
|
16
|
+
this.loadTheme();
|
17
|
+
|
18
|
+
if (!this.shouldEnable) return;
|
19
|
+
|
20
|
+
this.theme = localStorage.theme;
|
21
|
+
this['themeTarget'].classList = [this.theme];
|
22
|
+
}
|
23
|
+
|
24
|
+
toggleTheme(event) {
|
25
|
+
this.theme = event.target.value;
|
26
|
+
localStorage.theme = this.theme;
|
27
|
+
this['themeTarget'].classList = [this.theme];
|
28
|
+
}
|
29
|
+
|
30
|
+
loadTheme() {
|
31
|
+
this.theme = localStorage.theme;
|
32
|
+
this['themeTarget'].classList = [this.theme];
|
33
|
+
|
34
|
+
if (this['hasToggleTarget']) this['toggleTarget'].value = this.theme;
|
35
|
+
}
|
36
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Connects to data-controller="trix-attachment-blocker"
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = []
|
6
|
+
|
7
|
+
connect() {
|
8
|
+
console.log("trix-attachment-blocker")
|
9
|
+
|
10
|
+
window.addEventListener('trix-file-accept', (event) => {
|
11
|
+
event.preventDefault()
|
12
|
+
alert("File attachments are not supported at this time.")
|
13
|
+
})
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import Tribute from "tributejs"
|
3
|
+
import Trix from "trix"
|
4
|
+
|
5
|
+
// Connects to data-controller="trix-autocomplete"
|
6
|
+
export default class extends Controller {
|
7
|
+
static values = {
|
8
|
+
url: String,
|
9
|
+
searchKey: String,
|
10
|
+
mode: String,
|
11
|
+
trigger: String,
|
12
|
+
auto: Boolean
|
13
|
+
}
|
14
|
+
|
15
|
+
connect() {
|
16
|
+
console.debug("trix-autocomplete")
|
17
|
+
|
18
|
+
if (!this.searchKeyValue)
|
19
|
+
this.searchKeyValue = 'query'
|
20
|
+
|
21
|
+
if (!this.modeValue)
|
22
|
+
this.modeValue = this.element.editor ? 'trix' : 'input'
|
23
|
+
|
24
|
+
if (!this.urlValue) {
|
25
|
+
console.debug("Autocomplete needs both URL value (i.e. 'https://autocomplete.com')")
|
26
|
+
return this.disconnect()
|
27
|
+
}
|
28
|
+
|
29
|
+
this.initializeTribute()
|
30
|
+
this.listen()
|
31
|
+
}
|
32
|
+
|
33
|
+
disconnect() {
|
34
|
+
this.tribute.detach(this.element)
|
35
|
+
}
|
36
|
+
|
37
|
+
initializeTribute() {
|
38
|
+
this.tribute = new Tribute({
|
39
|
+
trigger: this.triggerValue || '@',
|
40
|
+
allowSpaces: true,
|
41
|
+
lookup: "name",
|
42
|
+
fillAttr: this.modeValue === 'trix' ? 'value' : 'name',
|
43
|
+
autocompleteMode: this.autoValue,
|
44
|
+
replaceTextSuffix: ', ',
|
45
|
+
values: this._fetch.bind(this)
|
46
|
+
})
|
47
|
+
this.tribute.attach((this.element))
|
48
|
+
this.tribute.range.pasteHtml = this._pasteHTML.bind(this)
|
49
|
+
}
|
50
|
+
|
51
|
+
listen() {
|
52
|
+
this.element.addEventListener("tribute-replaced", (event) => {
|
53
|
+
let completion = event.detail.item.original
|
54
|
+
|
55
|
+
if (this.modeValue === 'trix')
|
56
|
+
this._insertTrixAttachment(completion)
|
57
|
+
})
|
58
|
+
}
|
59
|
+
|
60
|
+
_fetch(value, callback) {
|
61
|
+
let params = new URLSearchParams()
|
62
|
+
params.append(this.searchKeyValue, value)
|
63
|
+
|
64
|
+
fetch(`${this.urlValue}?${params}`)
|
65
|
+
.then(response => response.json())
|
66
|
+
.then(data => callback(data))
|
67
|
+
.catch(callback([]))
|
68
|
+
}
|
69
|
+
|
70
|
+
_insertTrixAttachment(completion) {
|
71
|
+
let attachment = new Trix.Attachment({
|
72
|
+
sgid: completion.sgid,
|
73
|
+
content: completion.content
|
74
|
+
})
|
75
|
+
this.element.editor.insertAttachment(attachment)
|
76
|
+
this.element.editor.insertString("")
|
77
|
+
}
|
78
|
+
|
79
|
+
_pasteHTML(_, start, end) {
|
80
|
+
let range = this.element.editor.getSelectedRange()
|
81
|
+
let position = range[0]
|
82
|
+
let length = end - start
|
83
|
+
this.element.editor.setSelectedRange([position - length, position])
|
84
|
+
this.element.editor.deleteInDirection('backward')
|
85
|
+
}
|
86
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import Trix from "trix"
|
3
|
+
|
4
|
+
// Connects to data-controller="trix-blockcode"
|
5
|
+
export default class extends Controller {
|
6
|
+
connect() {
|
7
|
+
Trix.config.textAttributes.inlineCode = {
|
8
|
+
tagName: "code",
|
9
|
+
inheritable: true
|
10
|
+
}
|
11
|
+
|
12
|
+
window.addEventListener("trix-initialize", event => {
|
13
|
+
const element = event.target
|
14
|
+
const { toolbarElement, editor } = element
|
15
|
+
|
16
|
+
const blockCodeButton = toolbarElement.querySelector("[data-trix-attribute=code]")
|
17
|
+
const inlineCodeButton = blockCodeButton.cloneNode(true)
|
18
|
+
|
19
|
+
inlineCodeButton.hidden = true
|
20
|
+
inlineCodeButton.dataset.trixAttribute = "inlineCode"
|
21
|
+
blockCodeButton.insertAdjacentElement("afterend", inlineCodeButton)
|
22
|
+
|
23
|
+
element.addEventListener("trix-selection-change", _ => {
|
24
|
+
const type = getCodeFormattingType()
|
25
|
+
blockCodeButton.hidden = type == "inline"
|
26
|
+
inlineCodeButton.hidden = type == "block"
|
27
|
+
})
|
28
|
+
|
29
|
+
const getCodeFormattingType = () => {
|
30
|
+
if (editor.attributeIsActive("code")) return "block"
|
31
|
+
if (editor.attributeIsActive("inlineCode")) return "inline"
|
32
|
+
|
33
|
+
const range = editor.getSelectedRange()
|
34
|
+
if (range[0] == range[1]) return "block"
|
35
|
+
|
36
|
+
const text = editor.getSelectedDocument().toString().trim()
|
37
|
+
return /\n/.test(text) ? "block" : "inline"
|
38
|
+
}
|
39
|
+
})
|
40
|
+
}
|
41
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Connects to data-controller="trix-clipboard"
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = ['code']
|
6
|
+
|
7
|
+
get defaults() {
|
8
|
+
return {
|
9
|
+
actionElements: ['BUTTON'],
|
10
|
+
codeElements: ['PRE'],
|
11
|
+
codeClasses: ['hljs']
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
get trix() {
|
16
|
+
return this.element.children[0];
|
17
|
+
}
|
18
|
+
|
19
|
+
connect() {
|
20
|
+
if (document.queryCommandSupported("copy")) {
|
21
|
+
this.element.classList.add("clipboard--supported")
|
22
|
+
|
23
|
+
this.element.addEventListener('click', (event) => {
|
24
|
+
const {target} = event
|
25
|
+
|
26
|
+
if (this._isActionTarget(target)) {
|
27
|
+
const code = this._getCodeSibling(target)
|
28
|
+
if (code) this.copy(code)
|
29
|
+
}
|
30
|
+
})
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
_isActionTarget(element) {
|
35
|
+
return this.defaults.actionElements.includes(element.tagName)
|
36
|
+
}
|
37
|
+
|
38
|
+
_getCodeSibling(element) {
|
39
|
+
const snippet = element.nextSibling
|
40
|
+
const isCode = this.defaults.codeElements.includes(snippet.tagName)
|
41
|
+
const isHljs = snippet.classList.contains(this.defaults.codeClasses)
|
42
|
+
|
43
|
+
return (isCode && isHljs) ? snippet.innerText : false
|
44
|
+
}
|
45
|
+
|
46
|
+
copy(code) {
|
47
|
+
navigator.clipboard.writeText(code)
|
48
|
+
.then(() => { console.log('Copied code snippet!') })
|
49
|
+
}
|
50
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import {Controller} from '@hotwired/stimulus'
|
2
|
+
import { ntc as nameThatColor } from '@cosmicice/namethatcolor'
|
3
|
+
|
4
|
+
// Connects to data-controller="trix-color"
|
5
|
+
export default class extends Controller {
|
6
|
+
get trix() {
|
7
|
+
return this.element.children[0]
|
8
|
+
}
|
9
|
+
|
10
|
+
get colorTest() {
|
11
|
+
return /color#[0-9a-f]{3,6}/gi
|
12
|
+
}
|
13
|
+
|
14
|
+
connect() {
|
15
|
+
let content = this.trix.innerHTML
|
16
|
+
this.trix.innerHTML = content.replace(this.colorTest, this._color)
|
17
|
+
}
|
18
|
+
|
19
|
+
_color(value, position, offset) {
|
20
|
+
// Drop the color prefix
|
21
|
+
const backgroundColor = value.replace('color', '')
|
22
|
+
let c = backgroundColor.replace('#', '')
|
23
|
+
|
24
|
+
if (c.length === 3) {
|
25
|
+
// Support shorthand notation. i.e #c0f
|
26
|
+
// c0f => cc00ff;
|
27
|
+
c = `${c[0]}${c[0]}${c[1]}${c[1]}${c[2]}${c[2]}`
|
28
|
+
}
|
29
|
+
|
30
|
+
// YIQ color contrast calculation:
|
31
|
+
// https://24ways.org/2010/calculating-color-contrast
|
32
|
+
// https://gorails.com/episodes/contrasting-colors-with-yiq
|
33
|
+
const r = parseInt(c.substr(0,2),16)
|
34
|
+
const g = parseInt(c.substr(2,2),16)
|
35
|
+
const b = parseInt(c.substr(4,2),16)
|
36
|
+
const yiq = ((r*299) + (g*587) + (b*114)) / 1000
|
37
|
+
const foregroundColor = (yiq >= 128) ? 'black' : 'white'
|
38
|
+
|
39
|
+
const colorName = nameThatColor.name(backgroundColor).name
|
40
|
+
|
41
|
+
return `
|
42
|
+
<span class="tooltip border" style="background: ${backgroundColor} !important;">
|
43
|
+
<span sr-only="${colorName}" class="tooltip-text" style="background: ${backgroundColor} !important; color: ${foregroundColor} !important;">
|
44
|
+
${backgroundColor} (${colorName})
|
45
|
+
</span>
|
46
|
+
</span>
|
47
|
+
`
|
48
|
+
}
|
49
|
+
}
|