@fiscozen/thumbnail 0.2.0
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/CHANGELOG.md +82 -0
- package/LICENSE +21 -0
- package/README.md +122 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +39 -0
- package/coverage/coverage-final.json +5 -0
- package/coverage/dist/src/FzThumbnail.vue.d.ts.html +172 -0
- package/coverage/dist/src/index.html +131 -0
- package/coverage/dist/src/types.d.ts.html +367 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +131 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/src/FzThumbnail.vue.html +619 -0
- package/coverage/src/index.html +131 -0
- package/coverage/src/types.ts.html +379 -0
- package/dist/index.d.ts +2 -0
- package/dist/src/FzThumbnail.vue.d.ts +29 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/types.d.ts +94 -0
- package/dist/thumbnail.css +2 -0
- package/dist/thumbnail.js +94 -0
- package/dist/thumbnail.umd.cjs +1 -0
- package/eslintrc.cjs +18 -0
- package/package.json +41 -0
- package/prettierrc.js +5 -0
- package/src/FzThumbnail.vue +178 -0
- package/src/__tests__/FzThumbnail.spec.ts +396 -0
- package/src/__tests__/__snapshots__/FzThumbnail.spec.ts.snap +47 -0
- package/src/index.ts +2 -0
- package/src/types.ts +98 -0
- package/tsconfig.json +4 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vite.config.ts +34 -0
- package/vitest.config.ts +26 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @fiscozen/thumbnail
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 976684e: `FzThumbnail` — a new package, and the first way the design system can show an
|
|
8
|
+
arbitrary image (LIB-2918).
|
|
9
|
+
|
|
10
|
+
`<img>` is in `forbiddenTags` at every atomic-design layer, and until now there
|
|
11
|
+
was nothing to compose in its place: the only three components that render one are
|
|
12
|
+
`FzAvatar` — round, with hardwired sizes, built for people — and
|
|
13
|
+
`FzRadioCard` / `FzCheckboxCard`, where the image is an illustration inside a
|
|
14
|
+
selectable card. So any surface that had to show a user-uploaded image had neither
|
|
15
|
+
a component nor a legal fallback. In the frontoffice customer chat that is a
|
|
16
|
+
divergence already in production: image attachments render as file cards,
|
|
17
|
+
indistinguishable from a document, and not even distinguishable iconographically
|
|
18
|
+
because the icon kit has no image, photo or camera glyph. `FzThumbnail` extracts
|
|
19
|
+
and generalises the card pattern (`imageUrl` + `imageAlt` + `object-cover`) rather
|
|
20
|
+
than inventing anything.
|
|
21
|
+
- **The caller imposes the dimensions**, so this does not repeat `FzAvatar`'s
|
|
22
|
+
mistake of owning a size — but they are **props** (`width`, `height`,
|
|
23
|
+
`aspectRatio`), not a class. The consuming apps forbid `class` outright at the
|
|
24
|
+
organism, template and page layers and `style` above an atom, so a class-only
|
|
25
|
+
sizing contract would make the component unusable exactly where a page needs it.
|
|
26
|
+
Deciding it inside the component is the only way that capability exists for
|
|
27
|
+
those layers — the same reasoning as `FzNavbar`'s `elevation`. A class from a
|
|
28
|
+
layer that permits one still works. Left unsized, the box takes its container's
|
|
29
|
+
width at the image's natural aspect ratio — visible, but a size nobody chose,
|
|
30
|
+
and the one case where a load failure moves the layout.
|
|
31
|
+
- **`alt` is required by the type**, with an explicit `alt=""` for decorative
|
|
32
|
+
images: a decision visible at the call site rather than an optional prop that
|
|
33
|
+
gets forgotten. A missing `alt` makes a screen reader announce the URL. The name
|
|
34
|
+
also survives a load failure: a native `<img>` whose URL 404s still exposes its
|
|
35
|
+
`alt` to assistive tech, so replacing the element with a placeholder re-applies
|
|
36
|
+
the name as `role="img"` + `aria-label` rather than dropping it — otherwise a
|
|
37
|
+
screen-reader user ends up worse off than a sighted one, who at least sees the
|
|
38
|
+
placeholder icon. A decorative `alt=""` and an empty `src` stay silent; neither
|
|
39
|
+
ever named anything.
|
|
40
|
+
- **An `overlay` slot** for an action — the chat's download button, the composer's
|
|
41
|
+
remove control. The layer is inert and hands pointer events back to its direct
|
|
42
|
+
children, so it does not swallow clicks meant for the thumbnail, and
|
|
43
|
+
`overlayPosition` pins the content to a corner without the call site writing a
|
|
44
|
+
class either.
|
|
45
|
+
- **A load error does not leave a hole in a sized box.** The placeholder —
|
|
46
|
+
`placeholderIcon` on a `grey-100` fill, `file` by default since the kit has no
|
|
47
|
+
image glyph — occupies the same box (an unsized box is only as tall as its
|
|
48
|
+
content, and a placeholder has no intrinsic ratio to supply that height with, so
|
|
49
|
+
there it collapses to the icon), and an `error` event carries the failing `src`
|
|
50
|
+
so a caller can fall back to something else. A new `src` clears the failure and
|
|
51
|
+
retries. `error`
|
|
52
|
+
reports a load failure and only that: an empty `src` shows the same placeholder
|
|
53
|
+
without firing anything, because the `<img>` never mounted, so a caller wanting
|
|
54
|
+
one "nothing is showing" signal checks `!src` alongside `@error`.
|
|
55
|
+
- **An optional scrim** (`grey-500` at 20%, overriding the design's
|
|
56
|
+
`rgba(74, 85, 101, 0.2)` onto the DS palette), drawn over the image only, so an
|
|
57
|
+
overlaid control stays legible on a light photo. Override the colour with
|
|
58
|
+
`--fz-thumbnail-scrim` and the 20% with `--fz-thumbnail-scrim-opacity`.
|
|
59
|
+
- **`imgProps` reaches the `<img>` itself** — `referrerpolicy`, `crossorigin`,
|
|
60
|
+
`decoding`, `fetchpriority`, `srcset`, `sizes`. The component has a single root
|
|
61
|
+
element, so a fallthrough attribute lands on the wrapping box, where
|
|
62
|
+
`referrerpolicy` does nothing; without this prop the image is simply out of
|
|
63
|
+
reach. Keeping `class` and listeners on the box is what a caller sizing or
|
|
64
|
+
clicking a thumbnail wants, so the two go to different places on purpose.
|
|
65
|
+
`src`, `alt`, `loading` and `onError` remain the component's own contract and are
|
|
66
|
+
`Omit`ted from the prop's type, so naming one is a compile error rather than a
|
|
67
|
+
silent no-op — `onError` in particular could not simply be documented as losing,
|
|
68
|
+
since Vue's `mergeProps` chains listeners instead of replacing them.
|
|
69
|
+
Use `:imgProps="{ referrerpolicy: 'no-referrer' }"` for a third-party host.
|
|
70
|
+
- `radius` defaults to `base` — 4px, the designs' value — and `loading` to `lazy`.
|
|
71
|
+
|
|
72
|
+
The scrim is declared in this package's stylesheet rather than as
|
|
73
|
+
`bg-grey-500/20`, and that is not a style preference: the design system's colours
|
|
74
|
+
resolve to `var(--grey-500, …)`, Tailwind cannot decompose a `var()` into
|
|
75
|
+
channels, and an opacity modifier on a token colour therefore generates **no rule
|
|
76
|
+
at all**. Verified against the DS preset with the Tailwind CLI. The 20% is an
|
|
77
|
+
`opacity` declaration rather than a baked `rgba()` for the same kind of reason: an
|
|
78
|
+
`rgba()` would freeze `--grey-500`'s fallback, leaving a retheming consumer with
|
|
79
|
+
the scrim as the one grey that did not move.
|
|
80
|
+
|
|
81
|
+
Unblocks LIB-2912 (`FzpChatAttachmentCard`), whose README documents the file-card
|
|
82
|
+
fallback as temporary; nothing else about that component has to change.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Fiscozen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @fiscozen/thumbnail
|
|
2
|
+
|
|
3
|
+
`FzThumbnail` — an image from a URL, cropped into a box the caller sizes.
|
|
4
|
+
|
|
5
|
+
The design system had no way to show an arbitrary image. `FzAvatar` is round and
|
|
6
|
+
owns its sizes; `FzRadioCard` and `FzCheckboxCard` render an illustration inside a
|
|
7
|
+
selectable card. This generalises that second pattern into a component of its own.
|
|
8
|
+
|
|
9
|
+
```vue
|
|
10
|
+
<FzThumbnail src="/receipt.jpg" alt="Scontrino di marzo" width="158px" height="108px" />
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## The caller owns the box
|
|
14
|
+
|
|
15
|
+
`FzThumbnail` hardwires no dimension — that is the point, and it is what keeps it
|
|
16
|
+
from repeating `FzAvatar`'s mistake. Give it:
|
|
17
|
+
|
|
18
|
+
- `width` **and** `height`, or
|
|
19
|
+
- one of them plus `aspectRatio`.
|
|
20
|
+
|
|
21
|
+
With no dimension at all the box takes its container's width and the image's
|
|
22
|
+
*natural* aspect ratio. That is visible, not broken — but it is a size neither
|
|
23
|
+
you nor the design chose, and it is the one case where a load failure moves the
|
|
24
|
+
layout (see below). Give it a dimension.
|
|
25
|
+
|
|
26
|
+
**They are props, not classes, on purpose.** The consuming apps forbid `class`
|
|
27
|
+
outright at the organism, template and page layers, and `style` anywhere above an
|
|
28
|
+
atom, so a class-only sizing contract would make the component unusable exactly
|
|
29
|
+
where a page needs it. Deciding it inside the component is the only way that
|
|
30
|
+
capability exists for those layers — the same reasoning as `FzNavbar`'s
|
|
31
|
+
`elevation`. A Tailwind class still works where the layer permits one; pick one
|
|
32
|
+
mechanism or the other rather than both.
|
|
33
|
+
|
|
34
|
+
## Overlaying an action
|
|
35
|
+
|
|
36
|
+
The `overlay` slot sits above the image in a layer that is itself inert, with
|
|
37
|
+
pointer events handed back to its direct children — so the layer does not swallow
|
|
38
|
+
clicks meant for the thumbnail. `overlayPosition` pins the content 8px in from a
|
|
39
|
+
corner, again as a prop rather than a class, so a page can place an action too.
|
|
40
|
+
|
|
41
|
+
```vue
|
|
42
|
+
<FzThumbnail :src="attachment.url" alt="" width="100%" height="168px" scrim bordered>
|
|
43
|
+
<template #overlay>
|
|
44
|
+
<FzIconButton
|
|
45
|
+
iconName="arrow-down-to-line"
|
|
46
|
+
ariaLabel="Scarica"
|
|
47
|
+
@click="download(attachment)"
|
|
48
|
+
/>
|
|
49
|
+
</template>
|
|
50
|
+
</FzThumbnail>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Content that needs finer placement can still position itself, from a layer that
|
|
54
|
+
permits a class.
|
|
55
|
+
|
|
56
|
+
`scrim` lays `grey-500` at 20% over the image, so an overlaid control stays
|
|
57
|
+
legible on a light photo. Override the colour with `--fz-thumbnail-scrim` and the
|
|
58
|
+
20% with `--fz-thumbnail-scrim-opacity` — the colour goes through `var(--grey-500)`
|
|
59
|
+
rather than a baked `rgba()`, so retheming the token moves the scrim with every
|
|
60
|
+
other grey.
|
|
61
|
+
|
|
62
|
+
## A broken URL does not leave a hole in a sized box
|
|
63
|
+
|
|
64
|
+
On a load error the image is replaced by a placeholder — `placeholderIcon` on a
|
|
65
|
+
`grey-100` fill — that occupies the same box, and an `error` event fires with the
|
|
66
|
+
failing `src` so the caller can fall back to something else entirely. Changing
|
|
67
|
+
`src` clears the failure and retries.
|
|
68
|
+
|
|
69
|
+
**That holds as long as the box has a size of its own.** An unsized box is only
|
|
70
|
+
as tall as its content, and the placeholder — unlike an image — has no natural
|
|
71
|
+
ratio to supply one, so it collapses to the icon:
|
|
72
|
+
|
|
73
|
+
| unsized box, 600px container, 400×300 image | |
|
|
74
|
+
|---|---|
|
|
75
|
+
| image loads | 600×450 |
|
|
76
|
+
| image fails | 600×20 |
|
|
77
|
+
|
|
78
|
+
Which is the second reason to pass a dimension. Give the box a `height` or an
|
|
79
|
+
`aspectRatio` and the placeholder fills exactly the space the image did.
|
|
80
|
+
|
|
81
|
+
`error` reports a *load failure* and nothing else. An empty `src` shows the same
|
|
82
|
+
placeholder but fires no event — the image never mounted, so there was nothing to
|
|
83
|
+
fail. If you want one signal for "no image is showing", check `!src` alongside
|
|
84
|
+
`@error`.
|
|
85
|
+
|
|
86
|
+
Note the icon kit has no image, photo or camera glyph, so the default is `file`.
|
|
87
|
+
|
|
88
|
+
## `alt` is required
|
|
89
|
+
|
|
90
|
+
Not an optional prop that gets forgotten. Pass an explicit `alt=""` for a
|
|
91
|
+
decorative image, which makes that a visible decision at the call site.
|
|
92
|
+
|
|
93
|
+
The name survives a load failure. A native `<img>` whose URL 404s still exposes
|
|
94
|
+
its `alt` to a screen reader — the element is there, only the pixels are missing.
|
|
95
|
+
This component replaces the `<img>` with a placeholder div, so it re-applies the
|
|
96
|
+
name as `role="img"` + `aria-label` rather than letting it vanish. A decorative
|
|
97
|
+
`alt=""` stays silent, and so does an empty `src`: neither named anything.
|
|
98
|
+
|
|
99
|
+
## Props
|
|
100
|
+
|
|
101
|
+
| Prop | Type | Default | |
|
|
102
|
+
|---|---|---|---|
|
|
103
|
+
| `src` | `string` | — | Image URL. |
|
|
104
|
+
| `alt` | `string` | — | Accessible name. Required; `""` for decorative. |
|
|
105
|
+
| `width` | `string` | — | Any CSS length. |
|
|
106
|
+
| `height` | `string` | — | Any CSS length. |
|
|
107
|
+
| `aspectRatio` | `string` | — | e.g. `'16 / 9'`. Ignored when both dimensions are set. |
|
|
108
|
+
| `radius` | `'none' \| 'sm' \| 'base' \| 'lg' \| 'xl'` | `'base'` | `base` is 4px, the design's value. |
|
|
109
|
+
| `bordered` | `boolean` | `false` | 1px `grey-100` border. |
|
|
110
|
+
| `scrim` | `boolean` | `false` | Translucent overlay over the image only. |
|
|
111
|
+
| `overlayPosition` | `'top-start' \| 'top-end' \| 'bottom-start' \| 'bottom-end' \| 'center'` | `'bottom-end'` | Where the `overlay` slot's content sits, 8px in. |
|
|
112
|
+
| `placeholderIcon` | `string` | `'file'` | Icon shown on load error. |
|
|
113
|
+
| `loading` | `'lazy' \| 'eager'` | `'lazy'` | Native loading hint. |
|
|
114
|
+
| `imgProps` | `Omit<ImgHTMLAttributes, 'src' \| 'alt' \| 'loading' \| 'onError'>` | — | Extra attributes for the `<img>` (`referrerpolicy`, `crossorigin`, `decoding`, `srcset`…). A fallthrough attribute lands on the root box instead, so this is the way to reach the image. The four keys the component owns are excluded from the type, so naming one is a compile error rather than a silent no-op. |
|
|
115
|
+
|
|
116
|
+
| Event | Payload | |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `error` | `string` | The `src` that failed to load. |
|
|
119
|
+
|
|
120
|
+
| Slot | |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `overlay` | Rendered above the image and the scrim, in an inert layer. |
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
body, html {
|
|
2
|
+
margin:0; padding: 0;
|
|
3
|
+
height: 100%;
|
|
4
|
+
}
|
|
5
|
+
body {
|
|
6
|
+
font-family: Helvetica Neue, Helvetica, Arial;
|
|
7
|
+
font-size: 14px;
|
|
8
|
+
color:#333;
|
|
9
|
+
}
|
|
10
|
+
.small { font-size: 12px; }
|
|
11
|
+
*, *:after, *:before {
|
|
12
|
+
-webkit-box-sizing:border-box;
|
|
13
|
+
-moz-box-sizing:border-box;
|
|
14
|
+
box-sizing:border-box;
|
|
15
|
+
}
|
|
16
|
+
h1 { font-size: 20px; margin: 0;}
|
|
17
|
+
h2 { font-size: 14px; }
|
|
18
|
+
pre {
|
|
19
|
+
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
-moz-tab-size: 2;
|
|
23
|
+
-o-tab-size: 2;
|
|
24
|
+
tab-size: 2;
|
|
25
|
+
}
|
|
26
|
+
a { color:#0074D9; text-decoration:none; }
|
|
27
|
+
a:hover { text-decoration:underline; }
|
|
28
|
+
.strong { font-weight: bold; }
|
|
29
|
+
.space-top1 { padding: 10px 0 0 0; }
|
|
30
|
+
.pad2y { padding: 20px 0; }
|
|
31
|
+
.pad1y { padding: 10px 0; }
|
|
32
|
+
.pad2x { padding: 0 20px; }
|
|
33
|
+
.pad2 { padding: 20px; }
|
|
34
|
+
.pad1 { padding: 10px; }
|
|
35
|
+
.space-left2 { padding-left:55px; }
|
|
36
|
+
.space-right2 { padding-right:20px; }
|
|
37
|
+
.center { text-align:center; }
|
|
38
|
+
.clearfix { display:block; }
|
|
39
|
+
.clearfix:after {
|
|
40
|
+
content:'';
|
|
41
|
+
display:block;
|
|
42
|
+
height:0;
|
|
43
|
+
clear:both;
|
|
44
|
+
visibility:hidden;
|
|
45
|
+
}
|
|
46
|
+
.fl { float: left; }
|
|
47
|
+
@media only screen and (max-width:640px) {
|
|
48
|
+
.col3 { width:100%; max-width:100%; }
|
|
49
|
+
.hide-mobile { display:none!important; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.quiet {
|
|
53
|
+
color: #7f7f7f;
|
|
54
|
+
color: rgba(0,0,0,0.5);
|
|
55
|
+
}
|
|
56
|
+
.quiet a { opacity: 0.7; }
|
|
57
|
+
|
|
58
|
+
.fraction {
|
|
59
|
+
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
|
60
|
+
font-size: 10px;
|
|
61
|
+
color: #555;
|
|
62
|
+
background: #E8E8E8;
|
|
63
|
+
padding: 4px 5px;
|
|
64
|
+
border-radius: 3px;
|
|
65
|
+
vertical-align: middle;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
div.path a:link, div.path a:visited { color: #333; }
|
|
69
|
+
table.coverage {
|
|
70
|
+
border-collapse: collapse;
|
|
71
|
+
margin: 10px 0 0 0;
|
|
72
|
+
padding: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
table.coverage td {
|
|
76
|
+
margin: 0;
|
|
77
|
+
padding: 0;
|
|
78
|
+
vertical-align: top;
|
|
79
|
+
}
|
|
80
|
+
table.coverage td.line-count {
|
|
81
|
+
text-align: right;
|
|
82
|
+
padding: 0 5px 0 20px;
|
|
83
|
+
}
|
|
84
|
+
table.coverage td.line-coverage {
|
|
85
|
+
text-align: right;
|
|
86
|
+
padding-right: 10px;
|
|
87
|
+
min-width:20px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
table.coverage td span.cline-any {
|
|
91
|
+
display: inline-block;
|
|
92
|
+
padding: 0 5px;
|
|
93
|
+
width: 100%;
|
|
94
|
+
}
|
|
95
|
+
.missing-if-branch {
|
|
96
|
+
display: inline-block;
|
|
97
|
+
margin-right: 5px;
|
|
98
|
+
border-radius: 3px;
|
|
99
|
+
position: relative;
|
|
100
|
+
padding: 0 4px;
|
|
101
|
+
background: #333;
|
|
102
|
+
color: yellow;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.skip-if-branch {
|
|
106
|
+
display: none;
|
|
107
|
+
margin-right: 10px;
|
|
108
|
+
position: relative;
|
|
109
|
+
padding: 0 4px;
|
|
110
|
+
background: #ccc;
|
|
111
|
+
color: white;
|
|
112
|
+
}
|
|
113
|
+
.missing-if-branch .typ, .skip-if-branch .typ {
|
|
114
|
+
color: inherit !important;
|
|
115
|
+
}
|
|
116
|
+
.coverage-summary {
|
|
117
|
+
border-collapse: collapse;
|
|
118
|
+
width: 100%;
|
|
119
|
+
}
|
|
120
|
+
.coverage-summary tr { border-bottom: 1px solid #bbb; }
|
|
121
|
+
.keyline-all { border: 1px solid #ddd; }
|
|
122
|
+
.coverage-summary td, .coverage-summary th { padding: 10px; }
|
|
123
|
+
.coverage-summary tbody { border: 1px solid #bbb; }
|
|
124
|
+
.coverage-summary td { border-right: 1px solid #bbb; }
|
|
125
|
+
.coverage-summary td:last-child { border-right: none; }
|
|
126
|
+
.coverage-summary th {
|
|
127
|
+
text-align: left;
|
|
128
|
+
font-weight: normal;
|
|
129
|
+
white-space: nowrap;
|
|
130
|
+
}
|
|
131
|
+
.coverage-summary th.file { border-right: none !important; }
|
|
132
|
+
.coverage-summary th.pct { }
|
|
133
|
+
.coverage-summary th.pic,
|
|
134
|
+
.coverage-summary th.abs,
|
|
135
|
+
.coverage-summary td.pct,
|
|
136
|
+
.coverage-summary td.abs { text-align: right; }
|
|
137
|
+
.coverage-summary td.file { white-space: nowrap; }
|
|
138
|
+
.coverage-summary td.pic { min-width: 120px !important; }
|
|
139
|
+
.coverage-summary tfoot td { }
|
|
140
|
+
|
|
141
|
+
.coverage-summary .sorter {
|
|
142
|
+
height: 10px;
|
|
143
|
+
width: 7px;
|
|
144
|
+
display: inline-block;
|
|
145
|
+
margin-left: 0.5em;
|
|
146
|
+
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
|
|
147
|
+
}
|
|
148
|
+
.coverage-summary .sorted .sorter {
|
|
149
|
+
background-position: 0 -20px;
|
|
150
|
+
}
|
|
151
|
+
.coverage-summary .sorted-desc .sorter {
|
|
152
|
+
background-position: 0 -10px;
|
|
153
|
+
}
|
|
154
|
+
.status-line { height: 10px; }
|
|
155
|
+
/* yellow */
|
|
156
|
+
.cbranch-no { background: yellow !important; color: #111; }
|
|
157
|
+
/* dark red */
|
|
158
|
+
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
|
|
159
|
+
.low .chart { border:1px solid #C21F39 }
|
|
160
|
+
.highlighted,
|
|
161
|
+
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
|
|
162
|
+
background: #C21F39 !important;
|
|
163
|
+
}
|
|
164
|
+
/* medium red */
|
|
165
|
+
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
|
|
166
|
+
/* light red */
|
|
167
|
+
.low, .cline-no { background:#FCE1E5 }
|
|
168
|
+
/* light green */
|
|
169
|
+
.high, .cline-yes { background:rgb(230,245,208) }
|
|
170
|
+
/* medium green */
|
|
171
|
+
.cstat-yes { background:rgb(161,215,106) }
|
|
172
|
+
/* dark green */
|
|
173
|
+
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
|
|
174
|
+
.high .chart { border:1px solid rgb(77,146,33) }
|
|
175
|
+
/* dark yellow (gold) */
|
|
176
|
+
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
|
|
177
|
+
.medium .chart { border:1px solid #f9cd0b; }
|
|
178
|
+
/* light yellow */
|
|
179
|
+
.medium { background: #fff4c2; }
|
|
180
|
+
|
|
181
|
+
.cstat-skip { background: #ddd; color: #111; }
|
|
182
|
+
.fstat-skip { background: #ddd; color: #111 !important; }
|
|
183
|
+
.cbranch-skip { background: #ddd !important; color: #111; }
|
|
184
|
+
|
|
185
|
+
span.cline-neutral { background: #eaeaea; }
|
|
186
|
+
|
|
187
|
+
.coverage-summary td.empty {
|
|
188
|
+
opacity: .5;
|
|
189
|
+
padding-top: 4px;
|
|
190
|
+
padding-bottom: 4px;
|
|
191
|
+
line-height: 1;
|
|
192
|
+
color: #888;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.cover-fill, .cover-empty {
|
|
196
|
+
display:inline-block;
|
|
197
|
+
height: 12px;
|
|
198
|
+
}
|
|
199
|
+
.chart {
|
|
200
|
+
line-height: 0;
|
|
201
|
+
}
|
|
202
|
+
.cover-empty {
|
|
203
|
+
background: white;
|
|
204
|
+
}
|
|
205
|
+
.cover-full {
|
|
206
|
+
border-right: none !important;
|
|
207
|
+
}
|
|
208
|
+
pre.prettyprint {
|
|
209
|
+
border: none !important;
|
|
210
|
+
padding: 0 !important;
|
|
211
|
+
margin: 0 !important;
|
|
212
|
+
}
|
|
213
|
+
.com { color: #999 !important; }
|
|
214
|
+
.ignore-none { color: #999; font-weight: normal; }
|
|
215
|
+
|
|
216
|
+
.wrapper {
|
|
217
|
+
min-height: 100%;
|
|
218
|
+
height: auto !important;
|
|
219
|
+
height: 100%;
|
|
220
|
+
margin: 0 auto -48px;
|
|
221
|
+
}
|
|
222
|
+
.footer, .push {
|
|
223
|
+
height: 48px;
|
|
224
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
var jumpToCode = (function init() {
|
|
3
|
+
// Classes of code we would like to highlight in the file view
|
|
4
|
+
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
|
|
5
|
+
|
|
6
|
+
// Elements to highlight in the file listing view
|
|
7
|
+
var fileListingElements = ['td.pct.low'];
|
|
8
|
+
|
|
9
|
+
// We don't want to select elements that are direct descendants of another match
|
|
10
|
+
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
|
|
11
|
+
|
|
12
|
+
// Selector that finds elements on the page to which we can jump
|
|
13
|
+
var selector =
|
|
14
|
+
fileListingElements.join(', ') +
|
|
15
|
+
', ' +
|
|
16
|
+
notSelector +
|
|
17
|
+
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
|
|
18
|
+
|
|
19
|
+
// The NodeList of matching elements
|
|
20
|
+
var missingCoverageElements = document.querySelectorAll(selector);
|
|
21
|
+
|
|
22
|
+
var currentIndex;
|
|
23
|
+
|
|
24
|
+
function toggleClass(index) {
|
|
25
|
+
missingCoverageElements
|
|
26
|
+
.item(currentIndex)
|
|
27
|
+
.classList.remove('highlighted');
|
|
28
|
+
missingCoverageElements.item(index).classList.add('highlighted');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function makeCurrent(index) {
|
|
32
|
+
toggleClass(index);
|
|
33
|
+
currentIndex = index;
|
|
34
|
+
missingCoverageElements.item(index).scrollIntoView({
|
|
35
|
+
behavior: 'smooth',
|
|
36
|
+
block: 'center',
|
|
37
|
+
inline: 'center'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function goToPrevious() {
|
|
42
|
+
var nextIndex = 0;
|
|
43
|
+
if (typeof currentIndex !== 'number' || currentIndex === 0) {
|
|
44
|
+
nextIndex = missingCoverageElements.length - 1;
|
|
45
|
+
} else if (missingCoverageElements.length > 1) {
|
|
46
|
+
nextIndex = currentIndex - 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
makeCurrent(nextIndex);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function goToNext() {
|
|
53
|
+
var nextIndex = 0;
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
typeof currentIndex === 'number' &&
|
|
57
|
+
currentIndex < missingCoverageElements.length - 1
|
|
58
|
+
) {
|
|
59
|
+
nextIndex = currentIndex + 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
makeCurrent(nextIndex);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return function jump(event) {
|
|
66
|
+
if (
|
|
67
|
+
document.getElementById('fileSearch') === document.activeElement &&
|
|
68
|
+
document.activeElement != null
|
|
69
|
+
) {
|
|
70
|
+
// if we're currently focused on the search input, we don't want to navigate
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
switch (event.which) {
|
|
75
|
+
case 78: // n
|
|
76
|
+
case 74: // j
|
|
77
|
+
goToNext();
|
|
78
|
+
break;
|
|
79
|
+
case 66: // b
|
|
80
|
+
case 75: // k
|
|
81
|
+
case 80: // p
|
|
82
|
+
goToPrevious();
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
})();
|
|
87
|
+
window.addEventListener('keydown', jumpToCode);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<coverage generated="1788859848137" clover="3.2.0">
|
|
3
|
+
<project timestamp="1788859848137" name="All files">
|
|
4
|
+
<metrics statements="15" coveredstatements="15" conditionals="21" coveredconditionals="21" methods="6" coveredmethods="6" elements="42" coveredelements="42" complexity="0" loc="15" ncloc="15" packages="2" files="4" classes="4"/>
|
|
5
|
+
<package name="dist.src">
|
|
6
|
+
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
|
|
7
|
+
<file name="FzThumbnail.vue.d.ts" path="/Users/fiscozen/Projects/design_system/packages/thumbnail/dist/src/FzThumbnail.vue.d.ts">
|
|
8
|
+
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
|
|
9
|
+
</file>
|
|
10
|
+
<file name="types.d.ts" path="/Users/fiscozen/Projects/design_system/packages/thumbnail/dist/src/types.d.ts">
|
|
11
|
+
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
|
|
12
|
+
</file>
|
|
13
|
+
</package>
|
|
14
|
+
<package name="src">
|
|
15
|
+
<metrics statements="15" coveredstatements="15" conditionals="21" coveredconditionals="21" methods="6" coveredmethods="6"/>
|
|
16
|
+
<file name="FzThumbnail.vue" path="/Users/fiscozen/Projects/design_system/packages/thumbnail/src/FzThumbnail.vue">
|
|
17
|
+
<metrics statements="15" coveredstatements="15" conditionals="21" coveredconditionals="21" methods="6" coveredmethods="6"/>
|
|
18
|
+
<line num="35" count="54" type="stmt"/>
|
|
19
|
+
<line num="44" count="54" type="stmt"/>
|
|
20
|
+
<line num="52" count="54" type="stmt"/>
|
|
21
|
+
<line num="56" count="54" type="stmt"/>
|
|
22
|
+
<line num="57" count="56" type="stmt"/>
|
|
23
|
+
<line num="59" count="2" type="stmt"/>
|
|
24
|
+
<line num="63" count="65" type="cond" truecount="2" falsecount="0"/>
|
|
25
|
+
<line num="72" count="54" type="cond" truecount="4" falsecount="0"/>
|
|
26
|
+
<line num="74" count="54" type="stmt"/>
|
|
27
|
+
<line num="82" count="54" type="stmt"/>
|
|
28
|
+
<line num="92" count="54" type="stmt"/>
|
|
29
|
+
<line num="98" count="54" type="stmt"/>
|
|
30
|
+
<line num="99" count="9" type="stmt"/>
|
|
31
|
+
<line num="100" count="9" type="stmt"/>
|
|
32
|
+
<line num="105" count="65" type="stmt"/>
|
|
33
|
+
</file>
|
|
34
|
+
<file name="types.ts" path="/Users/fiscozen/Projects/design_system/packages/thumbnail/src/types.ts">
|
|
35
|
+
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
|
|
36
|
+
</file>
|
|
37
|
+
</package>
|
|
38
|
+
</project>
|
|
39
|
+
</coverage>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{"/Users/fiscozen/Projects/design_system/packages/thumbnail/dist/src/FzThumbnail.vue.d.ts": {"path":"/Users/fiscozen/Projects/design_system/packages/thumbnail/dist/src/FzThumbnail.vue.d.ts","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
|
|
2
|
+
,"/Users/fiscozen/Projects/design_system/packages/thumbnail/dist/src/types.d.ts": {"path":"/Users/fiscozen/Projects/design_system/packages/thumbnail/dist/src/types.d.ts","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
|
|
3
|
+
,"/Users/fiscozen/Projects/design_system/packages/thumbnail/src/FzThumbnail.vue": {"path":"/Users/fiscozen/Projects/design_system/packages/thumbnail/src/FzThumbnail.vue","statementMap":{"0":{"start":{"line":35,"column":14},"end":{"line":35,"column":null}},"1":{"start":{"line":44,"column":13},"end":{"line":44,"column":null}},"2":{"start":{"line":52,"column":6},"end":{"line":52,"column":24}},"3":{"start":{"line":56,"column":0},"end":{"line":61,"column":null}},"4":{"start":{"line":57,"column":8},"end":{"line":57,"column":null}},"5":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"6":{"start":{"line":63,"column":6},"end":{"line":63,"column":61}},"7":{"start":{"line":63,"column":33},"end":{"line":63,"column":61}},"8":{"start":{"line":72,"column":6},"end":{"line":72,"column":91}},"9":{"start":{"line":72,"column":41},"end":{"line":72,"column":91}},"10":{"start":{"line":74,"column":57},"end":{"line":80,"column":null}},"11":{"start":{"line":82,"column":75},"end":{"line":88,"column":null}},"12":{"start":{"line":92,"column":6},"end":{"line":96,"column":2}},"13":{"start":{"line":92,"column":33},"end":{"line":96,"column":2}},"14":{"start":{"line":98,"column":6},"end":{"line":100,"column":null}},"15":{"start":{"line":99,"column":2},"end":{"line":99,"column":null}},"16":{"start":{"line":100,"column":2},"end":{"line":100,"column":null}},"17":{"start":{"line":105,"column":2},"end":{"line":154,"column":null}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":56,"column":0},"end":{"line":56,"column":null}},"loc":{"start":{"line":57,"column":8},"end":{"line":57,"column":null}},"line":57},"1":{"name":"(anonymous_1)","decl":{"start":{"line":57,"column":14},"end":{"line":57,"column":null}},"loc":{"start":{"line":58,"column":8},"end":{"line":61,"column":null}},"line":58},"2":{"name":"(anonymous_2)","decl":{"start":{"line":63,"column":18},"end":{"line":63,"column":33}},"loc":{"start":{"line":63,"column":33},"end":{"line":63,"column":61}},"line":63},"3":{"name":"(anonymous_3)","decl":{"start":{"line":72,"column":25},"end":{"line":72,"column":41}},"loc":{"start":{"line":72,"column":41},"end":{"line":72,"column":91}},"line":72},"4":{"name":"(anonymous_4)","decl":{"start":{"line":92,"column":17},"end":{"line":92,"column":33}},"loc":{"start":{"line":92,"column":33},"end":{"line":96,"column":2}},"line":92},"5":{"name":"(anonymous_5)","decl":{"start":{"line":98,"column":6},"end":{"line":98,"column":22}},"loc":{"start":{"line":98,"column":22},"end":{"line":100,"column":null}},"line":98}},"branchMap":{"0":{"loc":{"start":{"line":63,"column":33},"end":{"line":63,"column":61}},"type":"binary-expr","locations":[{"start":{"line":63,"column":33},"end":{"line":63,"column":48}},{"start":{"line":63,"column":48},"end":{"line":63,"column":61}}],"line":63},"1":{"loc":{"start":{"line":72,"column":41},"end":{"line":72,"column":91}},"type":"cond-expr","locations":[{"start":{"line":72,"column":69},"end":{"line":72,"column":81}},{"start":{"line":72,"column":81},"end":{"line":72,"column":91}}],"line":72},"2":{"loc":{"start":{"line":72,"column":41},"end":{"line":72,"column":69}},"type":"binary-expr","locations":[{"start":{"line":72,"column":41},"end":{"line":72,"column":57}},{"start":{"line":72,"column":57},"end":{"line":72,"column":69}}],"line":72},"3":{"loc":{"start":{"line":93,"column":6},"end":{"line":93,"column":47}},"type":"cond-expr","locations":[{"start":{"line":93,"column":20},"end":{"line":93,"column":42}},{"start":{"line":93,"column":45},"end":{"line":93,"column":47}}],"line":93},"4":{"loc":{"start":{"line":94,"column":6},"end":{"line":94,"column":50}},"type":"cond-expr","locations":[{"start":{"line":94,"column":21},"end":{"line":94,"column":45}},{"start":{"line":94,"column":48},"end":{"line":94,"column":50}}],"line":94},"5":{"loc":{"start":{"line":95,"column":6},"end":{"line":95,"column":null}},"type":"cond-expr","locations":[{"start":{"line":95,"column":26},"end":{"line":95,"column":60}},{"start":{"line":95,"column":63},"end":{"line":95,"column":null}}],"line":95},"6":{"loc":{"start":{"line":111,"column":12},"end":{"line":134,"column":72}},"type":"cond-expr","locations":[{"start":{"line":111,"column":12},"end":{"line":127,"column":null}},{"start":{"line":127,"column":4},"end":{"line":134,"column":72}}],"line":111},"7":{"loc":{"start":{"line":130,"column":13},"end":{"line":130,"column":null}},"type":"cond-expr","locations":[{"start":{"line":130,"column":29},"end":{"line":130,"column":40}},{"start":{"line":130,"column":40},"end":{"line":130,"column":null}}],"line":130},"8":{"loc":{"start":{"line":138,"column":12},"end":{"line":141,"column":null}},"type":"cond-expr","locations":[{"start":{"line":138,"column":21},"end":{"line":137,"column":null}},{"start":{"line":141,"column":6},"end":{"line":141,"column":null}}],"line":138},"9":{"loc":{"start":{"line":138,"column":12},"end":{"line":138,"column":null}},"type":"binary-expr","locations":[{"start":{"line":138,"column":12},"end":{"line":138,"column":21}},{"start":{"line":138,"column":21},"end":{"line":138,"column":null}}],"line":138},"10":{"loc":{"start":{"line":149,"column":12},"end":{"line":154,"column":null}},"type":"cond-expr","locations":[{"start":{"line":149,"column":19},"end":{"line":154,"column":null}}],"line":149}},"s":{"0":54,"1":54,"2":54,"3":54,"4":56,"5":2,"6":54,"7":65,"8":54,"9":15,"10":54,"11":54,"12":54,"13":54,"14":54,"15":9,"16":9,"17":65},"f":{"0":56,"1":2,"2":65,"3":15,"4":54,"5":9},"b":{"0":[65,59],"1":[8,7],"2":[15,9],"3":[3,51],"4":[3,51],"5":[1,53],"6":[50,15],"7":[8,7],"8":[3,62],"9":[65,4],"10":[10]},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
|
|
4
|
+
,"/Users/fiscozen/Projects/design_system/packages/thumbnail/src/types.ts": {"path":"/Users/fiscozen/Projects/design_system/packages/thumbnail/src/types.ts","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
|
|
5
|
+
}
|