so://phie

figure

The vanilla alternative for writing JSX-based React applications
git clone git://soophie.de/figure

blob: 0c71738c30f1d02b7106ee1f0e9ef991012c31d8 (3122 bytes)[doc][blame][raw]

1<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter&family=Noto+Sans">

2

3<div id="app"></div>

4

5<style>

6

7 * {

8 font-family: 'Noto Sans', sans-serif;

9 }

10

11 body {

12 margin: 0;

13 }

14

15</style>

16

17<script type="module">

18

19 import {useState, createElement} from 'https://cdn.skypack.dev/react';

20 import {render} from 'https://cdn.skypack.dev/react-dom';

21 import {createTlx, css} from './lib.js';

22

23 const tlx = createTlx(createElement);

24

25 const Button = (props, $slot) => {

26 const styles = {

27 ...css`

28 min-width: 40px;

29 height: 40px;

30 border: none;

31 outline: none;

32 border: 1px solid #a3a3a3;

33 background-color: ${props.disabled ? '#f5f5f5' : '#e5e5e5'};

34 cursor: ${props.disabled ? 'default' : 'pointer'};

35 font-size: 1rem;

36 `,

37 ...props.styles

38 };

39 return tlx`

40 <button

41 disabled="${props.disabled}"

42 style="${styles}"

43 @click="${() => props.onClick()}"

44 >

45 ${$slot}

46 </button>

47 `;

48 }

49

50 const Counter = (props) => {

51 const [defaultValue, setDefaultValue] = useState(0);

52 const { min = 0, max = 999, value = defaultValue, setValue = setDefaultValue } = props ?? {};

53 const validate = (value) => {

54 if(/^\d+$/.test(value) && parseInt(value) <= max) {

55 setValue(parseInt(value));

56 }

57 }

58 return tlx`

59 <div style="display: inline-flex; width: 100%; border: 1px solid #a3a3a3; border-radius: 4px; overflow: hidden; height: 40px; box-sizing: border-box;">

60 ${Button({

61 onClick: () => setValue(value - 1),

62 styles: css`

63 font-size: 1.2rem;

64 border-width: 0px 1px 0px 0px;

65 `,

66 disabled: value <= min

67 }, '-')}

68 <input type="text" @input="${(event) => validate(event.target.value)}" value="${value}" style="font-size: 1rem; text-align: center; border: none; flex: 1; min-width: 0; height: 40px; outline: none;">

69 ${Button({

70 onClick: () => setValue(value + 1),

71 styles: css`

72 font-size: 1.2rem;

73 border-width: 0px 0px 0px 1px;

74 `,

75 disabled: value >= max

76 }, '+')}

77 </div>

78 `;

79 }

80

81 const App = () => {

82 const [quantity, setQuantity] = useState(1);

83 const isDisabled = quantity <= 0;

84 return tlx`

85 <div style="margin: 100px; display: flex; width: 150px; flex-direction: column; gap: 15px; align-items: flex-start;">

86 ${Counter({ value: quantity, setValue: setQuantity })}

87 ${Button({

88 styles: css`

89 background-color: ${isDisabled ? '#f5f5f5' : '#f97316'};

90 border-color: ${isDisabled ? '#d4d4d4' : '#c2410c'};

91 color: ${isDisabled ? '#a3a3a3' : '#ffffff'};

92 border-radius: 4px;

93 width: 100%;

94 `,

95 onClick: () => alert(`Added ${quantity} ${quantity > 1 ? 'items' : 'item'} to the cart`),

96 disabled: isDisabled

97 }, 'Add to cart')}

98 </div>

99 `;

100 }

101

102 const app = document.querySelector('#app');

103 render(createElement(App), app);

104

105</script>