<% for (const element of segmented.elements) { %>
<%- include('./segmented-element', {element: element}); %>
<% } %>
diff --git a/src/component/table/.package.yml b/src/component/table/.package.yml
index 8c7142e81..632ecc86b 100644
--- a/src/component/table/.package.yml
+++ b/src/component/table/.package.yml
@@ -7,3 +7,19 @@ style:
- core
script:
- core
+ - checkbox
+ - tooltip
+example:
+ style:
+ - alert
+ - badge
+ - button
+ - checkbox
+ - input
+ - link
+ - pagination
+ - select
+ - segmented
+ - tag
+ - toggle
+ - tooltip
diff --git a/src/component/table/deprecated/example/index.ejs b/src/component/table/deprecated/example/index.ejs
new file mode 100755
index 000000000..f713ec21f
--- /dev/null
+++ b/src/component/table/deprecated/example/index.ejs
@@ -0,0 +1,27 @@
+<%
+const sample = getSample(include);
+let data = { caption: 'Titre du tableau (caption)', col: 6, row: 3};
+let dataBordered = { caption: 'Titre du tableau (caption)', bordered: true, col: 6, row: 3};
+let dataNoScroll = { caption: 'Titre du tableau (caption) non scrollable', noScroll: true, col: 6};
+let dataLayoutFixed = { caption: 'Titre du tableau (caption) fixé', layout: "fixed", col: 3};
+let dataNoCaption = { caption: 'Titre du tableau (caption) caché', noCaption: true, col: 6};
+let dataCaptionBottom = { caption: 'Titre du tableau (caption) en bas', captionBottom: true, col: 6};
+
+// @TODO: revoir les grilles avec layout
+%>
+
+<%- sample('Tableau par défaut', './sample/table-default', {table:data}, true) %>
+
+<%- sample('Tableau avec bordure', './sample/table-default', {table:dataBordered}, true) %>
+
+<%- sample('Tableau non scrollable', './sample/table-default', {table:dataNoScroll}, true) %>
+
+<%- sample('Tableau colonnes fixées (layout-fixed)', './sample/table-default', {table:dataLayoutFixed}, true) %>
+
+<%- sample('Tableau avec titre invisible', './sample/table-default', {table:dataNoCaption}, true) %>
+
+<%- sample('Tableau avec titre en bas', './sample/table-default', {table:dataCaptionBottom}, true) %>
+
+<%- sample('Tableau accentué', './sample/table-default', {table: {...data, accent:'green-emeraude'}}, true) %>
+
+<%- sample('Tableau accentué avec bordure', './sample/table-default', {table:{...dataBordered, accent:'green-emeraude'}}, true) %>
diff --git a/src/component/table/deprecated/example/sample/table-default.ejs b/src/component/table/deprecated/example/sample/table-default.ejs
new file mode 100755
index 000000000..5f220727d
--- /dev/null
+++ b/src/component/table/deprecated/example/sample/table-default.ejs
@@ -0,0 +1,25 @@
+<%
+const table = locals.table || {};
+const col = table.col || 2;
+const row = table.row || 2;
+let data = {
+ caption: table.caption || undefined,
+ bordered: table.bordered || false,
+ noScroll: table.noScroll || false,
+ noCaption: table.noCaption || false,
+ captionBottom: table.captionBottom || false,
+ accent: table.accent || undefined,
+ layout: table.layout
+}
+
+data.data = new Array(row);
+for(let r = 0; r < row; r++) {
+ data.data[r] = new Array(col);
+ for(let c = 0; c < col; c++) {
+ if(r === 0) data.data[0][c] = 'th' + c;
+ else data.data[r][c] = lorem(null , 25);
+ }
+}
+%>
+
+<%- include('../../template/ejs/table', {table: data}); %>
diff --git a/src/component/table/deprecated/style/_legacy.scss b/src/component/table/deprecated/style/_legacy.scss
new file mode 100644
index 000000000..012fd30eb
--- /dev/null
+++ b/src/component/table/deprecated/style/_legacy.scss
@@ -0,0 +1,92 @@
+////
+/// Table Legacy - deprecated
+/// @group table - deprecated
+////
+
+@use 'module/legacy';
+@use 'module/color';
+
+@include legacy.is(ie11) {
+ #{ns(table)} {
+ & > table {
+ background-image: none;
+
+ thead {
+ background-image: none;
+
+ tr {
+ &:first-child {
+ th {
+ @include color.border(contrast grey, (legacy: true, important: false, side: 'top'));
+ }
+ }
+
+ &:last-child {
+ th {
+ @include color.border(plain grey, (legacy: true, important: false, side: 'bottom'));
+ }
+ }
+ }
+ }
+
+ tbody {
+ tr {
+ background-image: none;
+
+ &:last-child {
+ th,
+ td {
+ @include color.border(contrast grey, (legacy: true, important: false, side: 'bottom'));
+ }
+ }
+ }
+ }
+
+ th,
+ td {
+ &:first-child {
+ @include color.border(contrast grey, (legacy: true, important: false, side: 'left'));
+ }
+
+ &:last-child {
+ @include color.border(contrast grey, (legacy: true, important: false, side: 'right'));
+ }
+ }
+ }
+
+ @include color.accentuate {
+ & > table {
+ background-image: none;
+
+ thead {
+ background-image: none;
+ }
+
+ tbody {
+ tr {
+ background-image: none;
+ }
+ }
+ }
+
+ {ns(table--bordered)} {
+ & > table {
+ tbody {
+ tr {
+ background-image: none;
+ }
+ }
+ }
+ }
+ }
+
+ &--bordered {
+ & > table {
+ th,
+ td {
+ @include color.border(contrast grey, (legacy: true, important: false, side: 'bottom'));
+ }
+ }
+ }
+ }
+}
diff --git a/src/component/table/deprecated/style/_module.scss b/src/component/table/deprecated/style/_module.scss
new file mode 100644
index 000000000..7507db2d4
--- /dev/null
+++ b/src/component/table/deprecated/style/_module.scss
@@ -0,0 +1,8 @@
+////
+/// Table Module - deprecated
+/// @group Table - deprecated
+////
+
+@import 'module/default';
+@import 'module/variants';
+@import 'module/shadow';
diff --git a/src/component/table/deprecated/style/_scheme.scss b/src/component/table/deprecated/style/_scheme.scss
new file mode 100644
index 000000000..36ef74bc3
--- /dev/null
+++ b/src/component/table/deprecated/style/_scheme.scss
@@ -0,0 +1,83 @@
+////
+/// Table Module - deprecated
+/// @group Table - deprecated
+////
+
+@use 'module/color';
+
+@mixin _deprecated-table-scheme($legacy: false) {
+ #{ns(table)} {
+ & > table {
+ @include color.background-image((border default grey) (border default grey) (border default grey) (border default grey), (legacy: $legacy));
+
+ caption {
+ @include color.text(title grey, (legacy: $legacy));
+ }
+
+ thead {
+ @include color.background-image(border plain grey, (legacy: $legacy));
+
+ @include color.background(alt grey, (legacy: $legacy));
+ @include color.text(title grey, (legacy: $legacy));
+ }
+
+ tbody {
+ @include color.background(default grey, (legacy: $legacy));
+
+ tr:nth-child(even) {
+ @include color.background(alt grey, (legacy: $legacy));
+ }
+ }
+ }
+
+ @include color.accentuate {
+ & > table {
+ @include color.background-image((border default accent) (border default accent) (border default accent) (border default accent), (legacy: $legacy));
+
+ thead {
+ @include color.background-image(border plain accent, (legacy: $legacy));
+ @include color.background(contrast accent, (legacy: $legacy));
+ }
+
+ tbody {
+ @include color.background(alt accent, (legacy: $legacy));
+
+ tr:nth-child(even) {
+ @include color.background(contrast accent, (legacy: $legacy));
+ }
+ }
+ }
+
+ /* Style bordered, ajoute des bordures entre chaque ligne */
+ {ns(table--bordered)} {
+ & > table {
+ tbody {
+ tr {
+ @include color.background-image(border default accent, (legacy: $legacy));
+
+ &:last-child {
+ background-image: none;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Style bordered, ajoute des bordures entre chaque ligne */
+ &--bordered {
+ & > table {
+ tbody {
+ tr {
+ @include color.background-image(border default grey, (legacy: $legacy));
+
+ /* Style bordered, enleve le style even/odd */
+ &:nth-child(even) {
+ @include color.transparent-background((legacy:$legacy, hover: true));
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/component/table/deprecated/style/module/_default.scss b/src/component/table/deprecated/style/module/_default.scss
new file mode 100644
index 000000000..e74720388
--- /dev/null
+++ b/src/component/table/deprecated/style/module/_default.scss
@@ -0,0 +1,62 @@
+////
+/// Table Module : default - deprecated
+/// @group Table - deprecated
+////
+
+#{ns(table)} {
+ @include before('', block) {
+ @include size(100%, 0);
+ }
+
+ &:not(:has(#{ns(table__container)})) {
+ @include padding-top(var(--table-offset));
+ }
+
+ &:not(#{ns(table--no-scroll)}) {
+ & > table {
+ width: 100%;
+ }
+ }
+
+ & > table {
+ @include size(100%);
+ display: block;
+ overflow: auto;
+ border-spacing: 0;
+
+ @include padding(1px);
+ @include margin(-1px);
+
+ background-size: 100% 1px, 1px 100%, 1px 100%, 100% 1px;
+ background-repeat: no-repeat, no-repeat, no-repeat;
+ background-position: 0 100%, 0 0, 100% 0, 100% 0;
+
+ td,
+ th {
+ text-align: left;
+ vertical-align: middle;
+ display: table-cell;
+ border: 0;
+ @include padding(3v);
+ @include padding(4v, md);
+ @include text-style(sm);
+ }
+
+ th {
+ font-weight: font-weight(bold);
+ }
+
+ thead {
+ background-size: 100% 1px;
+ background-position: bottom;
+ background-repeat: no-repeat;
+
+ td,
+ th {
+ font-weight: font-weight(bold);
+ @include padding-bottom(3.5v); // 0.5v for the box shadow
+ @include padding-bottom(4.5v, md);
+ }
+ }
+ }
+}
diff --git a/src/component/table/deprecated/style/module/_shadow.scss b/src/component/table/deprecated/style/module/_shadow.scss
new file mode 100644
index 000000000..843dbcbb8
--- /dev/null
+++ b/src/component/table/deprecated/style/module/_shadow.scss
@@ -0,0 +1,89 @@
+////
+/// Table module : Shadow - deprecated
+/// @group Table - deprecated
+////
+
+@use 'module/selector';
+
+/*
+* Ombres ajoutées en Js si le contenu est plus grand que le conteneur
+*/
+#{ns(table)} {
+ & > table {
+ {ns(table__shadow)} {
+ @include before('', block) {
+ @include absolute(var(--table-offset), 0, 0, 0);
+ @include z-index(over);
+ @include _table-scroll-shadow(false, false);
+ opacity: 0.32;
+ pointer-events: none;
+ transition: box-shadow 0.3s;
+ }
+
+ /**
+ * Modifier ombre à gauche
+ **/
+ &--left {
+ @include before {
+ @include _table-scroll-shadow(true, false); // @TODO: à implementer dans la mixin shadow
+ }
+
+ /**
+ * Modifier combinaison ombre à gauche et ombre à droite
+ **/
+ {ns(table__shadow--right)} {
+ @include before('', block) {
+ @include _table-scroll-shadow(true, true); // @TODO: à implementer dans la mixin shadow
+ }
+ }
+ }
+
+ /**
+ * Modifier ombre à droite
+ **/
+ &--right {
+ @include before {
+ @include _table-scroll-shadow(false, true);// @TODO: à implementer dans la mixin shadow
+ }
+ }
+ }
+ }
+}
+
+/*
+* Positionnement ombres sur le tableau sans caption
+*/
+#{ns(table--no-caption)} {
+ & > table {
+ {ns(table__shadow)} {
+ @include before('', block) {
+ @include absolute(0, 0, 0, 0);
+ }
+ }
+ }
+}
+
+/*
+* Positionnement ombres sur le tableau avec caption en bas
+*/
+#{ns(table--caption-bottom)} {
+ & > table {
+ {ns(table__shadow)} {
+ @include before('', block) {
+ @include absolute(0, 0, 0, 0);
+ }
+ }
+ }
+}
+
+@include selector.theme(dark) {
+ #{ns(table)} {
+ & > table {
+ {ns(table__shadow)} {
+ @include before {
+ opacity: 1;
+ }
+ }
+ }
+ }
+}
diff --git a/src/component/table/deprecated/style/module/_variants.scss b/src/component/table/deprecated/style/module/_variants.scss
new file mode 100644
index 000000000..d59ee96a0
--- /dev/null
+++ b/src/component/table/deprecated/style/module/_variants.scss
@@ -0,0 +1,90 @@
+////
+/// Table module : Variants - deprecated
+/// @group Table - deprecated
+////
+
+#{ns(table)} {
+ /*
+ * Cache la caption
+ */
+ &--no-caption {
+ @include padding-top(0);
+
+ & > table {
+ caption {
+ @include sr-only();
+ @include height(0);
+ }
+ }
+ }
+
+ /*
+ * Fixe le caption en bas du tableau
+ */
+ &--caption-bottom {
+ @include padding-top(0);
+ @include margin-bottom(0);
+ @include margin-top(4v);
+
+ & > table {
+ @include margin-bottom(calc(var(--table-offset) + 11v));
+
+ caption {
+ @include margin-top(4v);
+ @include height(min-content);
+ caption-side: bottom;
+ }
+ }
+
+ {ns-attr(js-table, true)} {
+ & > table {
+ caption {
+ @include absolute(100%, 0, 0, 0);
+ @include margin-top(4v);
+ }
+ }
+ }
+ }
+
+ /*
+ * pas de scroll ni de shadow
+ */
+ &--no-scroll {
+ @include min-width(auto);
+
+ & > table {
+ overflow-x: hidden;
+
+ caption {
+ @include max-width(calc(100vw - 8v)); // eol in mobile even if table overflow
+ }
+ }
+ }
+
+ /*
+ * Fixe la taille des colonnes du tableau
+ */
+ &--layout-fixed {
+ & > table {
+ display: table;
+ table-layout: fixed;
+ }
+ }
+
+ /* Style bordered, ajoute des bordures entre chaque ligne */
+ &--bordered {
+ & > table {
+ tbody {
+ tr {
+ background-size: 100% 1px;
+ background-position: bottom;
+ background-repeat: no-repeat;
+
+ &:last-child {
+ background-image: none;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/component/table/deprecated/template/ejs/table.ejs b/src/component/table/deprecated/template/ejs/table.ejs
new file mode 100644
index 000000000..a10fb853d
--- /dev/null
+++ b/src/component/table/deprecated/template/ejs/table.ejs
@@ -0,0 +1,72 @@
+<%#
+# paramètres Table
+
+* table.data (array(array), required): tableau de données à deux dimension [row][col] (la première ligne (row = 0) est reservée aux
)
+
+* table.caption (string, required) : titre du tableau
+
+* table.noScroll (boolean, optional) : {default: false} désactive le scroll
+
+* table.noCaption (boolean, optional) : {default: false} cache le texte de la caption
+
+* table.captionBottom (boolean, optional) : {default: false} positionne la caption en bas
+
+* table.bordered (boolean, optional) : {default: false} si true, ajoute des séparateurs entre chaque ligne et enleve l'effet even/odd
+
+* table.layout (string, optional) : si non undefined, fix la taille des colonnes à 100%/col
+
+* table.col (integer, optional) : nombre de colones à afficher (si différent du nombre de colones de data)
+
+* table.row (integer, optional) : nombre de lignes à afficher (si différent du nombre de lignes de data)
+
+* table.accent (string, optional): couleur d'accenturation du composant
+
+* table.id (string) : id de l'élément
+
+* table.classes (array, optional): classes supplémentaires du composant
+
+* table.attributes (array, optional): attributs supplémentaires du composant
+%>
+<% eval(include('../../../../../core/index.ejs')); %>
+
+<%
+let table = locals.table || {};
+let classes = table.classes || [];
+const attributes = table.attributes || {};
+attributes.id = table.id || uniqueId('table');
+const data = table.data || [[]];
+
+classes.push(prefix + '-table');
+if (table.accent !== undefined) classes.push(prefix + '-table--' + table.accent);
+if (table.bordered) classes.push(prefix + '-table--bordered');
+if (table.noScroll) classes.push(prefix + '-table--no-scroll');
+if (table.noCaption) classes.push(prefix + '-table--no-caption');
+if (table.captionBottom) classes.push(prefix + '-table--caption-bottom');
+if (table.layout) classes.push(prefix + '-table--layout-fixed');
+if (!table.row) table.row = data.length;
+if (!table.col) table.col = data[0].length;
+%>
+
+ <%- includeAttrs(attributes) %>>
+
+ <% if (table.caption !== undefined) { %>
+ <%= table.caption %>
+ <% } %>
+
+
+ <% for (let col = 0; col < table.col; col++) { %>
+ <%- table.data[0][col] %> |
+ <% } %>
+
+
+
+ <% for (let row = 1; row < table.row; row++) { %>
+
+ <% for (let col = 0; col < table.col; col++) { %>
+ <%- table.data[row][col] %> |
+ <% } %>
+
+ <% } %>
+
+
+
diff --git a/src/component/table/example/data/data-complex-tbody.json.ejs b/src/component/table/example/data/data-complex-tbody.json.ejs
new file mode 100644
index 000000000..f6bc28d97
--- /dev/null
+++ b/src/component/table/example/data/data-complex-tbody.json.ejs
@@ -0,0 +1,135 @@
+<%
+const getPictogram = (pictogram) => include('../../../../core/template/ejs/artwork/pictogram.ejs', {pictogram});
+
+const tbody = [
+ [
+ {
+ attributes: {
+ headers: 'complex-row-0 complex-thead-0-col-1 complex-thead-1-col-1'
+ },
+ content: 'Français'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-0 complex-thead-0-col-2 complex-thead-1-col-2'
+ },
+ content: 'Mathématiques'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-0 complex-thead-0-col-3 complex-thead-1-col-3'
+ },
+ content: 'LV1'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-0 complex-thead-0-col-3 complex-thead-1-col-4'
+ },
+ content: 'Histoire - Géographie'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-0 complex-thead-0-col-4 complex-thead-1-col-5'
+ },
+ content: 'EPS'
+ }
+ ],
+ [
+ {
+ attributes: {
+ colspan: 5,
+ headers: 'complex-row-1 complex-thead-0-col-1 complex-thead-0-col-2 complex-thead-0-col-3 complex-thead-0-col-4 complex-thead-1-col-1 complex-thead-1-col-2 complex-thead-1-col-3 complex-thead-1-col-4 complex-thead-1-col-5'
+ },
+ content: 'Etude dirigée Exemple de colspan sur toute la ligne'
+ }
+ ],
+ [
+ {
+ attributes: {
+ headers: 'complex-row-2 complex-thead-0-col-1 complex-thead-1-col-1'
+ },
+ content: 'Mathématiques'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-2 complex-thead-0-col-2 complex-thead-1-col-2'
+ },
+ content: 'Histoire - Géographie'
+ },
+ {
+ attributes: {
+ rowspan: 2,
+ headers: 'complex-row-2 complex-row-3 complex-thead-0-col-3 complex-thead-1-col-3'
+ },
+ content: `Arts appliqués ${getPictogram({name: 'environment'})}`
+ },
+ {
+ attributes: {
+ headers: 'complex-row-2 complex-thead-0-col-3 complex-thead-1-col-4'
+ },
+ content: 'LV2'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-2 complex-thead-0-col-4 complex-thead-1-col-5'
+ },
+ content: 'Sciences'
+ }
+ ],
+ [
+ {
+ attributes: {
+ headers: 'complex-row-3 complex-thead-0-col-1 complex-thead-1-col-1'
+ },
+ content: 'Français'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-3 complex-thead-0-col-2 complex-thead-1-col-2'
+ },
+ content: 'EPS'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-3 complex-thead-0-col-3 complex-thead-1-col-4'
+ },
+ content: 'Histoire - Géographie'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-3 complex-thead-0-col-4 complex-thead-1-col-5'
+ },
+ content: 'Physique - Chimie'
+ }
+ ],
+ [
+ {
+ attributes: {
+ headers: 'complex-row-4 complex-thead-0-col-1 complex-thead-1-col-1'
+ },
+ content: 'Sciences'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-4 complex-thead-0-col-2 complex-thead-1-col-2'
+ },
+ content: 'LV1'
+ },
+ {
+ attributes: {
+ colspan: 2,
+ headers: 'complex-row-4 complex-thead-0-col-3 complex-thead-1-col-3 complex-thead-1-col-4'
+ },
+ content: 'EPS Exemple de colspan sur 2 cellules'
+ },
+ {
+ attributes: {
+ headers: 'complex-row-4 complex-thead-0-col-4 complex-thead-1-col-5'
+ },
+ content: 'LV2'
+ }
+ ]
+];
+%>
+
+<%- JSON.stringify(tbody); %>
diff --git a/src/component/table/example/data/data-complex-thead.json.ejs b/src/component/table/example/data/data-complex-thead.json.ejs
new file mode 100644
index 000000000..bad5dabd3
--- /dev/null
+++ b/src/component/table/example/data/data-complex-thead.json.ejs
@@ -0,0 +1,62 @@
+<%
+const thead = [
+ [
+ {
+ attributes: {
+ rowspan: 2
+ },
+ classes: [`${prefix}-cell--fixed`],
+ content: `Horaires`
+ },
+ {
+ content: 'Lundi'
+ },
+ {
+ content: 'Mardi'
+ },
+ {
+ attributes: {
+ colspan: 2
+ },
+ content: 'Mercredi & Jeudi Exemple de 2 cellules fusionnées dans le Header'
+ },
+ {
+ content: 'Vendredi'
+ }
+ ],
+ [
+ {
+ attributes: {
+ headers: 'complex-thead-0-col-1'
+ },
+ content: 'Groupes 1 & 2'
+ },
+ {
+ attributes: {
+ headers: 'complex-thead-0-col-2'
+ },
+ content: 'Groupes 1 & 2'
+ },
+ {
+ attributes: {
+ headers: 'complex-thead-0-col-3'
+ },
+ content: 'Groupe 1'
+ },
+ {
+ attributes: {
+ headers: 'complex-thead-0-col-3'
+ },
+ content: 'Groupe 2'
+ },
+ {
+ attributes: {
+ headers: 'complex-thead-0-col-4'
+ },
+ content: 'Groupes 1 & 2'
+ }
+ ]
+];
+%>
+
+<%- JSON.stringify(thead); %>
diff --git a/src/component/table/example/data/data-complex.json.ejs b/src/component/table/example/data/data-complex.json.ejs
new file mode 100644
index 000000000..259ff7227
--- /dev/null
+++ b/src/component/table/example/data/data-complex.json.ejs
@@ -0,0 +1,35 @@
+<%
+const thead = JSON.parse(include('./data-complex-thead.json.ejs'));
+const tbodies = [JSON.parse(include('./data-complex-tbody.json.ejs'))];
+
+thead.forEach((row, rowIndex) => {
+ row.forEach((th, thIndex) => {
+ th.attributes = {
+ ...th.attributes,
+ id: `complex-thead-${rowIndex}-col-${thIndex}`
+ };
+ });
+});
+
+tbodies.forEach((tbody) => {
+ tbody.forEach((row, index) => {
+ row.unshift({
+ markup: 'th',
+ attributes: {
+ id: `complex-row-${index}`,
+ headers: 'complex-thead-0-col-0'
+ },
+ classes: [`${prefix}-cell--fixed`],
+ content: `${8 + index}h`
+ });
+ });
+});
+
+const table = {
+ thead,
+ tbodies
+};
+
+%>
+
+<%- JSON.stringify(table); %>
diff --git a/src/component/table/example/data/data-lorem.json.ejs b/src/component/table/example/data/data-lorem.json.ejs
new file mode 100644
index 000000000..faa3cb2cb
--- /dev/null
+++ b/src/component/table/example/data/data-lorem.json.ejs
@@ -0,0 +1,24 @@
+<%
+const table = locals.table || {};
+const col = table.col || 4;
+const row = table.row || 3;
+const loremLength = table.loremLength || 25;
+
+let thead = Array.from(Array(1), () => []);
+let tbody = Array.from(Array(row), () => []);
+
+for(let r = 0; r < row; r++) {
+ for(let c = 0; c < col; c++) {
+ if(r === 0) thead[0].push({content: 'th' + c});
+ tbody[r].push({content: lorem(null , loremLength)});
+ }
+}
+
+const data = {
+ ...table,
+ thead,
+ tbodies: [tbody]
+};
+%>
+
+<%- JSON.stringify(data); %>
diff --git a/src/component/table/example/data/data-miscellaneous.json.ejs b/src/component/table/example/data/data-miscellaneous.json.ejs
new file mode 100644
index 000000000..2a5a8b351
--- /dev/null
+++ b/src/component/table/example/data/data-miscellaneous.json.ejs
@@ -0,0 +1,118 @@
+<%
+const table = locals.table || {};
+
+const getSrOnlyCell = (content) => `${content}`;
+const getTextCell = (markup = 'span', classes = `${prefix}-cell__desc`) => `<${markup} class="${classes}">${getText('data.cell.text', 'table')}${markup}>`;
+const getTitleCell = (markup = 'span', classes = `${prefix}-cell__title`) => `<${markup} class="${classes}">${getText('data.cell.title', 'table')}${markup}>`;
+
+const getBadge = (badge) => include('../../../badge/template/ejs/badge.ejs', {badge});
+const getButton = (button) => include('../partials/getButton.ejs', {button});
+const getCheckbox = (checkbox) => include('../partials/getCheckbox.ejs', {checkbox});
+const getIcon = (icon) => include('../../../../core/template/ejs/icon/icon.ejs', {icon});
+const getInput = (input) => include('../../../input/input-base/template/ejs/input-group.ejs', {input});
+const getLink = (link) => include('../../../link/template/ejs/link.ejs', {link});
+const getPictogram = (pictogram) => include('../../../../core/template/ejs/artwork/pictogram.ejs', {pictogram});
+const getTag = (tag) => include('../../../tag/template/ejs/tag.ejs', {tag});
+
+const thead = [
+ [
+ {
+ attributes: {
+ role: 'columnheader'
+ },
+ classes: [`${prefix}-cell--fixed`],
+ content: getSrOnlyCell(getText('data.cell.action.select', 'table'))
+ },
+ {
+ content: getTitleCell()
+ },
+ {
+ content: getTitleCell('div') + getTextCell('div')
+ },
+ {
+ content: `${getTitleCell()} ${getButton({id: `${table.id}-thead-sort-asc-desc`, label: `${getText('data.cell.action.sort', 'table')}`, classes: [`${prefix}-btn--sort`]})} `
+ },
+ {
+ content: getButton({id: `${table.id}-thead-sort-desc`, label: `${getText('data.cell.action.sort', 'table')}`, classes: [`${prefix}-btn--sort`], attributes: {'aria-sorting': 'desc'}})
+ },
+ {
+ content: `${getTextCell()} ${getButton({label: getText('data.cell.text', 'table'), classes: [`${prefix}-ml-2v`, `${prefix}-btn--tooltip`], tooltip: {id: `${table.id}-thead-tooltip`, content: lorem()}})}`
+ },
+ {
+ content: getButton({id: `${table.id}-thead-sort-asc`, label: `${getText('data.cell.action.sort', 'table')}`, classes: [`${prefix}-btn--sort`], attributes: {'aria-sorting': 'asc'}})
+ },
+ {
+ content: getBadge({id: `${table.id}-thead-badge`, type: 'info', label: getText('data.cell.label', 'table')})
+ },
+ {
+ content: getTextCell()
+ },
+ {
+ content: getTitleCell()
+ },
+ {
+ content: getTitleCell() + getTextCell('span', `${prefix}-cell__desc ${prefix}-ml-2v`)
+ },
+ {
+ content: getIcon({name: 'arrow-right-s-line', size: 'sm', classes: [`${prefix}-mr-2v`]}) + getText('data.cell.text', 'table')
+ }
+ ]
+];
+
+const rowsLength = 6;
+const selectedLines = [0, 3, 4];
+const tbody = new Array(rowsLength);
+for(let r = 0; r < rowsLength; r++) {
+ tbody[r] = [
+ {
+ markup: 'th',
+ attributes: {scope: 'row'},
+ classes: [`${prefix}-cell--fixed`],
+ content: getCheckbox({id: `${table.id}-select-row-checkbox-${r + 1}`, label: `${getText('data.cell.action.selectRow', 'table')} ${r + 1} : ${getText('data.cell.title', 'table')}`, attributes: {name: 'row-select', checked: selectedLines.includes(r) || undefined}}) + getTitleCell('span')
+ },
+ {
+ content: getText('data.cell.text', 'table')
+ },
+ {
+ content: getBadge({type: 'info', label: getText('data.cell.label', 'table'), classes: [`${prefix}-mb-2v`], size: 'sm'}) + getTitleCell('div', `${prefix}-cell__title ${prefix}-mb-2v`) + getTextCell('div')
+ },
+ {
+ content: getBadge({type: 'success', label: getText('data.cell.label', 'table'), classes: [`${prefix}-mr-2v`], size: 'sm'}) + getText('data.cell.text', 'table')
+ },
+ {
+ content: getText('data.cell.text', 'table')
+ },
+ {
+ content: getInput({id: `text-input-text-${r + 1}`})
+ },
+ {
+ content: getText('data.cell.number', 'table')
+ },
+ {
+ content: getBadge({type: 'info', label: getText('data.cell.label', 'table')})
+ },
+ {
+ content: getButton({kind: 2})
+ },
+ {
+ content: getTag({type: 'clickable', href: '#', label: getText('data.cell.label', 'table')})
+ },
+ {
+ content: getLink({download: true, href: '/example/img/image.jpg', label: getText('data.cell.download.label', 'table'), detail: getText('data.cell.download.detail', 'table')})
+ },
+ {
+ content: getPictogram({name: 'leaf'})
+ }
+ ];
+}
+
+const data = {
+ table: {
+ ...table,
+ thead,
+ tbodies: [tbody]
+ }
+};
+%>
+
+<%- JSON.stringify(data); %>
diff --git a/src/component/table/example/data/data-simple.json.ejs b/src/component/table/example/data/data-simple.json.ejs
new file mode 100644
index 000000000..5361de5aa
--- /dev/null
+++ b/src/component/table/example/data/data-simple.json.ejs
@@ -0,0 +1,72 @@
+<%
+const table = locals.table || {};
+const dataLorem = JSON.parse(include('../data/data-lorem.json.ejs', { table: table.table }));
+
+const thead = dataLorem.thead;
+const tbodies = dataLorem.tbodies;
+
+const getCheckbox = (checkbox) => include('../partials/getCheckbox.ejs', {checkbox});
+const getSrOnlyCell = (content) => `${content}`;
+
+thead.forEach((row, rowIndex) => {
+ row.forEach((th, thIndex) => {
+ th.attributes = {
+ ...th.attributes,
+ scope: 'col'
+ };
+ });
+});
+
+if (table.selectable) {
+ thead.forEach((row) => {
+ row.unshift({
+ attributes: {
+ role: 'columnheader'
+ },
+ classes: [`${prefix}-cell--fixed`],
+ content: getSrOnlyCell(getText('data.cell.action.select', 'table'))
+ });
+ });
+ tbodies.forEach((tbody) => {
+ tbody.forEach((row, index) => {
+ row.unshift({
+ classes: [`${prefix}-cell--fixed`],
+ content: getCheckbox({id: `${uniqueId('table-select-checkbox')}--${index}`, label: `${getText('data.cell.action.selectRow', 'table')} ${index + 1}`, attributes: {name: 'row-select'}})
+ });
+ });
+ });
+}
+
+if (table.doubleEntry) {
+ thead.forEach((row) => {
+ row.unshift({
+ attributes: {
+ scope: 'row'
+ },
+ classes: [`${prefix}-cell--fixed`],
+ content: getSrOnlyCell(getText('data.cell.doubleEntry', 'table'))
+ });
+ });
+
+ tbodies.forEach((tbody) => {
+ tbody.forEach((row, index) => {
+ row.unshift({
+ markup: 'th',
+ attributes: {scope: 'row'},
+ classes: [`${prefix}-cell--fixed`],
+ content: `th${index}`
+ });
+ });
+ });
+}
+
+const data = {
+ table: {
+ ...table,
+ thead,
+ tbodies
+ }
+};
+%>
+
+<%- JSON.stringify(data); %>
diff --git a/src/component/table/example/deprecated/index.ejs b/src/component/table/example/deprecated/index.ejs
new file mode 100644
index 000000000..bcf14b10c
--- /dev/null
+++ b/src/component/table/example/deprecated/index.ejs
@@ -0,0 +1,3 @@
+<%- deprecation(); %>
+
+<%- include('../../deprecated/example/index') %>
diff --git a/src/component/table/example/footer/footer.ejs b/src/component/table/example/footer/footer.ejs
new file mode 100644
index 000000000..8f9212ca9
--- /dev/null
+++ b/src/component/table/example/footer/footer.ejs
@@ -0,0 +1,79 @@
+<% eval(include('../../../../core/index.ejs'));
+
+const selectData = {
+ id: uniqueId('table-footer-select'),
+ label: getText('data.navigation.select.label', 'table'),
+ labeClasses: [`${prefix}-sr-only`],
+ placeholder: getText('data.navigation.select.placeholder', 'table'),
+ options :[
+ {value:'4', label:getText('data.navigation.select.options.option1', 'table')},
+ {value:'10', label:getText('data.navigation.select.options.option2', 'table')},
+ {value:'20', label:getText('data.navigation.select.options.option3', 'table')},
+ ]
+}
+const select = include('../../../select/template/ejs/select-group.ejs', {select: selectData});
+
+
+const getData = (label, displayedLg = false, hasLgLabel = false, title = null) => {
+ return {
+ id: uniqueId('table-footer-pagination'),
+ label: label,
+ title: title,
+ href: '#',
+ displayedLg: displayedLg === true,
+ hasLgLabel: hasLgLabel === true
+ };
+};
+
+const getPageData = (pageNumber, displayedLg) => {
+ return getData(pageNumber, displayedLg === true, false, `Page ${pageNumber}`);
+}
+
+const getNavData = (label, hasLgLabel) => {
+ return getData(label, false, hasLgLabel === true, label);
+}
+
+const pages = [];
+let i;
+for (i = 1; i < 4; i++) pages.push(getPageData(i, i > 2));
+pages[0].active = true;
+
+const paginationData = {
+ first: getNavData('Première page'),
+ prev: getNavData('Précédente', true),
+ next: getNavData('Suivante', true),
+ last: getNavData('Dernière page'),
+ pages : pages
+}
+const pagination = include('../../../pagination/template/ejs/pagination', {pagination: paginationData});
+
+const buttonsGroupData = {
+ inline: 'md',
+ align:'right',
+ reverse: true,
+ buttons: [
+ {
+ label: getText('data.navigation.buttons.label', 'table'),
+ id: uniqueId('table-footer-button-primary'),
+ kind: 1
+ },
+ {
+ label: getText('data.navigation.buttons.label', 'table'),
+ id: uniqueId('table-footer-button-secondary'),
+ kind: 2
+ }
+ ]
+};
+const buttons = include('../../../button/template/ejs/buttons-group', {buttonsGroup: buttonsGroupData});
+%>
+
+
+
+
diff --git a/src/component/table/example/header/header.ejs b/src/component/table/example/header/header.ejs
new file mode 100644
index 000000000..f82602e0b
--- /dev/null
+++ b/src/component/table/example/header/header.ejs
@@ -0,0 +1,54 @@
+<% eval(include('../../../../core/index.ejs'));
+
+const buttonsGroupData = {
+ inline: 'md',
+ align:'right',
+ iconPlace:'left',
+ buttons: [
+ {
+ label: getText('data.actionBar.buttons.label', 'table'),
+ id: uniqueId('table-header-button-primary'),
+ icon: 'settings-5-line',
+ iconPlace:'left',
+ kind: 2
+ },
+ {
+ label: getText('data.actionBar.buttons.label', 'table'),
+ id: uniqueId('table-header-button-secondary'),
+ icon: 'settings-5-line',
+ iconPlace:'left',
+ kind: 2
+ }
+ ]
+};
+const buttons = include('../../../button/template/ejs/buttons-group', {buttonsGroup: buttonsGroupData});
+
+const segmentedData = {
+ id: uniqueId('table-header-segmented'),
+ noLegend: true,
+ legend: getText('data.actionBar.segmented.legend', 'table'),
+ elements: [
+ {
+ id: uniqueId('table-header-segmented-table'),
+ label: getText('data.actionBar.segmented.table', 'table'),
+ name: 'table-header-segmented-table',
+ value: 1,
+ icon: 'table-line',
+ checked: true
+ },
+ {
+ id: uniqueId('table-header-segmented-list'),
+ label: getText('data.actionBar.segmented.list', 'table'),
+ name: 'table-header-segmented-table',
+ value: 2,
+ icon: 'list-unordered',
+ checked: false
+ }
+ ]
+};
+const segmented = include('../../../segmented/template/ejs/segmented', {segmented: segmentedData});
+%>
+
+<%- segmented %>
+<%- getText('data.actionBar.detail', 'table') %>
+<%- buttons %>
diff --git a/src/component/table/example/index.ejs b/src/component/table/example/index.ejs
index 006b5cd68..530998151 100755
--- a/src/component/table/example/index.ejs
+++ b/src/component/table/example/index.ejs
@@ -1,27 +1,58 @@
+
<%
const sample = getSample(include);
-let data = { caption: 'Caption du tableau (accessibilité)', col: 6, row: 3};
-let dataBordered = { caption: 'Caption du tableau (accessibilité)', bordered: true, col: 6, row: 3};
-let dataNoScroll = { caption: 'Caption tableau non scrollable', noScroll: true, col: 6};
-let dataLayoutFixed = { caption: 'Caption tableau fixé', layout: "fixed", col: 3};
-let dataNoCaption = { caption: 'Caption cachée', noCaption: true, col: 6};
-let dataCaptionBottom = { caption: 'Caption en bas', captionBottom: true, col: 6};
+let loremData = JSON.parse(include('./data/data-lorem.json.ejs'));
+const complexData = JSON.parse(include('./data/data-complex.json.ejs'));
+%>
+
+<%-
+ sample(getText('sample.default', 'table'), './sample/table-simple', {table: {table: {id: 'table-md', caption: getText('data.caption.default', 'table')}}}, true);
+%>
+
+<%-
+ sample(getText('sample.sm', 'table'), './sample/table-simple', {table: {classes: [`${prefix}-table--sm`], table: {id: 'table-sm', caption: getText('data.caption.default', 'table')}}}, true);
+%>
+
+<%-
+ sample(getText('sample.lg', 'table'), './sample/table-simple', {table: {classes: [`${prefix}-table--lg`], table: {id: 'table-lg', caption: getText('data.caption.default', 'table')}}}, true);
+%>
+
+<%-
+ sample(getText('sample.bordered', 'table'), './sample/table-simple', {table: {bordered: true, table: {id: 'table-bordered', caption: getText('data.caption.default', 'table')}}}, true);
+%>
-// @TODO: revoir les grilles avec layout
+<%-
+ sample({title: getText('sample.columnSizes.title', 'table'), subtitle: getText('sample.columnSizes.subtitle', 'table')}, './sample/table-column-sizes', {table: {table: {id: 'table-column-sizes', caption: getText('data.caption.default', 'table')}}}, true);
%>
-<%- sample('Tableau par défaut', './sample/table-default', {table:data}, true) %>
+<%-
+ sample({title: getText('sample.utilityClasses.title', 'table'), subtitle: getText('sample.utilityClasses.subtitle', 'table')}, './sample/table-utility-classes', {table: {bordered: true, table: {id: 'table-default', caption: getText('data.caption.default', 'table')}}}, true);
+%>
-<%- sample('Tableau avec bordure', './sample/table-default', {table:dataBordered}, true) %>
+<%-
+ sample({title: getText('sample.multiline.title', 'table'), subtitle: getText('sample.multiline.subtitle', 'table')}, './sample/table-simple', {table: {table: {id: 'table-multiline', classes: [`${prefix}-cell--multiline`], caption: getText('data.caption.default', 'table')}}}, true);
+%>
-<%- sample('Tableau non scrollable', './sample/table-default', {table:dataNoScroll}, true) %>
+<%-
+ sample(getText('sample.noCaption', 'table'), './sample/table-simple', {table: {noCaption: true, table: {id: 'table-noCaption', caption: getText('data.caption.default', 'table')}}}, true);
+%>
-<%- sample('Tableau colonnes fixées (layout-fixed)', './sample/table-default', {table:dataLayoutFixed}, true) %>
+<%-
+ sample(getText('sample.captionBottom', 'table'), './sample/table-simple', {table: {captionBottom: true, table: {id: 'table-captionBottom', caption: getText('data.caption.default', 'table')}}}, true);
+%>
-<%- sample('Tableau caption invisible', './sample/table-default', {table:dataNoCaption}, true) %>
+<%-
+ sample(getText('sample.selectable', 'table'), './sample/table-simple', {table: {table: {id: 'table-selectable', caption: getText('data.caption.default', 'table'), selectable: true}}}, true);
+%>
-<%- sample('Tableau caption Bottom', './sample/table-default', {table:dataCaptionBottom}, true) %>
+<%-
+ sample(getText('sample.doubleEntry', 'table'), './sample/table-simple', {table: {table: {id: 'table-double-entry', caption: getText('data.caption.default', 'table'), doubleEntry: true}}}, true);
+%>
-<%- sample('Tableau accentué', './sample/table-default', {table: {...data, accent:'green-emeraude'}}, true) %>
+<%-
+ sample({title: getText('sample.complex.title', 'table'), subtitle: getText('sample.complex.subtitle', 'table')}, './sample/table-default', {table: {bordered: true, table: {id: 'table-complex', caption: getText('data.caption.default', 'table'), captionDetail: getText('data.caption.detail', 'table'), ...complexData}}}, true);
+%>
-<%- sample('Tableau accentué avec bordure', './sample/table-default', {table:{...dataBordered, accent:'green-emeraude'}}, true) %>
+<%-
+ sample(getText('sample.miscellaneous', 'table'), './sample/table-miscellaneous', {table: {header: true, footer: true, table: {id: 'table-miscellaneous', caption: getText('data.caption.large', 'table')}}}, true);
+%>
diff --git a/src/component/table/example/partials/getButton.ejs b/src/component/table/example/partials/getButton.ejs
new file mode 100644
index 000000000..4415e57f0
--- /dev/null
+++ b/src/component/table/example/partials/getButton.ejs
@@ -0,0 +1,17 @@
+<%
+const button = locals.button || {};
+const defaultButton = {
+ id: uniqueId('table-default-button'),
+ label: getText('data.cell.action.button', 'table'),
+ size: 'sm'
+};
+
+const option = {
+ ...defaultButton,
+ ...button
+};
+
+const getButton = include('../../../button/template/ejs/button.ejs', {button: option});
+%>
+
+<%- getButton %>
diff --git a/src/component/table/example/partials/getCheckbox.ejs b/src/component/table/example/partials/getCheckbox.ejs
new file mode 100644
index 000000000..ca8bf3eb0
--- /dev/null
+++ b/src/component/table/example/partials/getCheckbox.ejs
@@ -0,0 +1,20 @@
+<%
+const checkbox = locals.checkbox || {}
+const defaultCheckbox = {
+ id: uniqueId('table-default-checkbox'),
+ attributes: {
+ 'aria-label': getText('data.cell.action.select', 'table'),
+ },
+ size: 'sm',
+ includeEmptyMessagesGroup: false,
+};
+
+const option = {
+ ...defaultCheckbox,
+ ...checkbox
+};
+
+const getCheckbox = include('../../../checkbox/template/ejs/checkbox-group.ejs', {checkbox: option});
+%>
+
+<%- getCheckbox %>
diff --git a/src/component/table/example/sample/table-column-sizes.ejs b/src/component/table/example/sample/table-column-sizes.ejs
new file mode 100644
index 000000000..78c138096
--- /dev/null
+++ b/src/component/table/example/sample/table-column-sizes.ejs
@@ -0,0 +1,29 @@
+<%
+const table = locals.table || {};
+const dataLorem = JSON.parse(include('../data/data-lorem.json.ejs', { table: table.table }));
+
+const loremThead = dataLorem.thead;
+loremThead.forEach(thead => {
+ thead.forEach((row, index) => {
+ const sizes = ['xs','sm', 'md', 'lg'];
+ row.content = sizes[index] || 'default';
+ row.classes = sizes[index] ? [`${prefix}-col--${sizes[index]}`] : [];
+ });
+});
+
+const loremBodies = dataLorem.tbodies;
+loremBodies.forEach(tbodies => {
+ tbodies.forEach(tbody => {
+ tbody.forEach((row, index) => {
+ row.content = lorem(null , 5 * (index + 1));
+ });
+ });
+});
+
+const data = {
+ ...table,
+ table: dataLorem
+}
+%>
+
+<%- include('./table-default', {table: data}); %>
diff --git a/src/component/table/example/sample/table-default.ejs b/src/component/table/example/sample/table-default.ejs
old mode 100755
new mode 100644
index 5f220727d..ffb11523c
--- a/src/component/table/example/sample/table-default.ejs
+++ b/src/component/table/example/sample/table-default.ejs
@@ -1,25 +1,13 @@
<%
const table = locals.table || {};
-const col = table.col || 2;
-const row = table.row || 2;
-let data = {
- caption: table.caption || undefined,
- bordered: table.bordered || false,
- noScroll: table.noScroll || false,
- noCaption: table.noCaption || false,
- captionBottom: table.captionBottom || false,
- accent: table.accent || undefined,
- layout: table.layout
+
+if (table.header) {
+ table.header = include('../header/header.ejs');
}
-data.data = new Array(row);
-for(let r = 0; r < row; r++) {
- data.data[r] = new Array(col);
- for(let c = 0; c < col; c++) {
- if(r === 0) data.data[0][c] = 'th' + c;
- else data.data[r][c] = lorem(null , 25);
- }
+if (table.footer) {
+ table.footer = include('../footer/footer.ejs');
}
%>
-<%- include('../../template/ejs/table', {table: data}); %>
+<%- include('../../template/ejs/wrapper', {table}); %>
diff --git a/src/component/table/example/sample/table-miscellaneous.ejs b/src/component/table/example/sample/table-miscellaneous.ejs
new file mode 100644
index 000000000..00a056e06
--- /dev/null
+++ b/src/component/table/example/sample/table-miscellaneous.ejs
@@ -0,0 +1,11 @@
+<%
+const table = locals.table || {};
+const dataMiscellaneous = JSON.parse(include('../data/data-miscellaneous.json.ejs', { table: table.table }));
+
+const data = {
+ ...table,
+ ...dataMiscellaneous
+}
+%>
+
+<%- include('./table-default', {table: data}); %>
diff --git a/src/component/table/example/sample/table-simple.ejs b/src/component/table/example/sample/table-simple.ejs
new file mode 100644
index 000000000..a0f71e2ab
--- /dev/null
+++ b/src/component/table/example/sample/table-simple.ejs
@@ -0,0 +1,11 @@
+<%
+const table = locals.table || {};
+const dataSimple = JSON.parse(include('../data/data-simple.json.ejs', { table: table.table }));
+
+const data = {
+ ...table,
+ ...dataSimple
+}
+%>
+
+<%- include('./table-default', {table: data}); %>
diff --git a/src/component/table/example/sample/table-utility-classes.ejs b/src/component/table/example/sample/table-utility-classes.ejs
new file mode 100644
index 000000000..ee9b76fae
--- /dev/null
+++ b/src/component/table/example/sample/table-utility-classes.ejs
@@ -0,0 +1,32 @@
+<%
+const table = locals.table || {};
+const dataLorem = JSON.parse(include('../data/data-lorem.json.ejs', { table: table.table }));
+
+const utilities = ['top', 'bottom', 'center', 'right'];
+
+const loremThead = dataLorem.thead;
+loremThead.forEach(thead => {
+ thead.forEach((row, index) => {
+ row.content = utilities[index] || 'default';
+ });
+});
+
+const loremBodies = dataLorem.tbodies;
+loremBodies.forEach(tbodies => {
+ tbodies.forEach(tbody => {
+ tbody.forEach((row, index) => {
+ row.classes = utilities[index] ? [`${prefix}-cell--${utilities[index]}`] : [];
+ if (index === tbody.length - 1) {
+ row.content = `${lorem(null , 5)} ${lorem(null , 10)} ${lorem(null , 25)}`
+ }
+ });
+ });
+});
+
+const data = {
+ ...table,
+ table: dataLorem
+}
+%>
+
+<%- include('./table-default', {table: data}); %>
diff --git a/src/component/table/i18n/fr.yml b/src/component/table/i18n/fr.yml
new file mode 100644
index 000000000..656cb809e
--- /dev/null
+++ b/src/component/table/i18n/fr.yml
@@ -0,0 +1,71 @@
+title: Tableaux
+description: Les tableaux sont utilisés pour organiser et afficher les informations d'un jeu de données.
+
+subdir:
+ title: Autres versions
+ deprecated: version dépréciée
+
+sample:
+ bordered: Tableau avec bordures verticales
+ captionBottom: Tableau avec titre en bas
+ columnSizes:
+ subtitle: En ajoutant les classes fr-col--xs, fr-col--sm, fr-col--md ou fr-col--lg sur les cellules d'en-tête du tableau
+ title: Exemple des colonnes de tailles minimales fixées
+ complex:
+ subtitle: Lorsqu’un tableau de données contient des en-têtes qui ne sont pas répartis uniquement sur la première ligne et/ou la première colonne de la grille ou dont la portée n’est pas valable pour l’ensemble de la colonne ou de la ligne, on parle de tableau de données complexe. Il est alors nécessaire de fournir un « résumé » permettant d’en expliquer sa nature et sa structure afin d’en faciliter la consultation pour des utilisateurs de technologies d’assistance par exemple.
+ title: Tableau complexe (comportant des cellules fusionnées)
+ default: Tableau MD (défaut)
+ doubleEntry: Tableau à double entrée
+ lg: Tableau LG
+ miscellaneous: Tableau avec filtre et navigation comportant différents types de données
+ multiline:
+ subtitle: Il est possible de réactiver le retour à la ligne des éléments inline en ajoutant la classe fr-cell--multiline sur l'element table , une ligne ou une cellule du tableau
+ title: Exemple de cellules avec retour à la ligne automatique
+ noCaption: Tableau avec titre invisible
+ selectable: Tableau avec lignes sélectionnables
+ sm: Tableau SM
+ utilityClasses:
+ subtitle: Par défaut le contenu des cellules est centré verticalement et aligné à gauche mais peut être modifié à l'aide des classe utilitaires fr-cell--top, fr-cell--bottom pour l'alignement vertical et fr-cell--center, fr-cell--right pour l'alignement horizontal
+ title: Exemple des classes utilitaires d'alignement
+
+data:
+ caption:
+ detail: '(Résumé) Emploi du temps horaire des Groupes 1 et 2, le matin des jours de la semaine ouvrée (Lundi au Vendredi) : - la première colonne représente le planning de la journée de Lundi pour les groupes 1 et 2,
- la deuxième colonne représente le planning de la journée de Mardi pour les groupes 1 et 2,
- la troisième colonne représente le planning des journées de Mercredi et Jeudi pour le groupe 1,
- la quatrième colonne représente le planning des journées de Mercredi et Jeudi pour le groupe 2,
- la cinquième colonne représente le planning de la journée de Vendredi pour les groupes 1 et 2.
'
+ default: Titre du tableau (caption)
+ large: Titre long du tableau (caption) Repellat natus illo omnis nulla nostrum ut doloremque ipsam voluptas officiis cumque porro.
+ cell:
+ action:
+ button: Libellé bouton
+ empty: Action
+ sort: Trier
+ select: Sélectionner
+ selectAll: Sélectionner toutes les lignes
+ selectRow: Sélectionner la ligne
+ collapsed: Exemple de cellule fusionnée
+ doubleEntry: En tête de colonne [À MODIFIER]
+ download:
+ detail: JPG – 61,88 Ko
+ label: Lien de Téléchargement
+ label: Libellé par défaut
+ number: 30,00 €
+ text: Texte par défaut
+ title: Titre par défaut
+ navigation:
+ buttons:
+ label: Action tableau
+ detail: 215 lignes
+ select:
+ label: Nombre de lignes par page
+ placeholder: Nombre de lignes par page
+ options:
+ option1: 4 lignes par page
+ option2: 10 lignes par page
+ option3: 20 lignes par page
+ actionBar:
+ buttons:
+ label: Action groupée
+ detail: Nombre de lignes sélectionnées
+ segmented:
+ legend: Type d'affichage
+ list: Liste
+ table: Tableau
diff --git a/src/component/table/index.js b/src/component/table/index.js
index cbf23d3c4..073404a87 100644
--- a/src/component/table/index.js
+++ b/src/component/table/index.js
@@ -3,12 +3,14 @@ import { Table } from './script/table/table.js';
import { TableElement } from './script/table/table-element.js';
import { TableSelector } from './script/table/table-selector.js';
import { TableCaption } from './script/table/table-caption.js';
+import { TableRow } from './script/table/table-row.js';
api.table = {
Table: Table,
TableElement: TableElement,
TableCaption: TableCaption,
- TableSelector: TableSelector
+ TableSelector: TableSelector,
+ TableRow: TableRow
};
export default api;
diff --git a/src/component/table/legacy.scss b/src/component/table/legacy.scss
index d421e53a3..52928c089 100644
--- a/src/component/table/legacy.scss
+++ b/src/component/table/legacy.scss
@@ -12,5 +12,10 @@
@import 'index';
@import 'style/scheme';
@import 'style/legacy';
+@import 'deprecated/style/scheme';
@include _table-scheme(true);
+@include _deprecated-table-scheme(true);
+
+// deprecated
+@import 'deprecated/style/legacy';
diff --git a/src/component/table/main.js b/src/component/table/main.js
index 5205366aa..4fc3de0fa 100644
--- a/src/component/table/main.js
+++ b/src/component/table/main.js
@@ -3,5 +3,6 @@ import api from './index.js';
api.internals.register(api.table.TableSelector.TABLE, api.table.Table);
api.internals.register(api.table.TableSelector.ELEMENT, api.table.TableElement);
api.internals.register(api.table.TableSelector.CAPTION, api.table.TableCaption);
+api.internals.register(api.table.TableSelector.ROW, api.table.TableRow);
export default api;
diff --git a/src/component/table/main.scss b/src/component/table/main.scss
index 9f4572c8c..9c1541659 100644
--- a/src/component/table/main.scss
+++ b/src/component/table/main.scss
@@ -18,3 +18,9 @@
@import 'style/scheme';
@include _table-scheme;
+
+// deprecated
+@import 'deprecated/style/module';
+@import 'deprecated/style/scheme';
+
+@include _deprecated-table-scheme;
diff --git a/src/component/table/script/table/table-element.js b/src/component/table/script/table/table-element.js
index 083af8785..f82701565 100644
--- a/src/component/table/script/table/table-element.js
+++ b/src/component/table/script/table/table-element.js
@@ -1,7 +1,7 @@
import api from '../../api.js';
import { TableSelector } from './table-selector.js';
-const SCROLL_OFFSET = 8; // valeur en px du scroll avant laquelle le shadow s'active ou se desactive
+const SCROLL_OFFSET = 0; // valeur en px du scroll avant laquelle le shadow s'active ou se desactive
class TableElement extends api.core.Instance {
static get instanceClassName () {
@@ -11,6 +11,7 @@ class TableElement extends api.core.Instance {
init () {
this.listen('scroll', this.scroll.bind(this));
this.content = this.querySelector('tbody');
+ this.tableOffsetHeight = 0;
this.isResizing = true;
}
diff --git a/src/component/table/script/table/table-emission.js b/src/component/table/script/table/table-emission.js
index 5eb292441..ca3bec541 100644
--- a/src/component/table/script/table/table-emission.js
+++ b/src/component/table/script/table/table-emission.js
@@ -3,5 +3,6 @@ import api from '../../api.js';
export const TableEmission = {
SCROLLABLE: api.internals.ns.emission('table', 'scrollable'),
CHANGE: api.internals.ns.emission('table', 'change'),
- CAPTION_HEIGHT: api.internals.ns.emission('table', 'captionheight')
+ CAPTION_HEIGHT: api.internals.ns.emission('table', 'captionheight'),
+ CAPTION_WIDTH: api.internals.ns.emission('table', 'captionwidth')
};
diff --git a/src/component/table/script/table/table-row.js b/src/component/table/script/table/table-row.js
new file mode 100644
index 000000000..3c252a27d
--- /dev/null
+++ b/src/component/table/script/table/table-row.js
@@ -0,0 +1,41 @@
+import api from '../../api.js';
+import { CheckboxEmission } from '../../../checkbox/script/checkbox/checkbox-emission.js';
+
+class TableRow extends api.core.Instance {
+ static get instanceClassName () {
+ return 'TableRow';
+ }
+
+ init () {
+ if (api.checkbox) {
+ this.addAscent(CheckboxEmission.CHANGE, this._handleCheckboxChange.bind(this));
+ this.descend(CheckboxEmission.RETRIEVE);
+ }
+ }
+
+ _handleCheckboxChange (node) {
+ if (node.name === 'row-select') {
+ this.isSelected = node.checked === true;
+ }
+ }
+
+ render () {
+ const height = this.getRect().height + 2;
+ if (this._height === height) return;
+ this._height = height;
+ this.setProperty('--row-height', `${this._height}px`);
+ }
+
+ get isSelected () {
+ return this._isSelected;
+ }
+
+ set isSelected (value) {
+ if (this._isSelected === value) return;
+ this.isRendering = value;
+ this._isSelected = value;
+ this.setAttribute('aria-selected', value);
+ }
+}
+
+export { TableRow };
diff --git a/src/component/table/script/table/table-selector.js b/src/component/table/script/table/table-selector.js
index 3d7d187ba..d63ef9948 100644
--- a/src/component/table/script/table/table-selector.js
+++ b/src/component/table/script/table/table-selector.js
@@ -5,6 +5,8 @@ export const TableSelector = {
SHADOW: api.internals.ns.selector('table__shadow'),
SHADOW_LEFT: api.internals.ns.selector('table__shadow--left'),
SHADOW_RIGHT: api.internals.ns.selector('table__shadow--right'),
- ELEMENT: `${api.internals.ns.selector('table')}:not(${api.internals.ns.selector('table--no-scroll')}) table`,
- CAPTION: `${api.internals.ns.selector('table')} table caption`
+ ELEMENT: [`${api.internals.ns.selector('table')}:not(${api.internals.ns.selector('table--no-scroll')}) table`, `${api.internals.ns.selector('table')} ${api.internals.ns.selector('table__container')}`],
+ CAPTION: `${api.internals.ns.selector('table')} table caption`,
+ ROW: `${api.internals.ns.selector('table')} tbody tr`,
+ COL: `${api.internals.ns.selector('table')} thead th`
};
diff --git a/src/component/table/script/table/table.js b/src/component/table/script/table/table.js
index f56223a5a..2eb877868 100644
--- a/src/component/table/script/table/table.js
+++ b/src/component/table/script/table/table.js
@@ -9,6 +9,7 @@ class Table extends api.core.Instance {
}
init () {
+ this.rowsHeaderWidth = [];
this.addAscent(TableEmission.CAPTION_HEIGHT, this.setCaptionHeight.bind(this));
}
diff --git a/src/component/table/style/_legacy.scss b/src/component/table/style/_legacy.scss
index 2138a7e7a..6c5c860fc 100644
--- a/src/component/table/style/_legacy.scss
+++ b/src/component/table/style/_legacy.scss
@@ -4,15 +4,83 @@
////
@use 'module/legacy';
+@use 'module/spacing';
@include legacy.is(ie11) {
- /**
- * Correctif placement caption
- */
#{ns(table)} {
+ &__header {
+ #{ns(segmented)} {
+ flex: 1;
+ }
+
+ #{ns(table__detail)} {
+ flex: 2;
+ }
+ }
+
+ &__wrapper {
+ @include before(none);
+ }
+
+ &__content {
+ table {
+ border-top: 1px solid;
+ border-left: 1px solid;
+ border-right: 1px solid;
+
+ #{ns(cell--fixed)} {
+ @include relative;
+
+ #{ns(checkbox-group)} {
+ #{ns(label)} {
+ @include before(none);
+ }
+
+ input[type="checkbox"] {
+ top: spacing.space(2v);
+ }
+ }
+ }
+
+ tbody {
+ tr {
+ &[aria-selected=true] {
+ @include after(none);
+
+ th,
+ td {
+ border-top: 2px solid;
+ border-bottom: 2px solid;
+
+ &:first-child {
+ border-left: 2px solid;
+ }
+
+ &:last-child {
+ border-right: 2px solid;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
{ns-attr(js-table, true)} {
+ {ns(table--no-caption)},
+ {ns(table--caption-bottom)} {
+ #{ns(table__wrapper)} {
+ @include before(none);
+ }
+
+ caption {
+ @include relative;
+ }
+ }
+
caption {
@include relative;
+ @include margin-bottom(4v);
}
}
}
diff --git a/src/component/table/style/_module.scss b/src/component/table/style/_module.scss
index cd0fac194..5f0883948 100644
--- a/src/component/table/style/_module.scss
+++ b/src/component/table/style/_module.scss
@@ -4,5 +4,9 @@
////
@import 'module/default';
-@import 'module/variants';
+@import 'module/header';
+@import 'module/footer';
+@import 'module/sm';
+@import 'module/lg';
@import 'module/shadow';
+@import 'module/variants';
diff --git a/src/component/table/style/_scheme.scss b/src/component/table/style/_scheme.scss
index 208946d67..5c9513910 100644
--- a/src/component/table/style/_scheme.scss
+++ b/src/component/table/style/_scheme.scss
@@ -7,61 +7,69 @@
@mixin _table-scheme($legacy: false) {
#{ns(table)} {
- caption {
- @include color.text(title grey, (legacy: $legacy));
- }
-
- thead {
- @include color.background-image(border plain grey, (legacy: $legacy));
-
- @include color.background(contrast grey, (legacy: $legacy));
- @include color.text(title grey, (legacy: $legacy));
- }
-
- tbody {
- @include color.background(alt grey, (legacy: $legacy));
-
- tr:nth-child(even) {
- @include color.background(contrast grey, (legacy: $legacy));
+ &__wrapper {
+ @include before {
+ @include color.box-shadow(contrast grey, (legacy:$legacy), top-1-in left-1-in bottom-1-in right-1-in);
}
}
- @include color.accentuate {
- thead {
- @include color.background-image(border plain accent, (legacy: $legacy));
- @include color.background(contrast accent, (legacy: $legacy));
- }
-
- tbody {
- @include color.background(alt accent, (legacy: $legacy));
+ &__content {
+ table {
+ thead {
+ th {
+ @include color.background(alt grey, (legacy: $legacy));
+ @include color.background-image((border plain grey) (border contrast grey), (legacy: $legacy));
- tr:nth-child(even) {
- @include color.background(contrast accent, (legacy: $legacy));
+ &:last-child {
+ @include color.background(alt grey, (legacy: $legacy));
+ @include color.background-image((border plain grey), (legacy: $legacy));
+ }
+ }
}
- }
- /* Style bordered, ajoute des bordures entre chaque ligne */
- {ns(table--bordered)} {
tbody {
tr {
- @include color.background-image(border default accent, (legacy: $legacy));
+ &[aria-selected=true] {
+ &::after {
+ @include color.background-image((border action-high blue-france) (border action-high blue-france) (border action-high blue-france) (border action-high blue-france), (legacy: $legacy));
+ }
+ }
+ }
+
+ th,
+ td {
+ @include color.background-image((border contrast grey) (border contrast grey), (legacy: $legacy));
+ @include color.background(default grey, (legacy: $legacy));
+ }
+
+ th {
+ @include color.background(alt grey, (legacy: $legacy));
+ @include color.background-image((border contrast grey) (border contrast grey), (legacy: $legacy));
}
}
}
}
- /* Style bordered, ajoute des bordures entre chaque ligne */
- &--bordered {
- tbody {
- tr {
- @include color.background-image(border default grey, (legacy: $legacy));
+ &__shadow--left {
+ #{ns(table__content)} {
+ #{ns(cell--fixed)} {
+ @include color.background-image((border contrast grey) (border plain grey), (legacy: $legacy));
- /* Style bordered, enleve le style even/odd */
- &:nth-child(even) {
- @include color.transparent-background((legacy:$legacy, hover: true));
+ &[scope='col'] {
+ @include color.background-image((border plain grey) (border plain grey), (legacy: $legacy));
+ }
+ }
+
+ thead {
+ #{ns(cell--fixed)} {
+ @include color.background-image((border plain grey) (border plain grey), (legacy: $legacy));
}
}
}
}
+
+ &__detail {
+ @include color.text(mention grey, (legacy:$legacy));
+ }
}
}
diff --git a/src/component/table/style/module/_default.scss b/src/component/table/style/module/_default.scss
index 14bb23f97..e64eedf3b 100644
--- a/src/component/table/style/module/_default.scss
+++ b/src/component/table/style/module/_default.scss
@@ -3,35 +3,232 @@
/// @group Table
////
+@use 'module/media-query';
+@use 'module/color';
+@use 'module/spacing';
+
#{ns(table)} {
- --table-offset: #{space(4v)};
+ --table-offset: spacing.space(4v);
+ --row-height: 0;
+
@include set-text-margin(0);
@include set-title-margin(0);
-
@include relative;
@include margin-bottom(10v);
- @include padding-top(var(--table-offset));
- @include before('', block) {
- @include size(100%, 0);
+ &__wrapper {
+ @include padding(1px 1px 0);
+ @include margin(-1px 0);
}
- &:not(#{ns(table--no-scroll)}) {
+ &__container {
+ overflow: auto;
+ @include padding-top(var(--table-offset));
+ }
+
+ &__content {
+ width: 100%;
+ min-width: 40rem;
+
+ #{ns(cell)} {
+ &--fixed {
+ position: sticky;
+ left: 0;
+ }
+
+ &--center {
+ text-align: center;
+ }
+
+ &--right {
+ text-align: right;
+ }
+
+ &--top {
+ vertical-align: top;
+ }
+
+ &--bottom {
+ vertical-align: bottom;
+ }
+
+ &__title {
+ font-weight: 700;
+ }
+
+ &__desc {
+ font-weight: 400;
+ }
+
+ &--inline {
+ display: flex;
+ align-items: center;
+ @include margin(0 -2v);
+
+ > * {
+ @include margin(0 2v);
+ }
+ }
+
+ &--multiline {
+ &,
+ & * {
+ white-space: normal;
+ }
+ }
+
+ &--sort {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ #{ns(btn--sort)} {
+ @include margin-left(4v);
+ }
+ }
+ }
+
+ #{ns(col)} {
+ &--xs {
+ white-space: normal;
+ @include min-width(16v);
+ }
+
+ &--sm {
+ white-space: normal;
+ @include min-width(20v);
+ }
+
+ &--md {
+ white-space: normal;
+ @include min-width(50v);
+ }
+
+ &--lg {
+ white-space: normal;
+ @include min-width(100v);
+ }
+ }
+
table {
- width: 100%;
+ @include size(100%);
+ border-spacing: 0;
+
+ thead {
+ position: relative;
+
+ @include before('', block) {
+ @include absolute(inherit, 0, 0 , 0, 100%, 1px);
+ pointer-events: none;
+ @include z-index(over);
+ }
+
+ th {
+ @include font-weight(bold);
+ }
+ }
+
+ tbody {
+ tr:first-child[aria-selected=true]:has(+ tr:not([aria-selected=true])),
+ tr:not([aria-selected=true]) + tr:last-child[aria-selected=true],
+ tr:not([aria-selected=true]) + tr[aria-selected=true]:has(+ tr:not([aria-selected=true])) {
+ &::after {
+ // selected borders left-right + top-bottom
+ background-size: 100% 2px, 2px 100%, 2px 100%, 100% 2px;
+ background-repeat: no-repeat, no-repeat, no-repeat, no-repeat;
+ background-position: 100% 100%, 0 0, 100% 0, 100% 0;
+ }
+ }
+
+ tr[aria-selected=true] + tr[aria-selected=true] {
+ &::after {
+ // selected borders left-right
+ background-size: 2px 100%, 2px 100%;
+ background-repeat: no-repeat, no-repeat;
+ background-position: 0 0, 100% 0;
+ }
+ }
+
+ tr:first-child[aria-selected=true]:has(+ tr[aria-selected=true]),
+ tr:not([aria-selected=true]) + tr[aria-selected=true] {
+ &::after {
+ // selected borders left-right + top
+ background-size: 2px 100%, 2px 100%, 100% 2px;
+ background-repeat: no-repeat, no-repeat, no-repeat;
+ background-position: 0 0, 100% 0, 100% 0;
+ }
+ }
+
+ tr[aria-selected=true] + tr:last-child[aria-selected=true],
+ tr[aria-selected=true]:has(+ tr:not([aria-selected=true])) {
+ &::after {
+ // selected borders left-right + bottom
+ background-size: 100% 2px, 2px 100%, 2px 100%;
+ background-repeat: no-repeat, no-repeat, no-repeat;
+ background-position: 100% 100%, 0 0, 100% 0;
+ }
+ }
+
+ tr {
+ &[aria-selected="true"] {
+ &::after {
+ @include absolute(null, null, null, 0, 100%, var(--row-height));
+ @include z-index(above);
+ content: '';
+ pointer-events: none;
+ transform: translateY(spacing.space(-0.5v));
+
+ // selected borders left-right
+ background-size: spacing.space(0.5v 100%), spacing.space(0.5v 100%);
+ background-repeat: no-repeat, no-repeat;
+ background-position: 0 0, 100% 0;
+ }
+ }
+ }
+ }
}
- }
- table {
- @include size(100%);
- display: block;
- overflow: auto;
- border-spacing: 0;
- }
+ th,
+ td {
+ display: table-cell;
+ @include padding(2v 4v);
+ @include text-style(sm);
+ text-align: left;
+ vertical-align: middle;
+ white-space: nowrap;
+ background-size: 100% 1px;
+ background-repeat: no-repeat;
+ background-position: 100% 100%;
- {ns-attr(js-table, true)} {
- caption {
- @include absolute(0);
+ #{ns(checkbox-group)} {
+ display: inline-block;
+
+ #{ns(label)} {
+ @include text-style(sm);
+ }
+ }
+ }
+
+ #{ns(cell--fixed)} {
+ #{ns(checkbox-group)} {
+ & + * {
+ @include margin-left(2v);
+ }
+
+ #{ns(label)} {
+ color: transparent;
+ @include size(0);
+ pointer-events: none;
+
+ &::before {
+ left: spacing.space(-5v);
+ }
+ }
+
+ input[type="checkbox"] {
+ left: spacing.space(1v);
+ }
+ }
}
}
@@ -40,33 +237,30 @@
@include title-style(h4, true);
@include font-weight(bold);
text-align: left;
+
+ #{ns(table__caption__desc)} {
+ @include margin-top(4v);
+ @include text-style(md);
+ @include font-weight(regular);
+ }
}
- td,
- th {
- text-align: left;
- vertical-align: middle;
- display: table-cell;
- border: 0;
- @include padding(3v);
- @include padding(4v, md);
+ &__detail {
@include text-style(sm);
}
- th {
- font-weight: font-weight(bold);
- }
+ {ns-attr(js-table, true)} {
+ caption {
+ @include absolute(0);
+ }
- thead {
- background-size: 100% 2px;
- background-position: bottom;
- background-repeat: no-repeat;
+ #{ns(table__wrapper)} {
+ position: relative;
- td,
- th {
- font-weight: font-weight(bold);
- @include padding-bottom(3.5v); // 0.5v for the box shadow
- @include padding-bottom(4.5v, md);
+ @include before('', block) {
+ @include absolute(inherit, 0, 0 , 0, 100%, calc(100% - var(--table-offset)));
+ pointer-events: none;
+ }
}
}
}
diff --git a/src/component/table/style/module/_footer.scss b/src/component/table/style/module/_footer.scss
new file mode 100644
index 000000000..ef27a15d7
--- /dev/null
+++ b/src/component/table/style/module/_footer.scss
@@ -0,0 +1,76 @@
+////
+/// Table Module - footer
+/// @group Table
+////
+
+@use 'module/media-query';
+
+#{ns(table)} {
+ &__footer {
+ @include media-query.respond-from(md) {
+ @include display-flex(row, center, space-between, wrap);
+
+ #{ns(select-group)} {
+ #{ns(select)} {
+ @include size(64v);
+ }
+ }
+
+ #{ns(btns-group)} {
+ @include padding-left(2v);
+ }
+ }
+
+ &--start {
+ @include margin-top(4v);
+
+ @include media-query.respond-from(md) {
+ @include display-flex(row, center, flex-start);
+
+ #{ns(select-group)} {
+ @include padding-left(2v);
+ @include padding-right(2v);
+
+ #{ns(select)} {
+ @include margin-top(0);
+ }
+ }
+ }
+
+ #{ns(table__detail)} {
+ @include padding-right(2v);
+ }
+ }
+
+ &--middle {
+ @include margin-top(4v);
+
+ @include display-flex(row, center, center);
+
+ @include media-query.respond-from(lg) {
+ display: block;
+ @include padding-left(2v);
+ @include padding-right(2v);
+ }
+
+ #{ns(pagination)} {
+ @include margin-top(3v);
+
+ &__list {
+ @include media-query.respond-from(lg) {
+ flex-wrap: nowrap;
+ }
+ }
+ }
+ }
+
+ &--end {
+ @include media-query.respond-from(lg) {
+ flex: 1 0 0;
+ }
+
+ @include margin-top(6v);
+ flex: 1 0 100%;
+ }
+ }
+}
diff --git a/src/component/table/style/module/_header.scss b/src/component/table/style/module/_header.scss
new file mode 100644
index 000000000..a3594d013
--- /dev/null
+++ b/src/component/table/style/module/_header.scss
@@ -0,0 +1,46 @@
+////
+/// Table Module - footer
+/// @group Table
+////
+
+@use 'module/media-query';
+
+#{ns(table)} {
+ &__header {
+ @include media-query.respond-from(md) {
+ @include display-flex(row, center, space-between);
+
+ #{ns(table__detail)} {
+ order: 1;
+ flex: 1;
+ @include padding-right(2v);
+ }
+
+ #{ns(btns-group)} {
+ order: 2;
+ @include padding-left(2v);
+ @include padding-right(2v);
+ }
+
+ #{ns(segmented)} {
+ order: 3;
+ flex: 0;
+ @include padding-left(2v);
+ }
+ }
+
+ #{ns(btns-group)} {
+ @include media-query.respond-from(md) {
+ @include padding-right(2v);
+ }
+ }
+
+ #{ns(table__detail)} {
+ @include margin-bottom(4v);
+ }
+
+ #{ns(segmented)} {
+ @include margin-bottom(4v);
+ }
+ }
+}
diff --git a/src/component/table/style/module/_lg.scss b/src/component/table/style/module/_lg.scss
new file mode 100644
index 000000000..016a7f0bf
--- /dev/null
+++ b/src/component/table/style/module/_lg.scss
@@ -0,0 +1,13 @@
+////
+/// Table Module - lg
+/// @group Table
+////
+
+#{ns(table)} {
+ &--lg &__content {
+ th,
+ td {
+ @include padding(3v 6v);
+ }
+ }
+}
diff --git a/src/component/table/style/module/_shadow.scss b/src/component/table/style/module/_shadow.scss
index e6f890c2e..cabc7f066 100644
--- a/src/component/table/style/module/_shadow.scss
+++ b/src/component/table/style/module/_shadow.scss
@@ -6,68 +6,33 @@
@use 'module/selector';
/*
-* Ombres ajoutées en Js si le contenu est plus grand que le conteneur
+* Bordure ajoutée à gauche en Js si le contenu est plus grand que le conteneur
*/
-#{ns(table__shadow)} {
- @include before('', block) {
- @include absolute(var(--table-offset), 0, 0, 0);
- @include z-index(over);
- @include _table-scroll-shadow(false, false);
- opacity: 0.32;
- pointer-events: none;
- transition: box-shadow 0.3s;
- }
-
- /**
- * Modifier ombre à gauche
- **/
- &--left {
- @include before {
- @include _table-scroll-shadow(true, false); // @TODO: à implementer dans la mixin shadow
- }
- }
-
- /**
- * Modifier ombre à droite
- **/
- &--right {
- @include before {
- @include _table-scroll-shadow(false, true);// @TODO: à implementer dans la mixin shadow
- }
- }
-
- /**
- * Modifier combinaison ombre à gauche et ombre à droite
- **/
- &--left#{&}--right {
- @include before('', block) {
- @include _table-scroll-shadow(true, true); // @TODO: à implementer dans la mixin shadow
+#{ns(table)} {
+ #{ns(table__shadow)} {
+ @include after('', block) {
+ @include absolute(var(--table-offset), 0, 0, 0);
+ @include z-index(over);
+ @include _table-scroll-shadow(false, false);
+ opacity: 0.32;
+ pointer-events: none;
+ transition: box-shadow 0.3s;
}
- }
-}
-/*
-* Positionnement ombres sur le tableau sans caption
-*/
-#{ns(table--no-caption)} #{ns(table__shadow)} {
- @include before('', block) {
- @include absolute(0, 0, 0, 0);
- }
-}
-
-/*
-* Positionnement ombres sur le tableau avec caption en bas
-*/
-#{ns(table--caption-bottom)} #{ns(table__shadow)} {
- @include before('', block) {
- @include absolute(0, 0, 0, 0);
- }
-}
-
-@include selector.theme(dark) {
- #{ns(table__shadow)} {
- @include before {
- opacity: 1;
+ /**
+ * Modifier Bordure à gauche
+ **/
+ &--left {
+ th[scope='row'] {
+ @include z-index(over);
+ }
+
+ #{ns(cell--fixed)} {
+ @include z-index(over);
+ background-size: 100% 1px, 1px 100%;
+ background-repeat: no-repeat, no-repeat;
+ background-position: 0 100%, 100% 0;
+ }
}
}
}
diff --git a/src/component/table/style/module/_sm.scss b/src/component/table/style/module/_sm.scss
new file mode 100644
index 000000000..5f8df38f4
--- /dev/null
+++ b/src/component/table/style/module/_sm.scss
@@ -0,0 +1,13 @@
+////
+/// Table Module - sm
+/// @group Table
+////
+
+#{ns(table)} {
+ &--sm &__content {
+ th,
+ td {
+ @include padding(1v 3v);
+ }
+ }
+}
diff --git a/src/component/table/style/module/_variants.scss b/src/component/table/style/module/_variants.scss
index e5162d460..bb93a5006 100644
--- a/src/component/table/style/module/_variants.scss
+++ b/src/component/table/style/module/_variants.scss
@@ -3,76 +3,74 @@
/// @group Table
////
-/*
-* Cache la caption
-*/
-#{ns(table--no-caption)} {
- @include padding-top(0);
+#{ns(table)} {
+ /*
+ * Cache la caption
+ */
+ {ns(table--no-caption)} {
+ #{ns(table__wrapper)} {
+ @include before {
+ height: 100%;
+ }
- caption {
- @include sr-only();
- @include height(0);
- }
-}
-
-/*
-* Fixe le caption en bas du tableau
-*/
-#{ns(table--caption-bottom)} {
- @include padding-top(0);
- @include margin-bottom(0);
- @include margin-top(4v);
-
- table {
- @include margin-bottom(calc(var(--table-offset) + 11v));
- }
+ #{ns(table__container)} {
+ @include padding-top(0);
+ }
- {ns-attr(js-table, true)} {
- caption {
- @include absolute(100%, 0, 0, 0);
- @include margin-top(4v);
+ caption {
+ @include sr-only();
+ @include height(0);
+ }
}
}
- caption {
- @include margin-top(4v);
- @include height(min-content);
- caption-side: bottom;
- }
-}
+ /*
+ * Fixe le caption en bas du tableau
+ */
+ {ns(table--caption-bottom)} {
+ @include margin-bottom(0);
-/*
-* pas de scroll ni de shadow
-*/
-#{ns(table--no-scroll)} {
- @include min-width(auto);
+ #{ns(table__wrapper)} {
+ @include margin-bottom(calc(var(--table-offset) + 6v));
- table {
- overflow-x: hidden;
- }
+ &::before {
+ height: 100%;
+ }
- caption {
- @include max-width(calc(100vw - 8v)); // eol in mobile even if table overflow
- }
-}
+ #{ns(table__container)} {
+ @include padding-top(0);
+ }
+
+ caption {
+ @include margin-top(4v);
+ @include height(min-content);
+ caption-side: bottom;
+ }
+ }
-/*
-* Fixe la taille des colonnes du tableau
-*/
-#{ns(table--layout-fixed)} {
- table {
- display: table;
- table-layout: fixed;
+ {ns-attr(js-table, true)} {
+ caption {
+ @include absolute(100%, 0, 0, 0);
+ @include margin-top(4v);
+ }
+ }
}
-}
-/* Style bordered, ajoute des bordures entre chaque ligne */
-#{ns(table--bordered)} {
- tbody {
- tr {
- background-size: 100% 1px;
- background-position: bottom;
- background-repeat: no-repeat;
+ /* Style bordered, ajoute des bordures verticales entre chaque cellule */
+ {ns(table--bordered)} {
+ #{ns(table__content)} {
+ th,
+ td {
+ background-size: 100% 1px, 1px 100%;
+ background-repeat: no-repeat, no-repeat;
+ background-position: 0 100%, 100% 0;
+
+ &:last-child {
+ background-size: 100% 1px;
+ background-repeat: no-repeat;
+ background-position: 100% 100%;
+ }
+ }
}
}
}
diff --git a/src/component/table/template/ejs/table.ejs b/src/component/table/template/ejs/table.ejs
index 3ebafc4b3..ee31e0513 100644
--- a/src/component/table/template/ejs/table.ejs
+++ b/src/component/table/template/ejs/table.ejs
@@ -1,72 +1,44 @@
<%#
# paramètres Table
-* table.data (array(array), required): tableau de données à deux dimension [row][col] (la première ligne (row = 0) est reservée aux | )
+* table.thead (array, required): tableau de données de l'en-tête du tableau
-* table.caption (string, required) : titre du tableau
-
-* table.noScroll (boolean, optional) : {default: false} désactive le scroll
-
-* table.noCaption (boolean, optional) : {default: false} cache le texte de la caption
-
-* table.captionBottom (boolean, optional) : {default: false} positionne la caption en bas
-
-* table.bordered (boolean, optional) : {default: false} si true, ajoute des séparateurs entre chaque ligne et enleve l'effet even/odd
-
-* table.layout (string, optional) : si non undefined, fix la taille des colonnes à 100%/col
+* table.tbodies (array, required): tableau de données du corps du tableau
-* table.col (integer, optional) : nombre de colones à afficher (si différent du nombre de colones de data)
-
-* table.row (integer, optional) : nombre de lignes à afficher (si différent du nombre de lignes de data)
+* table.caption (string, required) : titre du tableau
-* table.accent (string, optional): couleur d'accenturation du composant
+* table.captionDetail (string, optional) : description précise du tableau
-* table.id (string) : id de l'élément
+* table.id (string) : id du tableau
-* table.classes (array, optional): classes supplémentaires du composant
+* table.classes (array, optional): classes supplémentaires du tableau
-* table.attributes (array, optional): attributs supplémentaires du composant
+* table.attributes (object, optional): attributs supplémentaires du tableau
%>
-<% eval(include('../../../../core/index.ejs')); %>
-<%
+<% eval(include('../../../../core/index.ejs'));
+
let table = locals.table || {};
let classes = table.classes || [];
const attributes = table.attributes || {};
-attributes.id = table.id || uniqueId('table');
-const data = table.data || [[]];
-
-classes.push(prefix + '-table');
-if (table.accent !== undefined) classes.push(prefix + '-table--' + table.accent);
-if (table.bordered) classes.push(prefix + '-table--bordered');
-if (table.noScroll) classes.push(prefix + '-table--no-scroll');
-if (table.noCaption) classes.push(prefix + '-table--no-caption');
-if (table.captionBottom) classes.push(prefix + '-table--caption-bottom');
-if (table.layout) classes.push(prefix + '-table--layout-fixed');
-if (!table.row) table.row = data.length;
-if (!table.col) table.col = data[0].length;
+attributes.id = table.id || uniqueId('-table');
%>
- <%- includeAttrs(attributes) %>>
-
- <% if (table.caption !== undefined) { %>
- <%= table.caption %>
- <% } %>
-
-
- <% for(let col = 0; col < table.col; col++) { %>
- <%- table.data[0][col] %> |
- <% } %>
-
-
-
- <% for(let row = 1; row < table.row; row++) { %>
-
- <% for(let col = 0; col < table.col; col++) { %>
- <%- table.data[row][col] %> |
- <% } %>
-
+ <%- includeAttrs(attributes) %>>
+ <% if (table.caption !== undefined) { %>
+
+ <%= table.caption %>
+ <% if (table.captionDetail !== undefined) { %>
+ <%- table.captionDetail %>
<% } %>
-
-
-
+
+ <% } %>
+ <% if (table.thead) { %>
+ <%- include('./thead.ejs', {thead: table.thead}); %>
+ <% } %>
+ <% if (table.tbodies) { %>
+ <% for (let index = 0; index < table.tbodies.length; index++) { %>
+ <%- include('./tbody.ejs', {tbody: {table, index}}); %>
+ <% } %>
+ <% } %>
+
diff --git a/src/component/table/template/ejs/tbody.ejs b/src/component/table/template/ejs/tbody.ejs
new file mode 100644
index 000000000..6c3a6e4cc
--- /dev/null
+++ b/src/component/table/template/ejs/tbody.ejs
@@ -0,0 +1,42 @@
+<%#
+# paramètres Tbody
+
+* tbody.table (object, required): contenu du tableau
+ ** tbody.table.tbodies (array, required): tableau de données du tbody à trois dimensions [body][row][col]
+ *** tbody.table.tbodies[body][row][col].content (string, required): contenu de la cellule
+ *** tbody.table.tbodies[body][row][col].markup (string, optional): balise de la cellule (th, td)
+ *** tbody.table.tbodies[body][row][col].classes (array, optional): classes supplémentaires de la cellule
+ *** tbody.table.tbodies[body][row][col].attributes (array, optional): attributs supplémentaires de la cellule
+%>
+
+<% eval(include('../../../../core/index.ejs'));
+
+const table = tbody.table || {};
+const index = tbody.index || 0;
+%>
+
+
+ <% if (table.tbodies && table.tbodies[index]) { %>
+ <% for (let row = 0; row < table.tbodies[index].length; row++) { %>
+ <%
+ const rowAttributes = {};
+ rowAttributes.id = `${table.id}-row-key-${row + 1}`;
+ rowAttributes['data-row-key'] = row + 1;
+ %>
+ >
+ <% if (table.tbodies[index].length) { %>
+ <% for (let col = 0; col < table.tbodies[index][row].length; col++) { %>
+ <%
+ const colMarkup = table.tbodies[index][row][col].markup || 'td';
+ const colClasses = table.tbodies[index][row][col].classes || [];
+ const colAttributes = table.tbodies[index][row][col].attributes || {};
+ %>
+ <<%- colMarkup %> <%- includeClasses(colClasses) %> <%- includeAttrs(colAttributes) %>>
+ <%- table.tbodies[index][row][col].content %>
+ <%- colMarkup %>>
+ <% } %>
+ <% } %>
+
+ <% } %>
+ <% } %>
+
diff --git a/src/component/table/template/ejs/thead.ejs b/src/component/table/template/ejs/thead.ejs
new file mode 100644
index 000000000..6d29f6940
--- /dev/null
+++ b/src/component/table/template/ejs/thead.ejs
@@ -0,0 +1,31 @@
+<%#
+# paramètres Thead
+
+* thead (array, required): tableau de données de l'en-tête à deux dimensions [row][col]
+ ** thead[row][col].content (string, required): contenu de la cellule
+ ** thead[row][col].classes (array, optional): classes supplémentaires de la cellule
+ ** thead[row][col].attributes (array, optional): attributs supplémentaires de la cellule
+%>
+
+<% eval(include('../../../../core/index.ejs'));
+
+let thead = locals.thead || {};
+%>
+
+
+ <% for (let row = 0; row < thead.length; row++) { %>
+
+ <% if (thead[row] && thead[row].length) { %>
+ <% for (let col = 0; col < thead[row].length; col++) { %>
+ <%
+ let colClasses = thead[row][col].classes || [];
+ const colAttributes = thead[row][col].attributes || {};
+ %>
+ <%- includeAttrs(colAttributes) %>>
+ <%- thead[row][col].content %>
+ |
+ <% } %>
+ <% } %>
+
+ <% } %>
+
diff --git a/src/component/table/template/ejs/wrapper.ejs b/src/component/table/template/ejs/wrapper.ejs
new file mode 100644
index 000000000..10f9f5703
--- /dev/null
+++ b/src/component/table/template/ejs/wrapper.ejs
@@ -0,0 +1,54 @@
+<%#
+# paramètres Table wrapper
+
+* wrapper.table (object, required) : contenu du tableau
+
+* wrapper.noCaption (boolean, optional) : {default: false} cache le texte de la caption
+
+* wrapper.captionBottom (boolean, optional) : {default: false} positionne la caption en bas
+
+* wrapper.bordered (boolean, optional) : {default: false} si true, ajoute des séparateurs entre chaque ligne et enleve l'effet even/odd
+
+* wrapper.classes (array, optional): classes supplémentaires du composant
+
+* wrapper.attributes (object, optional): attributs supplémentaires du composant
+
+* wrapper.header (boolean, optional): {default: false} affiche l'en-tête du composant
+
+* wrapper.footer (boolean, optional): {default: false} affiche le pied de page du composant
+%>
+
+<% eval(include('../../../../core/index.ejs'));
+
+let wrapper = locals.table || {};
+let classes = wrapper.classes || [];
+
+const attributes = wrapper.attributes || {};
+
+const table = wrapper.table || [[]];
+attributes.id = table.id + '-component';
+
+classes.push(prefix + '-table');
+
+if (wrapper.noCaption) classes.push(prefix + '-table--no-caption');
+if (wrapper.captionBottom) classes.push(prefix + '-table--caption-bottom');
+if (wrapper.bordered) classes.push(prefix + '-table--bordered');
+%>
+
+ <%- includeAttrs(attributes) %>>
+ <% if (wrapper.header !== undefined) { %>
+
+ <% } %>
+ <% if (table) { %>
+
+
+
+ <%- include('./table.ejs', {table: table}); %>
+
+
+
+ <% } %>
+ <% if (wrapper.footer !== undefined) { %>
+
+ <% } %>
+
diff --git a/src/component/tooltip/style/_module.scss b/src/component/tooltip/style/_module.scss
index f619ba16f..69c0189ad 100644
--- a/src/component/tooltip/style/_module.scss
+++ b/src/component/tooltip/style/_module.scss
@@ -20,6 +20,7 @@
background-repeat: no-repeat;
background-position: spacing.space(calc(50% + var(--arrow-x)) calc(100% - 2v)), spacing.space(calc(50% + var(--arrow-x)) calc(100% - $decimalVUnity)), spacing.space(50% calc(100% - 3v)), spacing.space(50% calc(100% - 3v));
background-size: spacing.space(2v 1.5v), spacing.space(2v 1.5v), 100% 1px, spacing.space(100% calc(100% - 3v));
+ white-space: normal;
&:not(&--shown) {
// transition in/out
diff --git a/src/core/icon/system/arrow-up-down-line.svg b/src/core/icon/system/arrow-up-down-line.svg
new file mode 100644
index 000000000..fd32062ae
--- /dev/null
+++ b/src/core/icon/system/arrow-up-down-line.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/core/script/collapse/collapse.js b/src/core/script/collapse/collapse.js
index 372a4e807..20e59f016 100644
--- a/src/core/script/collapse/collapse.js
+++ b/src/core/script/collapse/collapse.js
@@ -19,42 +19,43 @@ class Collapse extends Disclosure {
init () {
super.init();
- this.listen('transitionend', this.transitionend.bind(this));
+ this.listen('transitionend', this.endCollapsing.bind(this));
}
- transitionend (e) {
+ endCollapsing (e) {
+ if (!this._isCollpasing) return;
+ if (this._timeout) clearTimeout(this._timeout);
+ this._timeout = null;
+ this._isCollpasing = false;
this.removeClass(CollapseSelector.COLLAPSING);
if (!this.isDisclosed) {
if (this.isLegacy) this.style.maxHeight = '';
- else this.style.removeProperty('--collapse-max-height');
}
}
unbound () {
if (this.isLegacy) this.style.maxHeight = 'none';
- else this.style.setProperty('--collapse-max-height', 'none');
}
disclose (withhold) {
if (this.isDisclosed === true || !this.isEnabled) return false;
this.unbound();
- this.request(() => {
- this.addClass(CollapseSelector.COLLAPSING);
- this.adjust();
- this.request(() => {
- super.disclose(withhold);
- });
- });
+ this.collapsing(() => super.disclose(withhold));
}
conceal (withhold, preventFocus) {
if (this.isDisclosed === false) return false;
+ this.collapsing(() => super.conceal(withhold, preventFocus));
+ }
+
+ collapsing (request) {
+ this._isCollpasing = true;
+ if (this._timeout) clearTimeout(this._timeout);
+ this.addClass(CollapseSelector.COLLAPSING);
+ this.adjust();
this.request(() => {
- this.addClass(CollapseSelector.COLLAPSING);
- this.adjust();
- this.request(() => {
- super.conceal(withhold, preventFocus);
- });
+ request();
+ this._timeout = setTimeout(this.endCollapsing.bind(this), 500);
});
}
diff --git a/src/core/style/collapse/_tool.scss b/src/core/style/collapse/_tool.scss
index 417449ccc..55c2c5779 100644
--- a/src/core/style/collapse/_tool.scss
+++ b/src/core/style/collapse/_tool.scss
@@ -4,13 +4,13 @@
////
@mixin collapse($className: collapse) {
- --collapse-max-height: 0;
--collapse: -99999px;
--collapser: '';
+ --collapse-max-height: none;
overflow: hidden;
transition: visibility 0.3s;
- max-height: 0;
+ // max-height: 0;
max-height: var(--collapse-max-height);
&::before {
@@ -21,6 +21,10 @@
margin-top: 0;
}
+ #{&}:not(&--expanded):not(#{ns(collapsing)}) {
+ --collapse-max-height: 0;
+ }
+
#{&}:not(&--expanded) {
visibility: hidden;
|