1+ <template >
2+ <table
3+ ref =" TableComponent"
4+ :class =" [
5+ 'table-component',
6+ `table-component--linear-${breakpoint}`
7+ ]"
8+ >
9+ <slot />
10+ </table >
11+ </template >
12+
13+ <script setup lang="ts">
14+ import { PropType , computed , ref } from ' vue' ;
15+ import { Breakpoint , validateBreakpoint } from ' ../types/Breakpoint' ;
16+
17+ /**
18+ * Tables display categorical information organized across rows and columns in
19+ * order to facilitate the comparative analysis of data.
20+ *
21+ * The table component provides a wrapper around the common HTML table
22+ * elements such as `<thead>`, `<tbody>`, `<tr>`, `<th>` and `<td>`, to apply
23+ * design system styles to tabular data.
24+ *
25+ * Adding a `data-header` attribute to the cells allows us to maintain the column
26+ * headers and display them in the table's linearized form to provide additional
27+ * context.
28+ *
29+ * **Example:**
30+ *
31+ * ```html
32+ * <td data-header="Column Header">Content Here</td>
33+ * ```
34+ */
35+ const TableComponent = ref (' TableComponent' );
36+ const props = defineProps ({
37+ linearize: {
38+ type: String as PropType <Breakpoint >,
39+ default: Breakpoint .Tablet ,
40+ validator : (value : Breakpoint ): boolean => {
41+ return validateBreakpoint ( value );
42+ }
43+ }
44+ });
45+
46+ const breakpoint = computed <string >(() => {
47+ return validateBreakpoint ( props .linearize ) ? props .linearize : ' tablet' ;
48+ });
49+
50+ defineExpose ({TableComponent });
51+
52+ </script >
53+
54+ <style lang="scss">
55+ @import " @wikimedia/codex-design-tokens/theme-wikimedia-ui" ;
56+
57+ @mixin linear-table {
58+ /* *
59+ * Completely removes thead, modern screen readers will expose the
60+ * generated content
61+ */
62+ thead {
63+ display : none ;
64+ visibility : hidden ;
65+ }
66+
67+ /* *
68+ * Make everything display flex for alignment
69+ */
70+ tbody ,
71+ tr ,
72+ th ,
73+ td {
74+ height : auto ;
75+ display : flex ;
76+ flex-direction : column ;
77+ }
78+
79+ td ,
80+ th {
81+ flex-direction : row ;
82+ flex-basis : 60% ;
83+ }
84+
85+ /* *
86+ * Labeling
87+ *
88+ * Adding a data-header attribute to the cells
89+ * lets us add text before the content to provide
90+ * the missing context.
91+ *
92+ * Markup:
93+ * <td data-header="Column Header">Content Here</td>
94+ */
95+ /* stylelint-disable selector-no-qualifying-type */
96+ th [data-header ]::before ,
97+ td [data-header ]::before {
98+ content : attr (data-header );
99+ display : block ;
100+ font-weight : $font-weight-bold ;
101+ flex-basis : 40% ;
102+
103+ // Ensure headers stay exactly 40%
104+ // even if values are wider than 60%
105+ min-width : 40% ;
106+ }
107+
108+ th :not ([data-header ]) {
109+ font-weight : $font-weight-bold ;
110+ }
111+
112+ // Hide empty cells
113+ td :empty {
114+ display : none ;
115+ }
116+ }
117+
118+ .table-component {
119+ /* *
120+ * Layout
121+ */
122+ // As the specificationn state that the table columns should gain their
123+ // width from cell content (instead of just header content / width) we
124+ // revert to using the default table layout algorithm. This is done in
125+ // order to avoid changing the table's display proterty and thus
126+ // oblitirating it's inehrit accesibility:
127+ // See: https://www.tpgi.com/short-note-on-what-css-display-properties-do-to-table-semantics/
128+ table-layout : auto ;
129+ width : 100% ;
130+
131+ /* *
132+ * Borders
133+ */
134+ border-collapse : collapse ;
135+
136+ /* *
137+ * Colors
138+ */
139+ background-color : $background-color-base ;
140+ color : $color-base ;
141+
142+ /* *
143+ * Typography
144+ */
145+ font-family : $font-family-system-sans ;
146+ font-size : $font-size-medium ;
147+ font-weight : $font-weight-normal ;
148+
149+ tr {
150+ /* *
151+ * Layout
152+ */
153+
154+ /* *
155+ * Borders
156+ */
157+ border-bottom : $border-width-base $border-color-subtle solid ;
158+ border-radius : 0 ;
159+ }
160+
161+ tbody tr :hover {
162+ background-color : $background-color-interactive ;
163+ transition-duration : $transition-duration-medium ;
164+ transition-timing-function : $transition-timing-function-system ;
165+ transition-property : $transition-property-base ;
166+ }
167+
168+ th ,
169+ td {
170+ /* *
171+ * Layout
172+ */
173+ padding-inline : .75em ;
174+
175+ /* *
176+ * Typography
177+ */
178+ line-height : 20px ;
179+ text-align : start ;
180+ overflow-wrap : break-word ;
181+ hyphens : auto ;
182+ }
183+
184+ td {
185+ /* *
186+ * Layout
187+ */
188+ height : 48px ;
189+ padding-block : .5em ;
190+
191+ /* *
192+ * Typography
193+ */
194+ vertical-align : middle ;
195+ }
196+
197+ th {
198+ /* *
199+ * Layout
200+ */
201+ padding-block : .75em ;
202+
203+ /* *
204+ * Typography
205+ */
206+ font-weight : 700 ;
207+ vertical-align : top ;
208+ }
209+
210+ & --linear-mobile {
211+ @media (max-width : $max-width-breakpoint-mobile ) {
212+ @include linear-table ;
213+ }
214+ }
215+
216+ & --linear-tablet {
217+ @media (max-width : $max-width-breakpoint-tablet ) {
218+ @include linear-table ;
219+ }
220+ }
221+
222+ & --linear-desktop {
223+ @media (max-width : $max-width-breakpoint-desktop ) {
224+ @include linear-table ;
225+ }
226+ }
227+ }
228+ </style >
0 commit comments