@studiometa/ui 0.2.5 → 0.2.6
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.
- package/atoms/Button/Button.twig +68 -0
- package/atoms/Button/StyledButton.twig +47 -0
- package/atoms/Cursor/Cursor.twig +28 -0
- package/atoms/Figure/Figure.twig +120 -0
- package/atoms/Icon/Icon.twig +13 -0
- package/atoms/LargeText/LargeText.twig +49 -0
- package/index.cjs +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -1
- package/molecules/Accordion/Accordion.twig +54 -0
- package/molecules/Modal/Modal.twig +108 -0
- package/molecules/Modal/StyledModal.twig +39 -0
- package/molecules/Panel/Panel.twig +73 -0
- package/molecules/Panel/StyledPanel.twig +28 -0
- package/molecules/Sticky/Sticky.twig +31 -0
- package/molecules/Tabs/Tabs.twig +20 -0
- package/organisms/Frame/Frame.cjs +40 -1
- package/organisms/Frame/Frame.d.ts +34 -1
- package/organisms/Frame/Frame.js +1 -1
- package/organisms/Frame/FrameTarget.cjs +3 -3
- package/organisms/Frame/FrameTarget.js +1 -1
- package/organisms/ImageGrid/ImageGrid.twig +42 -0
- package/package.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Button component.
|
|
5
|
+
*
|
|
6
|
+
* @param string $label
|
|
7
|
+
* @param string $tag
|
|
8
|
+
* @param string $href
|
|
9
|
+
* @param array $attr
|
|
10
|
+
* @param string $icon
|
|
11
|
+
* @param array|string $icon_classes
|
|
12
|
+
* @param 'start'|'end' $icon_position
|
|
13
|
+
* @param boolean $icon_only
|
|
14
|
+
*/
|
|
15
|
+
#}
|
|
16
|
+
|
|
17
|
+
{# Customizable tag with a default value. #}
|
|
18
|
+
{% set tag =
|
|
19
|
+
tag|default(href is defined or (attr is defined and attr.href is defined) ? 'a' : 'button')
|
|
20
|
+
%}
|
|
21
|
+
|
|
22
|
+
{# Default icon values #}
|
|
23
|
+
{% set icon_position = icon_position|default('start') %}
|
|
24
|
+
{% set icon_only = icon_only|default(false) %}
|
|
25
|
+
|
|
26
|
+
{# Final attributes are a merge of the defaults, the given and then the required #}
|
|
27
|
+
{% set attributes =
|
|
28
|
+
merge_html_attributes(
|
|
29
|
+
attr ?? null,
|
|
30
|
+
{
|
|
31
|
+
title: label,
|
|
32
|
+
type: tag == 'button' ? 'button' : false,
|
|
33
|
+
href: tag == 'a' and href is defined ? href : false,
|
|
34
|
+
aria_label: icon_only ? label : false
|
|
35
|
+
},
|
|
36
|
+
{ class: ['btn'] }
|
|
37
|
+
)
|
|
38
|
+
%}
|
|
39
|
+
|
|
40
|
+
{% set rendered_icon %}
|
|
41
|
+
{% block icon %}
|
|
42
|
+
{% include '@ui/atoms/Icon/Icon.twig' with {
|
|
43
|
+
name: icon ?? '',
|
|
44
|
+
classes: icon_classes ?? {
|
|
45
|
+
'mr-3': not icon_only and icon_position == 'start',
|
|
46
|
+
'ml-3': not icon_only and icon_position == 'end',
|
|
47
|
+
}
|
|
48
|
+
} %}
|
|
49
|
+
{% endblock %}
|
|
50
|
+
{% endset %}
|
|
51
|
+
|
|
52
|
+
{% html_element tag with attributes %}
|
|
53
|
+
{% block content %}
|
|
54
|
+
{% if icon is defined and icon_position == 'start' %}
|
|
55
|
+
{{ rendered_icon }}
|
|
56
|
+
{% endif %}
|
|
57
|
+
{% block label %}
|
|
58
|
+
{% if icon_only %}
|
|
59
|
+
<span class="sr-only">{{ label }}</span>
|
|
60
|
+
{% else %}
|
|
61
|
+
{{ label }}
|
|
62
|
+
{% endif %}
|
|
63
|
+
{% endblock %}
|
|
64
|
+
{% if icon is defined and icon_position == 'end' %}
|
|
65
|
+
{{ rendered_icon }}
|
|
66
|
+
{% endif %}
|
|
67
|
+
{% endblock %}
|
|
68
|
+
{% end_html_element %}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Styled button.
|
|
5
|
+
*
|
|
6
|
+
* @param 'primary'|'secondary'|'tertiary' $theme
|
|
7
|
+
*/
|
|
8
|
+
#}
|
|
9
|
+
|
|
10
|
+
{% extends '@ui/atoms/Button/Button.twig' %}
|
|
11
|
+
|
|
12
|
+
{% set icon_only = icon_only|default(false) %}
|
|
13
|
+
|
|
14
|
+
{% set theme = theme|default('primary') %}
|
|
15
|
+
|
|
16
|
+
{% set theme_shared = [
|
|
17
|
+
'rounded cursor-pointer transition',
|
|
18
|
+
'disabled:cursor-not-allowed',
|
|
19
|
+
{
|
|
20
|
+
'inline-block': icon is not defined,
|
|
21
|
+
'inline-flex items-center': icon is defined,
|
|
22
|
+
'px-6 py-4': not icon_only,
|
|
23
|
+
'p-4': icon_only
|
|
24
|
+
}
|
|
25
|
+
] %}
|
|
26
|
+
|
|
27
|
+
{% set theme_primary = ['text-white bg-black', 'hover:bg-opacity-75 disabled:bg-opacity-50'] %}
|
|
28
|
+
|
|
29
|
+
{% set theme_secondary = [
|
|
30
|
+
'ring ring-inset ring-2 ring-black ring-opacity-25',
|
|
31
|
+
{
|
|
32
|
+
'hover:ring-opacity-100': (attr is defined and attr.disabled is not defined)
|
|
33
|
+
or attr is not defined
|
|
34
|
+
}
|
|
35
|
+
] %}
|
|
36
|
+
|
|
37
|
+
{% set attr =
|
|
38
|
+
attr
|
|
39
|
+
|default({})
|
|
40
|
+
|merge({
|
|
41
|
+
class: [
|
|
42
|
+
theme_shared,
|
|
43
|
+
theme == 'primary' ? theme_primary : '',
|
|
44
|
+
theme == 'secondary' ? theme_secondary : ''
|
|
45
|
+
]
|
|
46
|
+
})
|
|
47
|
+
%}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Cursor component.
|
|
5
|
+
*
|
|
6
|
+
* @param array $attr
|
|
7
|
+
* Custom attributes for the root element.
|
|
8
|
+
*
|
|
9
|
+
* @block $content
|
|
10
|
+
* Custom content for the root element, defaults to `''`.
|
|
11
|
+
*/
|
|
12
|
+
#}
|
|
13
|
+
|
|
14
|
+
{% set attributes =
|
|
15
|
+
merge_html_attributes(
|
|
16
|
+
attr ?? null,
|
|
17
|
+
{
|
|
18
|
+
data_component: 'Cursor',
|
|
19
|
+
class: 'fixed top-0 left-0 w-12 h-12 -mt-6 -ml-6 rounded-full bg-black'
|
|
20
|
+
},
|
|
21
|
+
{ class: 'pointer-events-none' }
|
|
22
|
+
)
|
|
23
|
+
%}
|
|
24
|
+
|
|
25
|
+
<div {{ html_attributes(attributes) }}>
|
|
26
|
+
{% block content %}
|
|
27
|
+
{% endblock %}
|
|
28
|
+
</div>
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Figure component.
|
|
5
|
+
*
|
|
6
|
+
* @param string $src
|
|
7
|
+
* @param string $srcset
|
|
8
|
+
* @param string $sizes
|
|
9
|
+
* @param number $width
|
|
10
|
+
* @param number $height
|
|
11
|
+
* @param string $alt
|
|
12
|
+
* @param string $caption
|
|
13
|
+
* @param boolean $lazy
|
|
14
|
+
* @param 'cover'|'contain'|'fill'|'none' $fit
|
|
15
|
+
* Define how the image will fit.
|
|
16
|
+
* @param boolean $absolute
|
|
17
|
+
* Use absolute position on the image holder instead of relative.
|
|
18
|
+
* @param boolean $inline
|
|
19
|
+
* Wether to enable the display of the figure inline or not. When `inline`, the root element
|
|
20
|
+
* will have a max-width set corresponding to the `width` given. Use with caution.
|
|
21
|
+
* @param array $attr
|
|
22
|
+
* Custom attributes for the root element.
|
|
23
|
+
* @param array $inner_attr
|
|
24
|
+
* Custom attributes for the inner element.
|
|
25
|
+
* @param array $img_attr
|
|
26
|
+
* Custom attributes for the image element.
|
|
27
|
+
* @param array $caption_attr
|
|
28
|
+
* Custom attributes for the caption element.
|
|
29
|
+
*
|
|
30
|
+
* @block $caption
|
|
31
|
+
* Use this block to customize the image's caption.
|
|
32
|
+
*
|
|
33
|
+
* @todo
|
|
34
|
+
* - twicpics: use TwicPics to serve image at the right size
|
|
35
|
+
* - loading: display a loader while loading, the fallback image if there was an error
|
|
36
|
+
* - mode: img | background → is it really necessary with `object-fit-...`?
|
|
37
|
+
*/
|
|
38
|
+
#}
|
|
39
|
+
|
|
40
|
+
{% set absolute = absolute|default(false) %}
|
|
41
|
+
{% set inline = inline|default(false) %}
|
|
42
|
+
{% set fit = fit ?? null %}
|
|
43
|
+
{% set height = height|default(100) %}
|
|
44
|
+
{% set width = width|default(100) %}
|
|
45
|
+
{% set lazy = lazy ?? true %}
|
|
46
|
+
|
|
47
|
+
{%- set placeholder_markup -%}
|
|
48
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
|
49
|
+
viewbox="0 0 {{ width }} {{ height }}"
|
|
50
|
+
width="{{ width }}"
|
|
51
|
+
height="{{ height }}">
|
|
52
|
+
<rect x="0" y="0" width="{{ width }}" height="{{ height }}" fill="#eee" />
|
|
53
|
+
</svg>
|
|
54
|
+
{%- endset -%}
|
|
55
|
+
{% set generic_placeholder = 'data:image/svg+xml,%s'|format(placeholder_markup|url_encode) %}
|
|
56
|
+
|
|
57
|
+
{% set attributes =
|
|
58
|
+
merge_html_attributes(
|
|
59
|
+
attr ?? null,
|
|
60
|
+
{ data_component: 'Figure', class: ['figure', 'w-full'] },
|
|
61
|
+
{
|
|
62
|
+
style: { height: absolute ? '100%' : '', maxWidth: inline ? width ~ 'px' : '' },
|
|
63
|
+
data_option_lazy: lazy
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
%}
|
|
67
|
+
|
|
68
|
+
{% set img_attributes =
|
|
69
|
+
merge_html_attributes(
|
|
70
|
+
img_attr ?? null,
|
|
71
|
+
{ class: 'figure__img' },
|
|
72
|
+
{
|
|
73
|
+
class: [
|
|
74
|
+
'absolute inset-0 w-full max-w-none h-full',
|
|
75
|
+
{
|
|
76
|
+
'object-cover': fit == 'cover',
|
|
77
|
+
'object-contain': fit == 'contain',
|
|
78
|
+
'object-fill': fit == 'fill',
|
|
79
|
+
'object-none': fit == 'none'
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
src: lazy ? placeholder|default(generic_placeholder) : src,
|
|
83
|
+
data_src: lazy ? src|default(generic_placeholder) : src,
|
|
84
|
+
alt: alt|default(''),
|
|
85
|
+
width: width|default(false),
|
|
86
|
+
height: height|default(false),
|
|
87
|
+
srcset: srcset|default(false),
|
|
88
|
+
sizes: sizes|default(false),
|
|
89
|
+
data_ref: 'img'
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
%}
|
|
93
|
+
|
|
94
|
+
{% set inner_attributes =
|
|
95
|
+
merge_html_attributes(
|
|
96
|
+
inner_attr ?? null,
|
|
97
|
+
{ class: ['figure__inner'] },
|
|
98
|
+
{
|
|
99
|
+
class: absolute ? ['absolute', 'inset-0'] : ['relative', 'w-full', 'h-0'],
|
|
100
|
+
style: { paddingTop: absolute ? '' : height * 100 / width ~ '%' }
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
%}
|
|
104
|
+
|
|
105
|
+
{% set caption_attributes =
|
|
106
|
+
merge_html_attributes(caption_attr ?? null, { class: 'figure__caption' })
|
|
107
|
+
%}
|
|
108
|
+
|
|
109
|
+
<figure {{ html_attributes(attributes) }}>
|
|
110
|
+
<div {{ html_attributes(inner_attributes) }}>
|
|
111
|
+
<img {{ html_attributes(img_attributes) }} />
|
|
112
|
+
</div>
|
|
113
|
+
{% if caption is defined %}
|
|
114
|
+
{% block caption %}
|
|
115
|
+
<figcaption {{ html_attributes(caption_attributes) }}>
|
|
116
|
+
{{ caption }}
|
|
117
|
+
</figcaption>
|
|
118
|
+
{% endblock %}
|
|
119
|
+
{% endif %}
|
|
120
|
+
</figure>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Icon component
|
|
5
|
+
*
|
|
6
|
+
* @param string $name The file name of the icon.
|
|
7
|
+
* @param string[] $classes Additional classes.
|
|
8
|
+
*/
|
|
9
|
+
#}
|
|
10
|
+
|
|
11
|
+
<span class="icon {{ 'icon--%s'|format(name) }} {{ html_classes(classes|default('inline-block')) }}">
|
|
12
|
+
{{ source('@svg/%s.svg'|format(name), ignore_missing = true) }}
|
|
13
|
+
</span>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* LargeText component.
|
|
5
|
+
*
|
|
6
|
+
* @param string $content
|
|
7
|
+
* The text content.
|
|
8
|
+
* @param number $repeat
|
|
9
|
+
* The number of times the content should be repeated, defaults to 2.
|
|
10
|
+
* @param array $attr
|
|
11
|
+
* Custom attributes for the root element.
|
|
12
|
+
* @param array $target_attr
|
|
13
|
+
* Custom attributes for the target element.
|
|
14
|
+
*/
|
|
15
|
+
#}
|
|
16
|
+
|
|
17
|
+
{% set attributes =
|
|
18
|
+
merge_html_attributes(
|
|
19
|
+
attr ?? null,
|
|
20
|
+
{ data_component: 'LargeText' },
|
|
21
|
+
{ class: 'overflow-x-hidden pointer-events-none' }
|
|
22
|
+
)
|
|
23
|
+
%}
|
|
24
|
+
|
|
25
|
+
{% set target_attributes =
|
|
26
|
+
merge_html_attributes(
|
|
27
|
+
target_attr ?? null,
|
|
28
|
+
{ class: 'text-9xl font-bold' },
|
|
29
|
+
{ data_ref: 'target', class: 'relative inline-block whitespace-nowrap' }
|
|
30
|
+
)
|
|
31
|
+
%}
|
|
32
|
+
|
|
33
|
+
{% set position_factor = attributes.data_option_sensitivity is defined
|
|
34
|
+
and attributes.data_option_sensitivity < 0
|
|
35
|
+
? - 100
|
|
36
|
+
: 100
|
|
37
|
+
%}
|
|
38
|
+
|
|
39
|
+
<div {{ html_attributes(attributes) }}>
|
|
40
|
+
<span {{ html_attributes(target_attributes) }}>
|
|
41
|
+
{% for count in 1..repeat ?? 2 %}
|
|
42
|
+
{% set content_attributes = {
|
|
43
|
+
style: { left: loop.first ? '' : loop.index0 * position_factor ~ '%' },
|
|
44
|
+
class: { 'absolute top-0': not loop.first }
|
|
45
|
+
} %}
|
|
46
|
+
<span {{ html_attributes(content_attributes) }}> {{ content }}</span>
|
|
47
|
+
{% endfor %}
|
|
48
|
+
</span>
|
|
49
|
+
</div>
|
package/index.cjs
CHANGED
|
@@ -19,4 +19,5 @@ module.exports = __toCommonJS(ui_exports);
|
|
|
19
19
|
__reExport(ui_exports, require("./primitives/index.cjs"), module.exports);
|
|
20
20
|
__reExport(ui_exports, require("./atoms/index.cjs"), module.exports);
|
|
21
21
|
__reExport(ui_exports, require("./molecules/index.cjs"), module.exports);
|
|
22
|
+
__reExport(ui_exports, require("./organisms/index.cjs"), module.exports);
|
|
22
23
|
if (module.exports.default) module.exports = module.exports.default;
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./primitives/index.js";export*from"./atoms/index.js";export*from"./molecules/index.js";
|
|
1
|
+
export*from"./primitives/index.js";export*from"./atoms/index.js";export*from"./molecules/index.js";export*from"./organisms/index.js";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Accordion component.
|
|
5
|
+
*
|
|
6
|
+
* @param array<{ title: string, content: unknown, attr: array }> $items
|
|
7
|
+
* The items of the accordion.
|
|
8
|
+
* @param array $attr
|
|
9
|
+
* Use it to customize the root element attributes.
|
|
10
|
+
* @param array $item_attr
|
|
11
|
+
* Use it to customize each item element attributes.
|
|
12
|
+
* @param array $item_container_attr
|
|
13
|
+
* Use it to customize each item container element attributes.
|
|
14
|
+
*
|
|
15
|
+
* @block $title
|
|
16
|
+
* Use it to customize each item's title.
|
|
17
|
+
* @block $content
|
|
18
|
+
* Use it to customize each item's content.
|
|
19
|
+
*/
|
|
20
|
+
#}
|
|
21
|
+
|
|
22
|
+
{% set attributes = merge_html_attributes(attr ?? null, { data_component: 'Accordion' }) %}
|
|
23
|
+
|
|
24
|
+
<div {{ html_attributes(attributes) }}>
|
|
25
|
+
{% for item in items %}
|
|
26
|
+
{% set item_attributes = merge_html_attributes(item_attr ?? null, { data_component: 'AccordionItem' }, item.attr ?? null) %}
|
|
27
|
+
{% set is_open = item_attributes.data_option_is_open ?? false %}
|
|
28
|
+
<div {{ html_attributes(item_attributes) }}>
|
|
29
|
+
<button data-ref="btn" class="block w-full" aria-expanded="{{ is_open ? 'true' : 'false' }}">
|
|
30
|
+
{% block title %}
|
|
31
|
+
{{ item.title }}
|
|
32
|
+
{% endblock %}
|
|
33
|
+
</button>
|
|
34
|
+
{% set item_container_attributes =
|
|
35
|
+
merge_html_attributes(
|
|
36
|
+
item_container_attr ?? null,
|
|
37
|
+
{},
|
|
38
|
+
{
|
|
39
|
+
data_ref: 'container',
|
|
40
|
+
style: { visibility: is_open ? '' : 'hidden', height: is_open ? '' : '0' },
|
|
41
|
+
class: 'relative overflow-hidden'
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
%}
|
|
45
|
+
<div {{ html_attributes(item_container_attributes) }}>
|
|
46
|
+
<div data-ref="content" aria-hidden="true">
|
|
47
|
+
{% block content %}
|
|
48
|
+
{{ item.content }}
|
|
49
|
+
{% endblock %}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
{% endfor %}
|
|
54
|
+
</div>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Modal component.
|
|
5
|
+
*
|
|
6
|
+
* @param array $attr
|
|
7
|
+
* Use it to customize the root element attributes.
|
|
8
|
+
* @param array $modal_attr
|
|
9
|
+
* Use it to customize the modal element attributes.
|
|
10
|
+
* @param array $overlay_attr
|
|
11
|
+
* Use it to customize the overlay element attributes.
|
|
12
|
+
* @param array $wrapper_attr
|
|
13
|
+
* Use it to customize the wrapper element attributes.
|
|
14
|
+
* @param array $container_atrr
|
|
15
|
+
* Use it to customize the container element attributes.
|
|
16
|
+
* @param array $content_atrr
|
|
17
|
+
* Use it to customize the content element attributes.
|
|
18
|
+
*
|
|
19
|
+
* @block $open
|
|
20
|
+
* Use this block to customize the open trigger button.
|
|
21
|
+
* @block $close
|
|
22
|
+
* Use this block to customize the close trigger button.
|
|
23
|
+
* @block $content
|
|
24
|
+
* Use this block to set the modal's content.
|
|
25
|
+
*/
|
|
26
|
+
#}
|
|
27
|
+
|
|
28
|
+
{# Root attributes #}
|
|
29
|
+
{% set attributes = merge_html_attributes(attr ?? null, { data_component: 'Modal' }) %}
|
|
30
|
+
|
|
31
|
+
{# Modal attributes #}
|
|
32
|
+
{% set modal_attributes =
|
|
33
|
+
merge_html_attributes(
|
|
34
|
+
modal_attr ?? null,
|
|
35
|
+
{ class: 'z-goku fixed inset-0' },
|
|
36
|
+
{
|
|
37
|
+
data_ref: 'modal',
|
|
38
|
+
role: 'dialog',
|
|
39
|
+
aria_modal: true,
|
|
40
|
+
aria_hidden: true,
|
|
41
|
+
style: { opacity: '0', pointer_events: 'none', visibility: 'hidden' }
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
%}
|
|
45
|
+
|
|
46
|
+
{# Overlay attributes #}
|
|
47
|
+
{% set overlay_attributes =
|
|
48
|
+
merge_html_attributes(
|
|
49
|
+
overlay_attr ?? null,
|
|
50
|
+
{ class: 'z-under absolute inset-0 bg-black bg-opacity-75' },
|
|
51
|
+
{ data_ref: 'overlay', tabindex: '-1' }
|
|
52
|
+
)
|
|
53
|
+
%}
|
|
54
|
+
|
|
55
|
+
{% set wrapper_attributes =
|
|
56
|
+
merge_html_attributes(
|
|
57
|
+
wrapper_attr ?? null,
|
|
58
|
+
{ class: 'absolute inset-0 flex items-center justify-center' },
|
|
59
|
+
{ class: 'pointer-events-none' }
|
|
60
|
+
)
|
|
61
|
+
%}
|
|
62
|
+
|
|
63
|
+
{# Container attributes #}
|
|
64
|
+
{% set container_attributes =
|
|
65
|
+
merge_html_attributes(
|
|
66
|
+
container_attr ?? null,
|
|
67
|
+
{ class: 'bg-white rounded shadow-l' },
|
|
68
|
+
{
|
|
69
|
+
data_ref: 'container',
|
|
70
|
+
class: 'z-above relative max-h-full overflow-x-hidden overflow-y-auto pointer-events-auto'
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
%}
|
|
74
|
+
|
|
75
|
+
{# Content attributes #}
|
|
76
|
+
{% set content_attributes =
|
|
77
|
+
merge_html_attributes(
|
|
78
|
+
content_attr ?? null,
|
|
79
|
+
{ class: 'max-w-3xl p-16 pt-20' },
|
|
80
|
+
{ data_ref: 'content' }
|
|
81
|
+
)
|
|
82
|
+
%}
|
|
83
|
+
|
|
84
|
+
<div {{ html_attributes(attributes) }}>
|
|
85
|
+
{% block open %}
|
|
86
|
+
{% include '@ui/atoms/Button/Button.twig' with { label: 'Open', attr: { data_ref: 'open' } } %}
|
|
87
|
+
{% endblock %}
|
|
88
|
+
<div {{ html_attributes(modal_attributes) }}>
|
|
89
|
+
<div {{ html_attributes(overlay_attributes) }}></div>
|
|
90
|
+
<div {{ html_attributes(wrapper_attributes) }}>
|
|
91
|
+
<div {{ html_attributes(container_attributes) }}>
|
|
92
|
+
{% block close %}
|
|
93
|
+
<div class="absolute top-0 right-0 m-2">
|
|
94
|
+
{% include '@ui/atoms/Button/Button.twig' with {
|
|
95
|
+
label: 'Close',
|
|
96
|
+
attr: { data_ref: 'close' }
|
|
97
|
+
} %}
|
|
98
|
+
</div>
|
|
99
|
+
{% endblock %}
|
|
100
|
+
<div {{ html_attributes(content_attributes) }}>
|
|
101
|
+
{% block content %}
|
|
102
|
+
No content.
|
|
103
|
+
{% endblock %}
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Modal component.
|
|
5
|
+
*
|
|
6
|
+
* @param array $attr
|
|
7
|
+
* Use it to customize the root element attributes.
|
|
8
|
+
* @param array $modal_attr
|
|
9
|
+
* Use it to customize the modal element attributes.
|
|
10
|
+
* @param array $overlay_attr
|
|
11
|
+
* Use it to customize the overlay element attributes.
|
|
12
|
+
* @param array $container_atrr
|
|
13
|
+
* Use it to customize the container element attributes.
|
|
14
|
+
* @param array $content_atrr
|
|
15
|
+
* Use it to customize the content element attributes.
|
|
16
|
+
*
|
|
17
|
+
* @block $open
|
|
18
|
+
* Use this block to customize the open trigger button.
|
|
19
|
+
* @block $close
|
|
20
|
+
* Use this block to customize the close trigger button.
|
|
21
|
+
* @block $content
|
|
22
|
+
* Use this block to set the modal's content.
|
|
23
|
+
*/
|
|
24
|
+
#}
|
|
25
|
+
|
|
26
|
+
{% extends '@ui/molecules/Modal/Modal.twig' %}
|
|
27
|
+
|
|
28
|
+
{% block open %}
|
|
29
|
+
{% include '@ui/atoms/Button/StyledButton.twig' with { label: 'Open', attr: { data_ref: 'open' } } %}
|
|
30
|
+
{% endblock %}
|
|
31
|
+
|
|
32
|
+
{% block close %}
|
|
33
|
+
<div class="absolute top-0 right-0 m-2">
|
|
34
|
+
{% include '@ui/atoms/Button/StyledButton.twig' with {
|
|
35
|
+
label: 'Close',
|
|
36
|
+
attr: { data_ref: 'close' }
|
|
37
|
+
} %}
|
|
38
|
+
</div>
|
|
39
|
+
{% endblock %}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Panel component.
|
|
5
|
+
*
|
|
6
|
+
* @param 'top'|'right'|'bottom'|'left' $position
|
|
7
|
+
* Position of the panel.
|
|
8
|
+
* @param array $wrapper_attr
|
|
9
|
+
* Custom attributes for the wrapper element.
|
|
10
|
+
*/
|
|
11
|
+
#}
|
|
12
|
+
|
|
13
|
+
{% extends '@ui/molecules/Modal/Modal.twig' %}
|
|
14
|
+
|
|
15
|
+
{% set position = position ?? 'left' %}
|
|
16
|
+
|
|
17
|
+
{% set attr =
|
|
18
|
+
merge_html_attributes(attr ?? null, { data_component: 'Panel', data_option_position: position })
|
|
19
|
+
%}
|
|
20
|
+
|
|
21
|
+
{% set overlay_attr =
|
|
22
|
+
merge_html_attributes(
|
|
23
|
+
overlay_attr ?? null,
|
|
24
|
+
{ class: 'z-under absolute inset-0 bg-black bg-opacity-75' },
|
|
25
|
+
{ class: 'transition duration-500 opacity-0' }
|
|
26
|
+
)
|
|
27
|
+
%}
|
|
28
|
+
|
|
29
|
+
{% set wrapper_attr =
|
|
30
|
+
merge_html_attributes(
|
|
31
|
+
wrapper_attr ?? null,
|
|
32
|
+
{},
|
|
33
|
+
{
|
|
34
|
+
class: [
|
|
35
|
+
'z-above absolute top-0 w-full h-full flex pointer-events-none',
|
|
36
|
+
{
|
|
37
|
+
'top-0 left-0 items-start justify-center': position == 'top',
|
|
38
|
+
'top-0 right-0 items-center justify-end': position == 'right',
|
|
39
|
+
'bottom-0 left-0 items-end justify-center': position == 'bottom',
|
|
40
|
+
'top-0 left-0 items-center justify-start': position == 'left'
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
%}
|
|
46
|
+
|
|
47
|
+
{% set container_attr =
|
|
48
|
+
merge_html_attributes(
|
|
49
|
+
container_attr ?? null,
|
|
50
|
+
{
|
|
51
|
+
class: [
|
|
52
|
+
'max-w-2xl',
|
|
53
|
+
{
|
|
54
|
+
'w-full': position == 'top',
|
|
55
|
+
'h-full': position == 'right',
|
|
56
|
+
'w-full': position == 'bottom',
|
|
57
|
+
'h-full': position == 'left'
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
class: [
|
|
63
|
+
'transform transition duration-500',
|
|
64
|
+
{
|
|
65
|
+
'-translate-y-full': position == 'top',
|
|
66
|
+
'translate-x-full': position == 'right',
|
|
67
|
+
'translate-y-full': position == 'bottom',
|
|
68
|
+
'-translate-x-full': position == 'left'
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
%}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Panel component.
|
|
5
|
+
*
|
|
6
|
+
* @param 'left'|'right' $position
|
|
7
|
+
* Position of the panel.
|
|
8
|
+
* @param array $wrapper_attr
|
|
9
|
+
* Custom attributes for the wrapper element.
|
|
10
|
+
*/
|
|
11
|
+
#}
|
|
12
|
+
|
|
13
|
+
{% extends '@ui/molecules/Panel/Panel.twig' %}
|
|
14
|
+
|
|
15
|
+
{% set container_attr = merge_html_attributes(container_attr ?? null, required={ class: 'bg-white' }) %}
|
|
16
|
+
|
|
17
|
+
{% block open %}
|
|
18
|
+
{% include '@ui/atoms/Button/StyledButton.twig' with { label: 'Open', attr: { data_ref: 'open' } } %}
|
|
19
|
+
{% endblock %}
|
|
20
|
+
|
|
21
|
+
{% block close %}
|
|
22
|
+
<div class="absolute top-0 right-0 m-2">
|
|
23
|
+
{% include '@ui/atoms/Button/StyledButton.twig' with {
|
|
24
|
+
label: 'Close',
|
|
25
|
+
attr: { data_ref: 'close' }
|
|
26
|
+
} %}
|
|
27
|
+
</div>
|
|
28
|
+
{% endblock %}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Sticky component.
|
|
5
|
+
*
|
|
6
|
+
* @param array $attr
|
|
7
|
+
* Custom attributes for the root element.
|
|
8
|
+
* @param array $innner_attr
|
|
9
|
+
* Custom attributes for the inner element.
|
|
10
|
+
*
|
|
11
|
+
* @block $content
|
|
12
|
+
* Use this block to set the content of the sticky element.
|
|
13
|
+
*/
|
|
14
|
+
#}
|
|
15
|
+
|
|
16
|
+
{% set default_attributes = { data_component: 'Sticky', class: 'z-10 sticky top-0 w-full' } %}
|
|
17
|
+
{% set attributes = attr|default({})|merge_html_attributes(default_attributes) %}
|
|
18
|
+
|
|
19
|
+
{% set default_inner_attributes = { data_ref: 'inner', class:'transition duration-500 ease-out-expo' } %}
|
|
20
|
+
{% set inner_attributes = inner_attr|default({})|merge_html_attributes(default_inner_attributes) %}
|
|
21
|
+
|
|
22
|
+
<div {{ html_attributes(attributes) }}>
|
|
23
|
+
<div data-ref="sentinelRef"
|
|
24
|
+
data-component="Sentinel"
|
|
25
|
+
class="absolute bottom-full w-full h-px pointer-events-none"></div>
|
|
26
|
+
<div {{ html_attributes(inner_attributes) }}>
|
|
27
|
+
{% block content %}
|
|
28
|
+
{{ content ?? '' }}
|
|
29
|
+
{% endblock %}
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<div data-component="Tabs" data-option-styles='{ "btn": { "open": { "borderBottomColor": "#fff" } } }'>
|
|
2
|
+
{% block title_wrapper %}
|
|
3
|
+
{% for item in items %}
|
|
4
|
+
<button data-ref="btn[]" style="border-bottom: 1px solid transparent;">
|
|
5
|
+
{% block title %}
|
|
6
|
+
{{ item.title }}
|
|
7
|
+
{% endblock %}
|
|
8
|
+
</button>
|
|
9
|
+
{% endfor %}
|
|
10
|
+
{% endblock %}
|
|
11
|
+
{% block content_wrapper %}
|
|
12
|
+
{% for item in items %}
|
|
13
|
+
<div data-ref="content[]" aria-hidden="false">
|
|
14
|
+
{% block content %}
|
|
15
|
+
{{ item.content }}
|
|
16
|
+
{% endblock %}
|
|
17
|
+
</div>
|
|
18
|
+
{% endfor %}
|
|
19
|
+
{% endblock %}
|
|
20
|
+
</div>
|
|
@@ -35,6 +35,12 @@ var import_utils = require("@studiometa/js-toolkit/utils");
|
|
|
35
35
|
var import_FrameAnchor = __toESM(require("./FrameAnchor.cjs"), 1);
|
|
36
36
|
var import_FrameForm = __toESM(require("./FrameForm.cjs"), 1);
|
|
37
37
|
var import_FrameTarget = __toESM(require("./FrameTarget.cjs"), 1);
|
|
38
|
+
function getScrollPosition() {
|
|
39
|
+
return {
|
|
40
|
+
left: window.pageXOffset,
|
|
41
|
+
top: window.pageYOffset
|
|
42
|
+
};
|
|
43
|
+
}
|
|
38
44
|
var cache = /* @__PURE__ */ new Map();
|
|
39
45
|
var _Frame = class extends import_js_toolkit.Base {
|
|
40
46
|
get id() {
|
|
@@ -58,6 +64,35 @@ var _Frame = class extends import_js_toolkit.Base {
|
|
|
58
64
|
get directChildFrameTarget() {
|
|
59
65
|
return this.getDirectChild("FrameTarget");
|
|
60
66
|
}
|
|
67
|
+
mounted() {
|
|
68
|
+
if (this.$options.history) {
|
|
69
|
+
window.addEventListener("popstate", this);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
destroyed() {
|
|
73
|
+
window.removeEventListener("popstate", this);
|
|
74
|
+
}
|
|
75
|
+
handleEvent(event) {
|
|
76
|
+
if (event.type === "popstate") {
|
|
77
|
+
this.onWindowPopstate(event);
|
|
78
|
+
}
|
|
79
|
+
if (event.type === "beforeunload") {
|
|
80
|
+
this.onWindowUnload();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
onWindowUnload() {
|
|
84
|
+
const { history } = window;
|
|
85
|
+
if (!history.state) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
history.replaceState({
|
|
89
|
+
...history.state,
|
|
90
|
+
scroll: getScrollPosition()
|
|
91
|
+
}, "");
|
|
92
|
+
}
|
|
93
|
+
onWindowPopstate(event) {
|
|
94
|
+
this.goTo(window.location.href, event.state);
|
|
95
|
+
}
|
|
61
96
|
onFrameAnchorFrameClick(event, index) {
|
|
62
97
|
if (!this.directChildFrameAnchor.includes(this.$children.FrameAnchor[index])) {
|
|
63
98
|
return;
|
|
@@ -82,7 +117,7 @@ var _Frame = class extends import_js_toolkit.Base {
|
|
|
82
117
|
parseHTML(string = "") {
|
|
83
118
|
return new DOMParser().parseFromString(string, "text/html");
|
|
84
119
|
}
|
|
85
|
-
async goTo(url) {
|
|
120
|
+
async goTo(url, scroll = null) {
|
|
86
121
|
this.$log("goTo", url);
|
|
87
122
|
const parsedUrl = new URL(url);
|
|
88
123
|
if (parsedUrl.origin !== window.location.origin) {
|
|
@@ -104,6 +139,10 @@ var _Frame = class extends import_js_toolkit.Base {
|
|
|
104
139
|
document.title = doc.title;
|
|
105
140
|
(0, import_utils.historyPush)({ path: parsedUrl.pathname, search: parsedUrl.searchParams });
|
|
106
141
|
}
|
|
142
|
+
if (scroll) {
|
|
143
|
+
document.scrollingElement.scrollTop = scroll.top;
|
|
144
|
+
document.scrollingElement.scrollLeft = scroll.left;
|
|
145
|
+
}
|
|
107
146
|
await (0, import_utils.nextFrame)();
|
|
108
147
|
this.$root.$update();
|
|
109
148
|
await (0, import_utils.nextFrame)();
|
|
@@ -63,6 +63,35 @@ export default class Frame extends Base {
|
|
|
63
63
|
* @returns {FrameTarget[]}
|
|
64
64
|
*/
|
|
65
65
|
get directChildFrameTarget(): FrameTarget[];
|
|
66
|
+
/**
|
|
67
|
+
* Mounted hook.
|
|
68
|
+
* @returns {void}
|
|
69
|
+
*/
|
|
70
|
+
mounted(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Destroyed hook.
|
|
73
|
+
* @returns {void}
|
|
74
|
+
*/
|
|
75
|
+
destroyed(): void;
|
|
76
|
+
/**
|
|
77
|
+
* Dispatch events.
|
|
78
|
+
* @param {PopStateEvent} event
|
|
79
|
+
* @returns {void}
|
|
80
|
+
*/
|
|
81
|
+
handleEvent(event: PopStateEvent): void;
|
|
82
|
+
/**
|
|
83
|
+
* Prevent scroll top on unload.
|
|
84
|
+
*
|
|
85
|
+
* @returns {void}
|
|
86
|
+
*/
|
|
87
|
+
onWindowUnload(): void;
|
|
88
|
+
/**
|
|
89
|
+
* Go to the previous URL on `popstate` event.
|
|
90
|
+
*
|
|
91
|
+
* @param {PopStateEvent} event
|
|
92
|
+
* @returns {void}
|
|
93
|
+
*/
|
|
94
|
+
onWindowPopstate(event: PopStateEvent): void;
|
|
66
95
|
/**
|
|
67
96
|
* Prevent click on `FrameAnchor`.
|
|
68
97
|
*
|
|
@@ -90,9 +119,13 @@ export default class Frame extends Base {
|
|
|
90
119
|
/**
|
|
91
120
|
* Go to the given url.
|
|
92
121
|
* @param {string} url
|
|
122
|
+
* @param {null|{ top: number, left: number }} [scroll]
|
|
93
123
|
* @returns {Promise<void>}
|
|
94
124
|
*/
|
|
95
|
-
goTo(url: string
|
|
125
|
+
goTo(url: string, scroll?: null | {
|
|
126
|
+
top: number;
|
|
127
|
+
left: number;
|
|
128
|
+
}): Promise<void>;
|
|
96
129
|
/**
|
|
97
130
|
* Fetch the given url.
|
|
98
131
|
* @param {string} url
|
package/organisms/Frame/Frame.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Base as
|
|
1
|
+
import{Base as m}from"@studiometa/js-toolkit";import{nextFrame as c,historyPush as f}from"@studiometa/js-toolkit/utils";import p from"./FrameAnchor.js";import g from"./FrameForm.js";import F from"./FrameTarget.js";function u(){return{left:window.pageXOffset,top:window.pageYOffset}}const s=new Map;class a extends m{static config={name:"Frame",emits:["before-fetch","after-fetch","before-leave","after-leave","before-content","after-content","before-enter","after-enter"],log:!0,components:{FrameAnchor:p,FrameForm:g,FrameTarget:F,Frame:a},options:{history:Boolean}};get id(){return this.$el.id}getDirectChild(e){return this.$children[e]?this.$children.Frame?this.$children[e].filter(t=>this.$children.Frame.every(r=>r.$children[e]?!r.$children[e].includes(t):!0)):this.$children[e]:[]}get directChildFrameAnchor(){return this.getDirectChild("FrameAnchor")}get directChildFrameForm(){return this.getDirectChild("form")}get directChildFrameTarget(){return this.getDirectChild("FrameTarget")}mounted(){this.$options.history&&window.addEventListener("popstate",this)}destroyed(){window.removeEventListener("popstate",this)}handleEvent(e){e.type==="popstate"&&this.onWindowPopstate(e),e.type==="beforeunload"&&this.onWindowUnload()}onWindowUnload(){const{history:e}=window;!e.state||e.replaceState({...e.state,scroll:u()},"")}onWindowPopstate(e){this.goTo(window.location.href,e.state)}onFrameAnchorFrameClick(e,t){if(!this.directChildFrameAnchor.includes(this.$children.FrameAnchor[t]))return;this.$log("onAFrameClick",e,t),e.preventDefault();const r=this.$children.FrameAnchor[t];r.href!==window.location.href&&this.goTo(r.href)}onFrameFormFrameSubmit(e,t){if(!this.directChildFrameForm.includes(this.$children.FrameForm[t]))return;this.$log("onFrameFormFrameSubmit",e),e.preventDefault();const r=this.$children.FrameForm[t];this.goTo(r.action)}parseHTML(e=""){return new DOMParser().parseFromString(e,"text/html")}async goTo(e,t=null){this.$log("goTo",e);const r=new URL(e);if(r.origin!==window.location.origin)throw new Error("Cross origin request are not allowed.");this.$emit("before-fetch",e);const i=await this.fetch(e),n=this.parseHTML(i),l=n.querySelector(`#${this.id}`),h=new a(l);h.$children.registerAll(),this.$emit("after-fetch",e,i),this.$emit("before-leave"),await Promise.all(this.directChildFrameTarget.map(o=>o.leave())),this.$emit("after-leave"),this.$emit("before-content"),this.directChildFrameTarget.map((o,d)=>o.updateContent(h.directChildFrameTarget[d])),this.$options.history&&(document.title=n.title,f({path:r.pathname,search:r.searchParams})),t&&(document.scrollingElement.scrollTop=t.top,document.scrollingElement.scrollLeft=t.left),await c(),this.$root.$update(),await c(),this.$emit("after-content"),this.$emit("before-enter"),await Promise.all(this.directChildFrameTarget.map(o=>o.enter())),this.$emit("after-enter")}async fetch(e){const t=s.get(e);if(t)return t.status==="pending"?t.promise:t.content;const r=fetch(e);try{s.set(e,{promise:r,status:"pending",content:void 0});const i=await r.then(n=>n.text());return s.set(e,{promise:r,status:"resolved",content:i}),i}catch(i){return s.set(e,{promise:r,status:"error",content:i}),i}}}export{a as default};
|
|
@@ -40,19 +40,19 @@ var _FrameTarget = class extends import_primitives.Transition {
|
|
|
40
40
|
}
|
|
41
41
|
async enter() {
|
|
42
42
|
this.$log("enter");
|
|
43
|
-
const { enterFrom: from, enterActive: active, enterTo: to, leaveTo } = this.$options;
|
|
43
|
+
const { enterFrom: from, enterActive: active, enterTo: to, leaveTo, enterKeep } = this.$options;
|
|
44
44
|
const transitionStyles = { from, active, to };
|
|
45
45
|
switch (this.$options.mode) {
|
|
46
46
|
case "append":
|
|
47
47
|
case "prepend":
|
|
48
48
|
await Promise.all(Array.from(this.$el.children).filter((child) => from.split(" ").every((className) => child.classList.contains(className))).map((child) => {
|
|
49
|
-
return (0, import_utils.transition)(child, transitionStyles);
|
|
49
|
+
return (0, import_utils.transition)(child, transitionStyles, enterKeep && "keep");
|
|
50
50
|
}));
|
|
51
51
|
break;
|
|
52
52
|
case "replace":
|
|
53
53
|
default:
|
|
54
54
|
transitionStyles.from = Array.from(new Set([from, leaveTo].flat())).join(" ");
|
|
55
|
-
await (0, import_utils.transition)(this.$el, transitionStyles);
|
|
55
|
+
await (0, import_utils.transition)(this.$el, transitionStyles, enterKeep && "keep");
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
updateContent(newTarget) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{transition as
|
|
1
|
+
import{transition as a}from"@studiometa/js-toolkit/utils";import{Transition as r}from"../../primitives/index.js";class o extends r{static config={...r.config,name:"FrameTarget",log:!0,options:{...r.config.options,mode:{type:String,default:"replace"},id:String}};static __INSERT_MODES={prepend:"afterbegin",append:"beforeend"};get $options(){const e=super.$options;return e.leaveKeep=!0,e}get id(){return this.$options.id??this.$el.id}async enter(){this.$log("enter");const{enterFrom:e,enterActive:t,enterTo:p,leaveTo:l,enterKeep:s}=this.$options,i={from:e,active:t,to:p};switch(this.$options.mode){case"append":case"prepend":await Promise.all(Array.from(this.$el.children).filter(n=>e.split(" ").every(c=>n.classList.contains(c))).map(n=>a(n,i,s&&"keep")));break;case"replace":default:i.from=Array.from(new Set([e,l].flat())).join(" "),await a(this.$el,i,s&&"keep")}}updateContent(e){switch(this.$options.mode){case"prepend":case"append":Array.from(e.$el.children).forEach(t=>{t.classList.add(...this.$options.enterFrom.split(" "))}),this.$el.insertAdjacentHTML(o.__INSERT_MODES[this.$options.mode],e.$el.innerHTML);break;case"replace":default:this.$el.innerHTML=e.$el.innerHTML;break}}}export{o as default};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{#
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* Image Grid.
|
|
5
|
+
*
|
|
6
|
+
* @param array<Figure> $images
|
|
7
|
+
* List of images to display. See Figure.twig for the composition of each image.
|
|
8
|
+
* @param array $attr
|
|
9
|
+
* Custom attributes for the root element.
|
|
10
|
+
* @param array $inner_attr
|
|
11
|
+
* Custom attributes for the inner element.
|
|
12
|
+
* @param array $image_attr
|
|
13
|
+
* Custom attributes for the images' elements.
|
|
14
|
+
*/
|
|
15
|
+
#}
|
|
16
|
+
|
|
17
|
+
{% set attributes = merge_html_attributes(attr ?? null, { class: 'image-grid' }) %}
|
|
18
|
+
{% set inner_attributes =
|
|
19
|
+
merge_html_attributes(inner_attr ?? null, { class: 'image-grid__inner s:grid grid-cols-12 gap-10' })
|
|
20
|
+
%}
|
|
21
|
+
|
|
22
|
+
<div {{ html_attributes(attributes) }}>
|
|
23
|
+
<div {{ html_attributes(inner_attributes) }}>
|
|
24
|
+
{% for image in images %}
|
|
25
|
+
{% set modulo = loop.index % 5 %}
|
|
26
|
+
{% set image_attributes = merge_html_attributes(image_attr ?? null, {
|
|
27
|
+
class: 'image-grid__img'
|
|
28
|
+
}, {
|
|
29
|
+
class: {
|
|
30
|
+
's:col-span-7': modulo == 1 or modulo == 4,
|
|
31
|
+
's:col-span-5 mt-10': modulo == 2,
|
|
32
|
+
's:col-span-5 mt-10 clear-m-left': modulo == 3 and not loop.last,
|
|
33
|
+
's:col-start-2 s:col-end-11': modulo == 0 or (modulo in [0,3] and loop.last),
|
|
34
|
+
's:col-start-2 s:col-end-13': loop.first and loop.last
|
|
35
|
+
}
|
|
36
|
+
}) %}
|
|
37
|
+
<div {{ html_attributes(image_attributes) }}>
|
|
38
|
+
{% include '@ui/atoms/Figure/Figure.twig' with image only %}
|
|
39
|
+
</div>
|
|
40
|
+
{% endfor %}
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|