diff --git a/package.json b/package.json index 154c1554a..b711782a2 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "pinia": "^2.0.26", "pretty-bytes": "^6.0.0", "qs": "^6.11.0", + "transliteration": "^2.3.5", "vue": "^3.2.45", "vue-grid-layout": "3.0.0-beta1", "vue-router": "^4.1.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 25d76f10e..b16c9149e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,6 +85,7 @@ importers: tailwindcss: ^3.2.4 tailwindcss-safe-area: ^0.2.2 tailwindcss-themer: ^2.0.2 + transliteration: ^2.3.5 typescript: ~4.7.4 unplugin-icons: ^0.14.14 vite: ^4.0.4 @@ -141,6 +142,7 @@ importers: pinia: 2.0.26_e7lp6ggkpgyi5vqd44m2kxvk6i pretty-bytes: 6.0.0 qs: 6.11.0 + transliteration: 2.3.5 vue: 3.2.45 vue-grid-layout: 3.0.0-beta1 vue-router: 4.1.6_vue@3.2.45 @@ -4267,7 +4269,6 @@ packages: /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true /ansi-regex/6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} @@ -4286,7 +4287,6 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - dev: true /ansi-styles/6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} @@ -4783,7 +4783,6 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true /clone/1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} @@ -4817,7 +4816,6 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - dev: true /color-name/1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} @@ -5358,7 +5356,6 @@ packages: /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true /emoji-regex/9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -5693,7 +5690,6 @@ packages: /escalade/3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} - dev: true /escape-html/1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} @@ -6296,7 +6292,6 @@ packages: /get-caller-file/2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - dev: true /get-func-name/2.0.0: resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} @@ -6802,7 +6797,6 @@ packages: /is-fullwidth-code-point/3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-fullwidth-code-point/4.0.0: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} @@ -8447,7 +8441,6 @@ packages: /require-directory/2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - dev: true /require-from-string/2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} @@ -8894,7 +8887,6 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true /string-width/5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} @@ -8954,7 +8946,6 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true /strip-ansi/7.0.1: resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} @@ -9264,6 +9255,14 @@ packages: punycode: 2.1.1 dev: true + /transliteration/2.3.5: + resolution: {integrity: sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + yargs: 17.5.1 + dev: false + /trim-newlines/3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} @@ -10267,7 +10266,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -10306,7 +10304,6 @@ packages: /y18n/5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - dev: true /yallist/2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} @@ -10345,7 +10342,6 @@ packages: /yargs-parser/21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - dev: true /yargs/15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} @@ -10388,7 +10384,6 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true /yauzl/2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} diff --git a/src/composables/use-slugify.ts b/src/composables/use-slugify.ts new file mode 100644 index 000000000..1a47228de --- /dev/null +++ b/src/composables/use-slugify.ts @@ -0,0 +1,26 @@ +import { slugify } from "transliteration"; +import { watch, type Ref } from "vue"; +export default function useSlugify( + source: Ref, + target: Ref, + auto: Ref +) { + watch( + () => source.value, + () => { + if (auto.value) { + handleGenerateSlug(); + } + } + ); + + const handleGenerateSlug = () => { + target.value = slugify(source.value, { + trim: true, + }); + }; + + return { + handleGenerateSlug, + }; +} diff --git a/src/formkit/inputs/category-select/CategorySelect.vue b/src/formkit/inputs/category-select/CategorySelect.vue index cd62d4cb7..e1baf5d09 100644 --- a/src/formkit/inputs/category-select/CategorySelect.vue +++ b/src/formkit/inputs/category-select/CategorySelect.vue @@ -13,6 +13,7 @@ import CategoryTag from "./components/CategoryTag.vue"; import SearchResultListItem from "./components/SearchResultListItem.vue"; import { apiClient } from "@/utils/api-client"; import { usePermission } from "@/utils/permission"; +import { slugify } from "transliteration"; const { currentUserHasPermission } = usePermission(); @@ -212,7 +213,7 @@ const handleCreateCategory = async () => { category: { spec: { displayName: text.value, - slug: text.value, + slug: slugify(text.value, { trim: true }), description: "", cover: "", template: "", diff --git a/src/formkit/inputs/tag-select/TagSelect.vue b/src/formkit/inputs/tag-select/TagSelect.vue index d6a5ee883..e62f38a18 100644 --- a/src/formkit/inputs/tag-select/TagSelect.vue +++ b/src/formkit/inputs/tag-select/TagSelect.vue @@ -12,6 +12,7 @@ import { import { onClickOutside } from "@vueuse/core"; import Fuse from "fuse.js"; import { usePermission } from "@/utils/permission"; +import { slugify } from "transliteration"; const { currentUserHasPermission } = usePermission(); @@ -196,7 +197,7 @@ const handleCreateTag = async () => { tag: { spec: { displayName: text.value, - slug: text.value, + slug: slugify(text.value, { trim: true }), color: "#ffffff", cover: "", }, diff --git a/src/modules/contents/pages/components/SinglePageSettingModal.vue b/src/modules/contents/pages/components/SinglePageSettingModal.vue index 0529e1c5c..67ce50c8d 100644 --- a/src/modules/contents/pages/components/SinglePageSettingModal.vue +++ b/src/modules/contents/pages/components/SinglePageSettingModal.vue @@ -1,5 +1,11 @@