Skip to content

Commit 82dd527

Browse files
committed
Implement multiple services support
1 parent ed32b1d commit 82dd527

27 files changed

+474
-328
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
Invoice generator for Google Chrome and Microsoft Edge.
66

7-
[![Screen shot 01][screenshot-01-url]][web-store-url]
8-
[![Screen shot 02][screenshot-02-url]][web-store-url]
7+
[![Screenshot 01][screenshot-01-url]][web-store-url]
8+
[![Screenshot 02][screenshot-02-url]][web-store-url]
9+
[![Screenshot 03][screenshot-03-url]][web-store-url]
910

1011
## Installation
1112

@@ -45,13 +46,14 @@ yarn dev
4546
- [x] Export/import options
4647
- [x] CI/CD
4748
- [ ] Tests
48-
- [ ] Multiple services
49+
- [x] Multiple services
4950
- [ ] Invoice reminder
5051
- [ ] Invoice expression
5152

5253
[web-store-url]: https://chrome.google.com/webstore/detail/invoice-generator/obdabdocagpfclncklefebhhgggkbbnk 'Invoice generator Web Store'
53-
[screenshot-01-url]: https://github.com/risoflora/invoice-generator/raw/master/contrib/screen-shot-1.png 'Invoice generator'
54-
[screenshot-02-url]: https://github.com/risoflora/invoice-generator/raw/master/contrib/screen-shot-2.png 'Invoice generator options'
54+
[screenshot-01-url]: https://github.com/risoflora/invoice-generator/raw/master/contrib/screenshot-01.png 'Invoice generator'
55+
[screenshot-02-url]: https://github.com/risoflora/invoice-generator/raw/master/contrib/screenshot-02.png 'Invoice generator items'
56+
[screenshot-03-url]: https://github.com/risoflora/invoice-generator/raw/master/contrib/screenshot-03.png 'Invoice generator options'
5557
[invoice-generator-logo-url]: https://github.com/risoflora/invoice-generator/raw/master/contrib/logo.png 'Invoice generator logo'
5658
[bootstrap-url]: https://github.com/twbs/bootstrap 'Bootstrap repository'
5759
[jspdf-url]: https://github.com/parallax/jsPDF 'jsPDF repository'

contrib/screen-shot-1.png

-203 KB
Binary file not shown.

contrib/screen-shot-2.png

-18.9 KB
Binary file not shown.

contrib/screenshot-01.png

230 KB
Loading

contrib/screenshot-02.png

235 KB
Loading

contrib/screenshot-03.png

61.8 KB
Loading

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
"name": "invoice-generator",
33
"description": "Invoice generator",
44
"private": true,
5-
"version": "2.0.0",
5+
"version": "3.0.0",
66
"scripts": {
77
"dev": "tsc && vite build --watch",
88
"build": "tsc && vite build"
99
},
1010
"dependencies": {
1111
"bootstrap": "^5.1.3",
1212
"bootstrap-icons": "^1.8.3",
13+
"deepmerge-ts": "^4.2.1",
1314
"jspdf": "^2.5.1",
1415
"react": "^18.2.0",
1516
"react-datepicker": "^4.8.0",

src/components/Accordion.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { FunctionComponent } from 'react';
2+
3+
const Accordion: FunctionComponent<JSX.IntrinsicElements['div']> = ({ className, children, ...props }) => (
4+
<div className={`accordion${className ? ` ${className}` : ''}`} {...props}>
5+
{children}
6+
</div>
7+
);
8+
9+
export default Accordion;

src/components/AccordionItem.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.accordion-button:focus {
2+
box-shadow: none !important;
3+
}
4+
5+
.accordion-button:not(.collapsed) {
6+
color: transparent !important;
7+
background-color: transparent !important;
8+
box-shadow: none !important;
9+
}
10+
11+
.accordion-button:not(.collapsed):after {
12+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") !important;
13+
}

src/components/AccordionItem.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { FunctionComponent, useRef, useState } from 'react';
2+
3+
import { uuid } from '../core/utils';
4+
5+
import './AccordionItem.scss';
6+
7+
type Props = {
8+
ownerSelector: string;
9+
title: string;
10+
titleClassName?: string;
11+
bodyClassName?: string;
12+
expanded?: boolean;
13+
};
14+
15+
const AccordionItem: FunctionComponent<Props & JSX.IntrinsicElements['div']> = ({
16+
className,
17+
title,
18+
titleClassName,
19+
bodyClassName,
20+
expanded,
21+
ownerSelector,
22+
children,
23+
...props
24+
}) => {
25+
const elementRef = useRef<HTMLDivElement>(null);
26+
const id = uuid();
27+
const headingTag = `heading-${id}`;
28+
const collapseTag = `collapse-${id}`;
29+
const owner = window.document.querySelector(ownerSelector);
30+
const [isExpanded, setIsExpanded] = useState(expanded);
31+
32+
const isShown = () => !!elementRef?.current?.classList.contains('show');
33+
34+
if (owner) {
35+
owner.addEventListener('shown.bs.collapse', () => setIsExpanded(isShown()));
36+
owner.addEventListener('hidden.bs.collapse', () => setIsExpanded(isShown()));
37+
}
38+
39+
return (
40+
<div className={`accordion-item${className ? ` ${className}` : ''}`} {...props}>
41+
<h2 className="accordion-header" id={headingTag}>
42+
<button
43+
className={`accordion-button${isExpanded ? '' : ' collapsed'}${titleClassName ? ` ${titleClassName}` : ''}`}
44+
type="button"
45+
data-bs-toggle="collapse"
46+
data-bs-target={`#${collapseTag}`}
47+
aria-expanded={isExpanded ? 'true' : 'false'}
48+
aria-controls={collapseTag}
49+
>
50+
{title && <strong className="text-black-50">{title}</strong>}
51+
</button>
52+
</h2>
53+
<div
54+
ref={elementRef}
55+
id={collapseTag}
56+
className={`accordion-collapse collapse${isExpanded ? ' show' : ''}`}
57+
aria-labelledby={headingTag}
58+
data-bs-parent={ownerSelector}
59+
>
60+
<div className={`accordion-body px-1 py-2${bodyClassName ? ` ${bodyClassName}` : ''}`}>{children}</div>
61+
</div>
62+
</div>
63+
);
64+
};
65+
66+
export default AccordionItem;

0 commit comments

Comments
 (0)