feat: add dark mode with flowbite
This commit is contained in:
377
package-lock.json
generated
377
package-lock.json
generated
@ -19,6 +19,9 @@
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-plugin-svelte": "^3.0.0",
|
||||
"flowbite": "^3.1.2",
|
||||
"flowbite-svelte": "^0.48.4",
|
||||
"flowbite-svelte-icons": "^2.0.3",
|
||||
"globals": "^16.0.0",
|
||||
"js-confetti": "^0.12.0",
|
||||
"jsdom": "^26.0.0",
|
||||
@ -816,6 +819,34 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.6.9",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
|
||||
"integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.6.13",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz",
|
||||
"integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.6.0",
|
||||
"@floating-ui/utils": "^0.2.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
|
||||
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
@ -980,6 +1011,72 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
"version": "15.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz",
|
||||
"integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
"@types/resolve": "1.20.2",
|
||||
"deepmerge": "^4.2.2",
|
||||
"is-module": "^1.0.0",
|
||||
"resolve": "^1.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^2.78.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
|
||||
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.35.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz",
|
||||
@ -1704,6 +1801,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.26.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz",
|
||||
@ -2023,6 +2127,13 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@yr/monotone-cubic-spline": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
|
||||
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
@ -2099,6 +2210,22 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/apexcharts": {
|
||||
"version": "3.54.1",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.54.1.tgz",
|
||||
"integrity": "sha512-E4et0h/J1U3r3EwS/WlqJCQIbepKbp6wGUmaAwJOMjHUP4Ci0gxanLa7FR3okx6p9coi4st6J853/Cb1NP0vpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@yr/monotone-cubic-spline": "^1.0.3",
|
||||
"svg.draggable.js": "^2.2.2",
|
||||
"svg.easing.js": "^2.0.0",
|
||||
"svg.filter.js": "^2.0.2",
|
||||
"svg.pathmorphing.js": "^0.1.3",
|
||||
"svg.resize.js": "^1.4.3",
|
||||
"svg.select.js": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
@ -3048,6 +3175,70 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/flowbite": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-3.1.2.tgz",
|
||||
"integrity": "sha512-MkwSgbbybCYgMC+go6Da5idEKUFfMqc/AmSjm/2ZbdmvoKf5frLPq/eIhXc9P+rC8t9boZtUXzHDgt5whZ6A/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.3",
|
||||
"flowbite-datepicker": "^1.3.1",
|
||||
"mini-svg-data-uri": "^1.4.3",
|
||||
"postcss": "^8.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/flowbite-datepicker": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/flowbite-datepicker/-/flowbite-datepicker-1.3.2.tgz",
|
||||
"integrity": "sha512-6Nfm0MCVX3mpaR7YSCjmEO2GO8CDt6CX8ZpQnGdeu03WUCWtEPQ/uy0PUiNtIJjJZWnX0Cm3H55MOhbD1g+E/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"flowbite": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/flowbite-datepicker/node_modules/flowbite": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.5.2.tgz",
|
||||
"integrity": "sha512-kwFD3n8/YW4EG8GlY3Od9IoKND97kitO+/ejISHSqpn3vw2i5K/+ZI8Jm2V+KC4fGdnfi0XZ+TzYqQb4Q1LshA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.3",
|
||||
"flowbite-datepicker": "^1.3.0",
|
||||
"mini-svg-data-uri": "^1.4.3"
|
||||
}
|
||||
},
|
||||
"node_modules/flowbite-svelte": {
|
||||
"version": "0.48.4",
|
||||
"resolved": "https://registry.npmjs.org/flowbite-svelte/-/flowbite-svelte-0.48.4.tgz",
|
||||
"integrity": "sha512-ivlBxNi2u9+D/nFeHs+vLJU6nYjKq/ooAwdXPP3qIlEnUyIl/hVsH87JtVWwVEgF31NwwQcZeKFkWd8K5DWiGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"apexcharts": "^3.54.1",
|
||||
"flowbite": "^3.1.2",
|
||||
"tailwind-merge": "^3.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.55.1 || ^4.0.0 || ^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/flowbite-svelte-icons": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/flowbite-svelte-icons/-/flowbite-svelte-icons-2.0.3.tgz",
|
||||
"integrity": "sha512-itIRhTZx+I774iyIj41NJkVlnoQiZ6SF1itc4XFVo887lwu7xlg54ewUXxRmmJHA5vmqhimM49SxXmt7lOjOzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.0.0",
|
||||
"tailwind-merge": "^2.3.0 || ^3.0.0",
|
||||
"tailwindcss": "^3.4.3 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
||||
@ -3345,6 +3536,22 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
@ -3368,6 +3575,13 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-module": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
@ -3942,6 +4156,16 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mini-svg-data-uri": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
||||
"integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mini-svg-data-uri": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@ -4111,6 +4335,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
@ -4141,8 +4372,6 @@
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@ -4504,6 +4733,27 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
@ -4756,6 +5006,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "5.23.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.23.0.tgz",
|
||||
@ -4835,6 +5098,105 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/svg.draggable.js": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.easing.js": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
|
||||
"integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"svg.js": ">=2.3.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.filter.js": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
|
||||
"integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.js": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/svg.pathmorphing.js": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
|
||||
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.resize.js": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.6.5",
|
||||
"svg.select.js": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.resize.js/node_modules/svg.select.js": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.select.js": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.6.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-tree": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||
@ -4842,6 +5204,17 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz",
|
||||
"integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.13.tgz",
|
||||
|
11
package.json
11
package.json
@ -24,20 +24,23 @@
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/svelte": "^5.2.4",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-plugin-svelte": "^3.0.0",
|
||||
"eslint": "^9.18.0",
|
||||
"flowbite": "^3.1.2",
|
||||
"flowbite-svelte": "^0.48.4",
|
||||
"flowbite-svelte-icons": "^2.0.3",
|
||||
"globals": "^16.0.0",
|
||||
"js-confetti": "^0.12.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"prettier": "^3.4.2",
|
||||
"svelte-check": "^4.0.0",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"typescript": "^5.0.0",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "^6.0.0",
|
||||
"vitest": "^3.0.0"
|
||||
}
|
||||
|
34
src/app.css
34
src/app.css
@ -1 +1,35 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@plugin 'flowbite/plugin';
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
/* https://www.tints.dev */
|
||||
|
||||
--color-primary-50: #e0faff;
|
||||
--color-primary-100: #c7f6ff;
|
||||
--color-primary-200: #8aebff;
|
||||
--color-primary-300: #52e2ff;
|
||||
--color-primary-400: #14d8ff;
|
||||
--color-primary-500: #00b8db;
|
||||
--color-primary-600: #0091ad;
|
||||
--color-primary-700: #006f85;
|
||||
--color-primary-800: #004857;
|
||||
--color-primary-900: #00262e;
|
||||
--color-primary-950: #001114;
|
||||
|
||||
--color-secondary-50: #e0fff5;
|
||||
--color-secondary-100: #b3ffe5;
|
||||
--color-secondary-200: #1affb2;
|
||||
--color-secondary-300: #00eb9c;
|
||||
--color-secondary-400: #00d68f;
|
||||
--color-secondary-500: #00bc7d;
|
||||
--color-secondary-600: #00a870;
|
||||
--color-secondary-700: #009463;
|
||||
--color-secondary-800: #007a52;
|
||||
--color-secondary-900: #00573a;
|
||||
--color-secondary-950: #00422c;
|
||||
}
|
||||
|
||||
@source "../node_modules/flowbite-svelte/dist";
|
||||
|
@ -6,7 +6,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<body
|
||||
data-sveltekit-preload-data="hover"
|
||||
class="bg-gray-100 text-black dark:bg-gray-900 dark:text-white"
|
||||
>
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,17 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { Toggle } from 'flowbite-svelte';
|
||||
import JSConfetti from 'js-confetti';
|
||||
import Cell from './Cell.svelte';
|
||||
import ValueSelector from './ValueSelector.svelte';
|
||||
import { Board, Difficulty } from './core/board';
|
||||
import { onMount } from 'svelte';
|
||||
import Cell from './Cell.svelte';
|
||||
import GameCommands from './GameCommands.svelte';
|
||||
import SumIndicator from './SumIndicator.svelte';
|
||||
import Switch from './Switch.svelte';
|
||||
import ValueSelector from './ValueSelector.svelte';
|
||||
import { Board, Difficulty } from './core/board';
|
||||
|
||||
let selectedI: number | undefined = undefined;
|
||||
let selectedJ: number | undefined = undefined;
|
||||
let status: 'incomplete' | 'error' | 'won' = 'incomplete';
|
||||
let valueMode: 'Write' | 'Annotation' = 'Write';
|
||||
let isCompletion = true;
|
||||
let lastMode: Difficulty;
|
||||
|
||||
const onSelect = (i: number, j: number) => () => {
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
const onSetValue = (number: number) => {
|
||||
if (selectedI !== undefined && selectedJ !== undefined) {
|
||||
if (valueMode === 'Write') {
|
||||
if (isCompletion) {
|
||||
board.grid[selectedI][selectedJ] = number;
|
||||
if (board.isComplete()) {
|
||||
status = board.isValid() ? 'won' : 'error';
|
||||
@ -71,28 +71,33 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<GameCommands {lastMode} onClick={onGameCommand} />
|
||||
<p class="py-4 text-center">STATUS: {status}</p>
|
||||
<div class="text-black dark:text-white">
|
||||
<GameCommands {lastMode} onClick={onGameCommand} />
|
||||
<p class="py-4 text-center">STATUS: {status}</p>
|
||||
|
||||
<div class="mx-auto grid max-w-xl grid-cols-[1fr_1fr_1fr_auto] gap-4">
|
||||
{#each board.grid as row, i (row)}
|
||||
{#each row as cell, j}
|
||||
<Cell
|
||||
value={cell}
|
||||
annotations={board.annotations[i][j]}
|
||||
onclick={onSelect(i, j)}
|
||||
selected={selectedI === i && selectedJ === j}
|
||||
disabled={!board.lockedCells[i][j]}
|
||||
/>
|
||||
<div class="grid grid-cols-[1fr_1fr_1fr_auto] gap-4">
|
||||
{#each board.grid as row, i (row)}
|
||||
{#each row as cell, j}
|
||||
<Cell
|
||||
value={cell}
|
||||
annotations={board.annotations[i][j]}
|
||||
onclick={onSelect(i, j)}
|
||||
selected={selectedI === i && selectedJ === j}
|
||||
disabled={!board.lockedCells[i][j]}
|
||||
/>
|
||||
{/each}
|
||||
<SumIndicator value={board.horizontalSum[i]} />
|
||||
{/each}
|
||||
<SumIndicator value={board.horizontalSum[i]} />
|
||||
{/each}
|
||||
{#each board.verticalSum as s}
|
||||
<SumIndicator value={s} />
|
||||
{/each}
|
||||
</div>
|
||||
{#each board.verticalSum as s}
|
||||
<SumIndicator value={s} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center py-8">
|
||||
<Switch bind:value={valueMode} design="multi" options={['Annotation', 'Write']} />
|
||||
<div class="flex justify-center py-8">
|
||||
<Toggle color="secondary" bind:checked={isCompletion}>
|
||||
<svelte:fragment slot="offLabel">Annotation</svelte:fragment>
|
||||
Completion
|
||||
</Toggle>
|
||||
</div>
|
||||
<ValueSelector onclick={onSetValue} used={board.getPlacedNumbers()} />
|
||||
</div>
|
||||
<ValueSelector onclick={onSetValue} used={board.getPlacedNumbers()} />
|
||||
|
@ -15,8 +15,9 @@
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="aspect-square rounded-4xl border border-gray-200"
|
||||
class:bg-cyan-500={selected}
|
||||
class="aspect-square rounded-4xl border border-gray-200 {selected
|
||||
? 'bg-primary-500 dark:bg-primary-500'
|
||||
: ''} {disabled ? 'bg-gray-200 dark:bg-gray-400' : ''}"
|
||||
class:bg-gray-200={disabled}
|
||||
class:cursor-pointer={!disabled}
|
||||
{onclick}
|
||||
@ -29,7 +30,7 @@
|
||||
<div class="flex flex-wrap justify-center space-y-2 space-x-2 md:flex-col">
|
||||
{#each annotations as nb (nb)}
|
||||
<div
|
||||
class="flex h-9 w-9 items-center justify-center rounded-full bg-emerald-300 p-1"
|
||||
class="bg-secondary-300 flex h-9 w-9 items-center justify-center rounded-full p-1 text-black"
|
||||
class:bg-white={selected}
|
||||
>
|
||||
{nb}
|
||||
|
@ -5,8 +5,8 @@
|
||||
<div class="flex justify-center space-x-2">
|
||||
{#each ['reset', 'easy', 'medium', 'hard'] as mode (mode)}
|
||||
<button
|
||||
class="cursor-pointer rounded-full bg-cyan-500 px-4 py-2 text-sm text-white capitalize"
|
||||
class:bg-cyan-800={lastMode === mode}
|
||||
class="bg-primary-500 cursor-pointer rounded-full px-4 py-2 text-sm text-white capitalize"
|
||||
class:bg-primary-800={lastMode === mode}
|
||||
onclick={() => onClick(mode)}>{mode}</button
|
||||
>
|
||||
{/each}
|
||||
|
10
src/lib/Navbar.svelte
Normal file
10
src/lib/Navbar.svelte
Normal file
@ -0,0 +1,10 @@
|
||||
<script>
|
||||
import { DarkMode } from 'flowbite-svelte';
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<h1 class="mb-5 grow text-center text-5xl font-medium">Fubuki</h1>
|
||||
<div class="absolute top-0 right-0">
|
||||
<DarkMode size="lg" />
|
||||
</div>
|
||||
</nav>
|
@ -1,263 +0,0 @@
|
||||
<script lang="ts">
|
||||
// copy from https://svelte.dev/playground/d65a4e9f0ae74d1eb1b08d13e428af32?version=5.23.0
|
||||
|
||||
// based on suggestions from:
|
||||
// Inclusive Components by Heydon Pickering https://inclusive-components.design/toggle-button/
|
||||
// On Designing and Building Toggle Switches by Sara Soueidan https://www.sarasoueidan.com/blog/toggle-switch-design/
|
||||
// and this example by Scott O'hara https://codepen.io/scottohara/pen/zLZwNv
|
||||
|
||||
export let label = '';
|
||||
export let design = 'inner label';
|
||||
export let options: string[] = [];
|
||||
export let fontSize = 16;
|
||||
export let value = 'on';
|
||||
|
||||
let checked = true;
|
||||
|
||||
const uniqueID = Math.floor(Math.random() * 100);
|
||||
|
||||
function handleClick(event) {
|
||||
const target = event.target;
|
||||
|
||||
const state = target.getAttribute('aria-checked');
|
||||
|
||||
checked = state === 'true' ? false : true;
|
||||
|
||||
value = checked === true ? 'on' : 'off';
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if design == 'inner'}
|
||||
<div class="s s--inner">
|
||||
<span id={`switch-${uniqueID}`}>{label}</span>
|
||||
<button
|
||||
role="switch"
|
||||
aria-checked={checked}
|
||||
aria-labelledby={`switch-${uniqueID}`}
|
||||
on:click={handleClick}
|
||||
>
|
||||
<span>on</span>
|
||||
<span>off</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else if design == 'slider'}
|
||||
<div class="s s--slider" style="font-size:{fontSize}px">
|
||||
<span id={`switch-${uniqueID}`}>{label}</span>
|
||||
<button
|
||||
role="switch"
|
||||
aria-checked={checked}
|
||||
aria-labelledby={`switch-${uniqueID}`}
|
||||
on:click={handleClick}
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="s s--multi">
|
||||
<div
|
||||
role="radiogroup"
|
||||
class="group-container"
|
||||
aria-labelledby={`label-${uniqueID}`}
|
||||
style="font-size:{fontSize}px"
|
||||
id={`group-${uniqueID}`}
|
||||
>
|
||||
<div class="legend" id={`label-${uniqueID}`}>{label}</div>
|
||||
{#each options as option}
|
||||
<input type="radio" id={`${option}-${uniqueID}`} value={option} bind:group={value} />
|
||||
<label for={`${option}-${uniqueID}`}>
|
||||
{option}
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
@reference "tailwindcss";
|
||||
|
||||
:root {
|
||||
--accent-color: theme(colors.emerald.300);
|
||||
--gray: #ccc;
|
||||
}
|
||||
/* Inner Design Option */
|
||||
.s--inner button {
|
||||
padding: 0.5em;
|
||||
background-color: #fff;
|
||||
border: 1px solid var(--gray);
|
||||
}
|
||||
[role='switch'][aria-checked='true'] :first-child,
|
||||
[role='switch'][aria-checked='false'] :last-child {
|
||||
display: none;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.s--inner button span {
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
.s--inner button:focus {
|
||||
outline: var(--accent-color) solid 1px;
|
||||
}
|
||||
|
||||
/* Slider Design Option */
|
||||
|
||||
.s--slider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.s--slider button {
|
||||
width: 3em;
|
||||
height: 1.6em;
|
||||
position: relative;
|
||||
margin: 0 0 0 0.5em;
|
||||
background: var(--gray);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.s--slider button::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
background: #fff;
|
||||
top: 0.13em;
|
||||
right: 1.5em;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.s--slider button[aria-checked='true'] {
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.s--slider button[aria-checked='true']::before {
|
||||
transform: translateX(1.3em);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.s--slider button:focus {
|
||||
box-shadow: 0 0px 0px 1px var(--accent-color);
|
||||
}
|
||||
|
||||
/* Multi Design Option */
|
||||
|
||||
/* Based on suggestions from Sara Soueidan https://www.sarasoueidan.com/blog/toggle-switch-design/
|
||||
and this example from Scott O'hara https://codepen.io/scottohara/pen/zLZwNv */
|
||||
|
||||
.s--multi .group-container {
|
||||
border: none;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* .s--multi legend {
|
||||
font-size: 2px;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
} */
|
||||
|
||||
.s--multi label {
|
||||
display: inline-block;
|
||||
line-height: 1.6;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.s--multi input {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.s--multi label:first-of-type {
|
||||
padding-right: 5em;
|
||||
}
|
||||
|
||||
.s--multi label:last-child {
|
||||
margin-left: -5em;
|
||||
padding-left: 5em;
|
||||
}
|
||||
|
||||
.s--multi:focus-within label:first-of-type:after {
|
||||
box-shadow: 0 0px 8px var(--accent-color);
|
||||
border-radius: 1.5em;
|
||||
}
|
||||
|
||||
/* making the switch UI. */
|
||||
.s--multi label:first-of-type:before,
|
||||
.s--multi label:first-of-type:after {
|
||||
content: '';
|
||||
height: 1.25em;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.s--multi label:first-of-type:before {
|
||||
border-radius: 100%;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
background: #fff;
|
||||
top: 0.2em;
|
||||
right: 1.2em;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.s--multi label:first-of-type:after {
|
||||
background: var(--accent-color);
|
||||
border-radius: 1em;
|
||||
margin: 0 1em;
|
||||
transition: background 0.2s ease-in-out;
|
||||
width: 3em;
|
||||
height: 1.6em;
|
||||
}
|
||||
|
||||
.s--multi input:first-of-type:checked ~ label:first-of-type:after {
|
||||
background: var(--gray);
|
||||
}
|
||||
|
||||
.s--multi input:first-of-type:checked ~ label:first-of-type:before {
|
||||
transform: translateX(-1.4em);
|
||||
}
|
||||
|
||||
.s--multi input:last-of-type:checked ~ label:last-of-type {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.s--multi input:focus {
|
||||
box-shadow: 0 0px 8px var(--accent-color);
|
||||
border-radius: 1.5em;
|
||||
}
|
||||
|
||||
/* gravy */
|
||||
|
||||
/* Inner Design Option */
|
||||
[role='switch'][aria-checked='true'] :first-child,
|
||||
[role='switch'][aria-checked='false'] :last-child {
|
||||
border-radius: 0.25em;
|
||||
background: var(--accent-color);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.s--inner button:focus {
|
||||
box-shadow: 0 0px 8px var(--accent-color);
|
||||
border-radius: 0.1em;
|
||||
}
|
||||
|
||||
/* Slider Design Option */
|
||||
.s--slider button {
|
||||
border-radius: 1.5em;
|
||||
}
|
||||
|
||||
.s--slider button::before {
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.s--slider button:focus {
|
||||
box-shadow: 0 0px 8px var(--accent-color);
|
||||
border-radius: 1.5em;
|
||||
}
|
||||
</style>
|
@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { TrashBinOutline } from 'flowbite-svelte-icons';
|
||||
const { onclick, used } = $props();
|
||||
|
||||
const numbers = Array.from({ length: 9 }, (_, i) => i + 1);
|
||||
@ -7,7 +8,7 @@
|
||||
<div class="flex flex-wrap justify-center space-x-2">
|
||||
{#each numbers as number (number)}
|
||||
<button
|
||||
class="mr-1 cursor-pointer rounded-full bg-emerald-300 px-4 py-2 disabled:bg-gray-200"
|
||||
class="bg-secondary-400 mr-1 cursor-pointer rounded-full px-4 py-2 text-black disabled:bg-gray-200"
|
||||
onclick={() => onclick(number)}
|
||||
disabled={used.includes(number)}
|
||||
>
|
||||
@ -15,9 +16,9 @@
|
||||
</button>
|
||||
{/each}
|
||||
<button
|
||||
class="mr-1 cursor-pointer rounded-full bg-emerald-300 px-4 py-2 disabled:bg-gray-200"
|
||||
class="bg-secondary-400 mr-1 cursor-pointer rounded-full px-4 py-2 text-black disabled:bg-gray-200"
|
||||
onclick={() => onclick()}
|
||||
>
|
||||
🗑️
|
||||
<TrashBinOutline strokeWidth="1.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1,13 +1,17 @@
|
||||
<script>
|
||||
import Board from '$lib/Board.svelte';
|
||||
import Navbar from '$lib/Navbar.svelte';
|
||||
import { Card } from 'flowbite-svelte';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>FUBIKI</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1 class="mb-5 text-center text-6xl font-medium">Fubuki</h1>
|
||||
<Navbar />
|
||||
|
||||
<div class="mx-2">
|
||||
<Board />
|
||||
<div class="flex justify-center">
|
||||
<Card size="md" padding="xl">
|
||||
<Board />
|
||||
</Card>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user