shimmer 0.0.19 → 0.0.22
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/.vscode/settings.json +1 -0
- data/lib/shimmer/form/builder.rb +88 -0
- data/lib/shimmer/form/checkboxes_field.rb +13 -0
- data/lib/shimmer/form/date_field.rb +13 -0
- data/lib/shimmer/form/email_field.rb +19 -0
- data/lib/shimmer/form/field.rb +26 -0
- data/lib/shimmer/form/number_field.rb +13 -0
- data/lib/shimmer/form/password_field.rb +19 -0
- data/lib/shimmer/form/pdf_field.rb +19 -0
- data/lib/shimmer/form/radio_field.rb +13 -0
- data/lib/shimmer/form/select_field.rb +19 -0
- data/lib/shimmer/form/text_area_field.rb +13 -0
- data/lib/shimmer/form/text_field.rb +13 -0
- data/lib/shimmer/form/time_field.rb +13 -0
- data/lib/shimmer/form.rb +16 -0
- data/lib/shimmer/version.rb +1 -1
- data/lib/shimmer.rb +1 -0
- data/src/consent.ts +42 -4
- data/src/controllers/analytics.ts +43 -0
- data/src/controllers/consent.ts +3 -2
- data/src/index.ts +10 -5
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36f3761e1bd5856a9fd2078201abda25b544eff5e807566ce85847ba291c47d9
|
4
|
+
data.tar.gz: c946f2ec830a472aee08d835dbb697711d46c107152723ee8f9bb43a36bc4173
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8bb839595c9e32ab185276bdaac0c8a6f43b4ce1fb1ce0d82d47a1c18c5ae082e29caa68c34d14856db2ebb2001fe4774f5b687599b794706d199767bd46de1
|
7
|
+
data.tar.gz: 885c2bdb0326b02c3eebd7d4606d79906b52264ef8b6043dc6f4061dffd6c8b5a786af22f9d25378d7aa21988a34d6412409adcd7acb6acce7f478e342323eba
|
data/.vscode/settings.json
CHANGED
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
module Form
|
5
|
+
class Builder < ActionView::Helpers::FormBuilder
|
6
|
+
class << self
|
7
|
+
def input_registry
|
8
|
+
@input_registry ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def register(klass)
|
12
|
+
input_registry[klass.type] = klass
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def input(method, as: guess_type(method), **options)
|
17
|
+
as ||= guess_type(method)
|
18
|
+
options[:class] ||= "input__input"
|
19
|
+
collection = options.delete :collection
|
20
|
+
collection_based = !collection.nil? || as == :select
|
21
|
+
collection ||= guess_collection(method) if collection_based
|
22
|
+
name_method ||= guess_name_method(method) if collection_based
|
23
|
+
id_method ||= :id if collection_based
|
24
|
+
classes = []
|
25
|
+
options[:required] ||= true if options[:required].nil? && required_attributes.include?(method)
|
26
|
+
options[:data] ||= {}
|
27
|
+
options[:data][:controller] = options.delete(:controller) if options[:controller]
|
28
|
+
wrapper_data = {}
|
29
|
+
extra = []
|
30
|
+
input_class = self.class.input_registry[as]
|
31
|
+
raise "Unknown type #{as}" unless input_class
|
32
|
+
input = input_class.new(builder: self, method: method, options: options, id_method: id_method, collection: collection, name_method: name_method)
|
33
|
+
wrap method: method, content: input.render, classes: classes + ["input--#{as}"], label: options[:label], data: wrapper_data, extra: extra
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def required_attributes
|
39
|
+
[]
|
40
|
+
end
|
41
|
+
|
42
|
+
def guess_type(method)
|
43
|
+
self.class.input_registry.values.find { |e| e.can_handle?(method) }&.type || :string
|
44
|
+
end
|
45
|
+
|
46
|
+
def guess_collection(method)
|
47
|
+
association_for(method)&.klass&.all || enum_for(method).map { |e| OpenStruct.new(id: e, name: e) } || []
|
48
|
+
end
|
49
|
+
|
50
|
+
def guess_name_method(method)
|
51
|
+
klass = association_for(method)&.klass
|
52
|
+
return :name unless klass
|
53
|
+
|
54
|
+
[:display_name, :name, :title].each do |key|
|
55
|
+
return key if klass.instance_methods.include?(key)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def association_for(method)
|
60
|
+
collection_method = method.to_s.delete_suffix("_id")
|
61
|
+
object.class.reflect_on_association(collection_method) if object.respond_to?(collection_method)
|
62
|
+
end
|
63
|
+
|
64
|
+
def enum_for(method)
|
65
|
+
object.class.types.keys if object.class.respond_to?(method.to_s.pluralize)
|
66
|
+
end
|
67
|
+
|
68
|
+
def wrap(method:, content:, classes:, label:, data: nil, extra: nil)
|
69
|
+
if object&.errors&.[](method)&.any?
|
70
|
+
classes << "input--error"
|
71
|
+
errors = safe_join(object.errors[method].map { |e| content_tag :div, e, class: "input__error" })
|
72
|
+
end
|
73
|
+
label = label == false ? nil : self.label(method, label, class: "input__label")
|
74
|
+
content_tag(:div, safe_join([label, content, errors, extra].compact), class: ["input"] + classes, data: data)
|
75
|
+
end
|
76
|
+
|
77
|
+
def helper
|
78
|
+
@template
|
79
|
+
end
|
80
|
+
|
81
|
+
def value_for(method)
|
82
|
+
object.public_send(method)
|
83
|
+
end
|
84
|
+
|
85
|
+
delegate :content_tag, :t, :safe_join, :icon, :tag, to: :helper
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
module Form
|
5
|
+
class EmailField < Field
|
6
|
+
self.type = :email
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def can_handle?(method)
|
10
|
+
method.to_s.end_with?("email")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
builder.email_field method, options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
module Form
|
5
|
+
class Field
|
6
|
+
class_attribute :type
|
7
|
+
|
8
|
+
attr_reader :builder, :method, :collection, :id_method, :name_method, :options
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def can_handle?(method)
|
12
|
+
false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(builder:, method:, collection:, id_method:, name_method:, options: {})
|
17
|
+
@builder = builder
|
18
|
+
@method = method
|
19
|
+
@collection = collection
|
20
|
+
@id_method = id_method
|
21
|
+
@name_method = name_method
|
22
|
+
@options = options
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
module Form
|
5
|
+
class PasswordField < Field
|
6
|
+
self.type = :password
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def can_handle?(method)
|
10
|
+
method.to_s.end_with?("password")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
builder.password_field method, options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
module Form
|
5
|
+
class PdfField < Field
|
6
|
+
self.type = :pdf
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def can_handle?(method)
|
10
|
+
method.to_s.end_with?("pdf")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
builder.file_field method, options.reverse_merge(accept: "application/pdf")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
module Form
|
5
|
+
class SelectField < Field
|
6
|
+
self.type = :select
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def can_handle?(method)
|
10
|
+
method.to_s.end_with?("_id")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
builder.collection_select method, collection, id_method, name_method, {}, options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/shimmer/form.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
module Form
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require_relative "./form/builder"
|
9
|
+
require_relative "./form/field"
|
10
|
+
|
11
|
+
Dir["#{File.expand_path("./form", __dir__)}/*"].sort.each do |e|
|
12
|
+
require e
|
13
|
+
name = e.split("/").last.delete_suffix(".rb")
|
14
|
+
next unless name.end_with?("_field")
|
15
|
+
Shimmer::Form::Builder.register("Shimmer::Form::#{name.classify}".constantize)
|
16
|
+
end
|
data/lib/shimmer/version.rb
CHANGED
data/lib/shimmer.rb
CHANGED
@@ -7,6 +7,7 @@ Dir["#{File.expand_path("../lib/shimmer/controllers", __dir__)}/*"].sort.each {
|
|
7
7
|
Dir["#{File.expand_path("../lib/shimmer/jobs", __dir__)}/*"].sort.each { |e| require e }
|
8
8
|
Dir["#{File.expand_path("../lib/shimmer/utils", __dir__)}/*"].sort.each { |e| require e }
|
9
9
|
require_relative "shimmer/auth"
|
10
|
+
require_relative "shimmer/form"
|
10
11
|
|
11
12
|
module Shimmer
|
12
13
|
class Error < StandardError; end
|
data/src/consent.ts
CHANGED
@@ -1,12 +1,24 @@
|
|
1
1
|
import { getCookie, setCookie } from "./util";
|
2
2
|
|
3
|
+
const consentCategories = ["essential", "targeting", "statistic"] as const;
|
4
|
+
export type ConsentCategory = typeof consentCategories[number];
|
5
|
+
|
3
6
|
export class Consent {
|
4
|
-
|
5
|
-
|
7
|
+
#consentListeners: Record<ConsentCategory, Array<() => void>> = {
|
8
|
+
essential: [],
|
9
|
+
targeting: [],
|
10
|
+
statistic: [],
|
11
|
+
};
|
12
|
+
|
13
|
+
get permitted(): ConsentCategory[] {
|
14
|
+
return (getCookie("consent") ?? "").split(",") as ConsentCategory[];
|
6
15
|
}
|
7
16
|
|
8
|
-
set permitted(settings:
|
17
|
+
set permitted(settings: ConsentCategory[]) {
|
9
18
|
setCookie("consent", settings.join(","));
|
19
|
+
settings.forEach((category) => {
|
20
|
+
this.#consentListeners[category].forEach((e) => e());
|
21
|
+
});
|
10
22
|
}
|
11
23
|
|
12
24
|
get given(): boolean {
|
@@ -14,7 +26,7 @@ export class Consent {
|
|
14
26
|
}
|
15
27
|
|
16
28
|
permitAll(): void {
|
17
|
-
this.permitted =
|
29
|
+
this.permitted = consentCategories.slice();
|
18
30
|
}
|
19
31
|
|
20
32
|
denyAll(): void {
|
@@ -25,4 +37,30 @@ export class Consent {
|
|
25
37
|
const element = document.getElementById("personalization-settings");
|
26
38
|
if (element) element.hidden = false;
|
27
39
|
}
|
40
|
+
|
41
|
+
consentFor(category: ConsentCategory): Promise<void> {
|
42
|
+
return new Promise((res) => {
|
43
|
+
if (this.permitted.includes(category)) {
|
44
|
+
res();
|
45
|
+
} else {
|
46
|
+
this.#consentListeners[category].push(res);
|
47
|
+
}
|
48
|
+
});
|
49
|
+
}
|
50
|
+
|
51
|
+
async enableGoogleAnalytics(
|
52
|
+
id: string,
|
53
|
+
role: ConsentCategory = "statistic"
|
54
|
+
): Promise<void> {
|
55
|
+
await this.consentFor(role);
|
56
|
+
window.gtag("js", new Date());
|
57
|
+
window.gtag("config", id);
|
58
|
+
const script = document.createElement("script");
|
59
|
+
script.async = true;
|
60
|
+
script.setAttribute(
|
61
|
+
"src",
|
62
|
+
`https://www.googletagmanager.com/gtag/js?id=${id}`
|
63
|
+
);
|
64
|
+
document.head.prepend(script);
|
65
|
+
}
|
28
66
|
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
interface DataEvent {
|
4
|
+
event: string;
|
5
|
+
}
|
6
|
+
|
7
|
+
declare global {
|
8
|
+
interface Window {
|
9
|
+
dataLayer?: DataEvent[];
|
10
|
+
gtag(...arg): void;
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
const dataLayer = (window.dataLayer = window.dataLayer ?? []);
|
15
|
+
function gtag(): void {
|
16
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
17
|
+
// @ts-ignore
|
18
|
+
// eslint-disable-next-line prefer-rest-params
|
19
|
+
dataLayer.push(arguments);
|
20
|
+
}
|
21
|
+
window.gtag = gtag;
|
22
|
+
|
23
|
+
export default class extends Controller {
|
24
|
+
connect(): void {
|
25
|
+
this.recordPage();
|
26
|
+
}
|
27
|
+
|
28
|
+
recordAction(event: MouseEvent): void {
|
29
|
+
const data = JSON.parse(
|
30
|
+
(event.target as HTMLElement).dataset.analytics ?? "{}"
|
31
|
+
);
|
32
|
+
dataLayer.push(data);
|
33
|
+
}
|
34
|
+
|
35
|
+
recordPage(): void {
|
36
|
+
const event = {
|
37
|
+
event: "Pageview",
|
38
|
+
path: location.pathname + location.search,
|
39
|
+
host: location.host,
|
40
|
+
};
|
41
|
+
dataLayer.push(event);
|
42
|
+
}
|
43
|
+
}
|
data/src/controllers/consent.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { Controller } from "@hotwired/stimulus";
|
2
|
+
import { ConsentCategory } from "../consent";
|
2
3
|
|
3
4
|
export default class extends Controller {
|
4
5
|
static targets = ["check"];
|
@@ -7,7 +8,7 @@ export default class extends Controller {
|
|
7
8
|
connect(): void {
|
8
9
|
this.checkTargets.forEach((input) => {
|
9
10
|
input.checked =
|
10
|
-
window.ui?.consent.permitted.includes(input.name) ||
|
11
|
+
window.ui?.consent.permitted.includes(input.name as ConsentCategory) ||
|
11
12
|
input.name === "essential";
|
12
13
|
});
|
13
14
|
}
|
@@ -29,7 +30,7 @@ export default class extends Controller {
|
|
29
30
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
30
31
|
window.ui!.consent.permitted = this.checkTargets
|
31
32
|
.filter((e) => e.checked)
|
32
|
-
.map((e) => e.name);
|
33
|
+
.map((e) => e.name) as ConsentCategory[];
|
33
34
|
this.closeAll();
|
34
35
|
}
|
35
36
|
|
data/src/index.ts
CHANGED
@@ -4,6 +4,7 @@ import { PopoverPresenter } from "./popover";
|
|
4
4
|
import { Consent } from "./consent";
|
5
5
|
import RemoteNavigationController from "./controllers/remote-navigation";
|
6
6
|
import ConsentController from "./controllers/consent";
|
7
|
+
import AnalyticsController from "./controllers/analytics";
|
7
8
|
import "./touch";
|
8
9
|
|
9
10
|
export { registerServiceWorker } from "./serviceworker";
|
@@ -19,6 +20,12 @@ declare global {
|
|
19
20
|
}
|
20
21
|
}
|
21
22
|
|
23
|
+
window.ui = {
|
24
|
+
modal: new ModalPresenter(),
|
25
|
+
popover: new PopoverPresenter(),
|
26
|
+
consent: new Consent(),
|
27
|
+
};
|
28
|
+
|
22
29
|
function createRemoteDestination(): void {
|
23
30
|
if (document.getElementById("shimmer")) {
|
24
31
|
return;
|
@@ -37,9 +44,7 @@ export async function start({
|
|
37
44
|
createRemoteDestination();
|
38
45
|
application.register("remote-navigation", RemoteNavigationController);
|
39
46
|
application.register("consent", ConsentController);
|
40
|
-
|
41
|
-
modal: new ModalPresenter(),
|
42
|
-
popover: new PopoverPresenter(),
|
43
|
-
consent: new Consent(),
|
44
|
-
};
|
47
|
+
application.register("analytics", AnalyticsController);
|
45
48
|
}
|
49
|
+
|
50
|
+
export const ui = window.ui;
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shimmer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jens Ravens
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -145,6 +145,20 @@ files:
|
|
145
145
|
- lib/shimmer/auth/user.rb
|
146
146
|
- lib/shimmer/controllers/files_controller.rb
|
147
147
|
- lib/shimmer/controllers/sitemaps_controller.rb
|
148
|
+
- lib/shimmer/form.rb
|
149
|
+
- lib/shimmer/form/builder.rb
|
150
|
+
- lib/shimmer/form/checkboxes_field.rb
|
151
|
+
- lib/shimmer/form/date_field.rb
|
152
|
+
- lib/shimmer/form/email_field.rb
|
153
|
+
- lib/shimmer/form/field.rb
|
154
|
+
- lib/shimmer/form/number_field.rb
|
155
|
+
- lib/shimmer/form/password_field.rb
|
156
|
+
- lib/shimmer/form/pdf_field.rb
|
157
|
+
- lib/shimmer/form/radio_field.rb
|
158
|
+
- lib/shimmer/form/select_field.rb
|
159
|
+
- lib/shimmer/form/text_area_field.rb
|
160
|
+
- lib/shimmer/form/text_field.rb
|
161
|
+
- lib/shimmer/form/time_field.rb
|
148
162
|
- lib/shimmer/helpers/meta_helper.rb
|
149
163
|
- lib/shimmer/jobs/sitemap_job.rb
|
150
164
|
- lib/shimmer/middlewares/cloudflare.rb
|
@@ -165,6 +179,7 @@ files:
|
|
165
179
|
- package.json
|
166
180
|
- rollup.config.js
|
167
181
|
- src/consent.ts
|
182
|
+
- src/controllers/analytics.ts
|
168
183
|
- src/controllers/consent.ts
|
169
184
|
- src/controllers/remote-navigation.ts
|
170
185
|
- src/index.ts
|