@codav/slug-generator 1.0.2 → 1.0.3

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/dist/index.d.mts CHANGED
@@ -16,6 +16,6 @@ type SlugGeneratorProps = {
16
16
  transliteratePersian?: boolean;
17
17
  };
18
18
 
19
- declare function SlugGenerator({ title, value, onChange, label, placeholder, buttonText, disabled, className, icon, dir, generateOnTitleChange, transliteratePersian, }: SlugGeneratorProps): react.JSX.Element;
19
+ declare function SlugGenerator({ title, value, onChange, label, placeholder, buttonText, disabled, className, icon, dir, transliteratePersian, }: SlugGeneratorProps): react.JSX.Element;
20
20
 
21
21
  export { SlugGenerator, type SlugGeneratorProps };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,6 @@ type SlugGeneratorProps = {
16
16
  transliteratePersian?: boolean;
17
17
  };
18
18
 
19
- declare function SlugGenerator({ title, value, onChange, label, placeholder, buttonText, disabled, className, icon, dir, generateOnTitleChange, transliteratePersian, }: SlugGeneratorProps): react.JSX.Element;
19
+ declare function SlugGenerator({ title, value, onChange, label, placeholder, buttonText, disabled, className, icon, dir, transliteratePersian, }: SlugGeneratorProps): react.JSX.Element;
20
20
 
21
21
  export { SlugGenerator, type SlugGeneratorProps };
package/dist/index.js CHANGED
@@ -83,12 +83,12 @@ var ARABIC_DIACRITICS = /[\u064B-\u065F\u0670]/g;
83
83
  function transliterate(text) {
84
84
  return text.normalize("NFKC").replace(ARABIC_DIACRITICS, "").split("").map((char) => PERSIAN_MAP[char] ?? char).join("");
85
85
  }
86
- function generateSlug(title, transliteratePersian) {
86
+ function createSlug(title, transliteratePersian = true) {
87
87
  let source = title.normalize("NFKC").replace(ARABIC_DIACRITICS, "");
88
88
  if (transliteratePersian) {
89
89
  source = transliterate(source);
90
90
  }
91
- return source.toLowerCase().trim().replace(/['’`"]/g, "").replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
91
+ return source.toLowerCase().trim().replace(/['’`"]/g, "").replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
92
92
  }
93
93
  function SlugGenerator({
94
94
  title,
@@ -98,44 +98,21 @@ function SlugGenerator({
98
98
  placeholder = "your-slug",
99
99
  buttonText = "Generate",
100
100
  disabled = false,
101
- className = "",
101
+ className,
102
102
  icon,
103
103
  dir = "ltr",
104
- generateOnTitleChange = false,
105
104
  transliteratePersian = true
106
105
  }) {
107
- const createSlug = () => {
108
- onChange(generateSlug(title, transliteratePersian));
106
+ const handleGenerate = () => {
107
+ if (!title.trim() || disabled) return;
108
+ onChange(createSlug(title, transliteratePersian));
109
109
  };
110
110
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_clsx.default)("w-full space-y-1.5", className), children: [
111
- label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-3", children: [
112
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "flex items-center gap-2 text-sm font-medium text-(--color-text-primary)", children: [
113
- icon,
114
- label
115
- ] }),
116
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
117
- "button",
118
- {
119
- type: "button",
120
- onClick: createSlug,
121
- disabled: disabled || !title.trim(),
122
- className: (0, import_clsx.default)(
123
- "inline-flex items-center gap-1.5 rounded-lg px-3 py-2 text-xs font-medium",
124
- "text-white transition-all duration-200",
125
- "bg-[var(--color-primary)]",
126
- "hover:bg-[var(--color-primary-soft)]",
127
- "focus:outline-none focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_15%,transparent)]",
128
- "disabled:cursor-not-allowed disabled:opacity-50"
129
- ),
130
- children: [
131
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.WandSparkles, { className: "size-4" }),
132
- buttonText
133
- ]
134
- }
135
- )
111
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "flex items-center gap-2 text-sm font-medium text-(--color-text-primary)", children: [
112
+ icon,
113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label })
136
114
  ] }),
137
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative group", children: [
138
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pointer-events-none absolute -inset-[1px] rounded-lg opacity-0 transition group-focus-within:opacity-100" }),
115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative", children: [
139
116
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
140
117
  "input",
141
118
  {
@@ -147,13 +124,16 @@ function SlugGenerator({
147
124
  onKeyDown: (event) => {
148
125
  if (event.key === "Enter") {
149
126
  event.preventDefault();
150
- createSlug();
127
+ handleGenerate();
151
128
  }
152
129
  },
153
130
  className: (0, import_clsx.default)(
154
- "relative w-full rounded-lg border border-(--color-border) bg-(--color-bg-main)",
155
- "px-4 py-3 pe-32 text-sm text-(--color-text-primary)",
156
- "outline-none transition-all placeholder:text-(--color-text-secondary)/70",
131
+ "w-full rounded-lg border border-(--color-border)",
132
+ "bg-(--color-bg-main)",
133
+ "py-3 pe-4 ps-32",
134
+ "text-sm text-(--color-text-primary)",
135
+ "outline-none transition-all",
136
+ "placeholder:text-(--color-text-secondary)/70",
157
137
  "hover:border-[color-mix(in_oklab,var(--color-primary)_22%,var(--color-border))]",
158
138
  "focus:border-[color-mix(in_oklab,var(--color-primary)_45%,var(--color-border))]",
159
139
  "focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_12%,transparent)]",
@@ -162,41 +142,27 @@ function SlugGenerator({
162
142
  )
163
143
  }
164
144
  ),
165
- !label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
166
- "button",
167
- {
168
- type: "button",
169
- onClick: createSlug,
170
- disabled: disabled || !title.trim(),
171
- className: (0, import_clsx.default)(
172
- "absolute inset-y-1.5 end-1.5 inline-flex items-center gap-1.5",
173
- "rounded-md px-3 text-xs font-medium text-white",
174
- "bg-[var(--color-primary)] transition-all duration-200",
175
- "hover:bg-[var(--color-primary-soft)]",
176
- "disabled:cursor-not-allowed disabled:opacity-50"
177
- ),
178
- children: [
179
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.WandSparkles, { className: "size-4" }),
180
- buttonText
181
- ]
182
- }
183
- ),
184
- label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
185
146
  "button",
186
147
  {
187
148
  type: "button",
188
- onClick: createSlug,
149
+ onClick: handleGenerate,
189
150
  disabled: disabled || !title.trim(),
190
151
  className: (0, import_clsx.default)(
191
- "absolute inset-y-1.5 end-1.5 inline-flex items-center gap-1.5",
192
- "rounded-md px-3 text-xs font-medium text-white",
193
- "bg-[var(--color-primary)] transition-all duration-200",
194
- "hover:bg-[var(--color-primary-soft)]",
152
+ "absolute inset-y-1.5 start-1.5",
153
+ "inline-flex items-center justify-center gap-1.5",
154
+ "rounded-md px-3",
155
+ "text-xs font-medium text-white",
156
+ "bg-(--color-primary)",
157
+ "transition-all duration-200",
158
+ "hover:bg-(--color-primary-soft)",
159
+ "focus:outline-none",
160
+ "focus:ring-2 focus:ring-(--color-primary)/30",
195
161
  "disabled:cursor-not-allowed disabled:opacity-50"
196
162
  ),
197
163
  children: [
198
164
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.WandSparkles, { className: "size-4" }),
199
- buttonText
165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: buttonText })
200
166
  ]
201
167
  }
202
168
  )
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/SlugGenerator.tsx"],"sourcesContent":["export { default as SlugGenerator } from \"./SlugGenerator\";\r\nexport type { SlugGeneratorProps } from \"./types\";","\"use client\";\r\n\r\nimport { WandSparkles } from \"lucide-react\";\r\nimport clsx from \"clsx\";\r\nimport type { SlugGeneratorProps } from \"./types\";\r\n\r\nconst PERSIAN_MAP: Record<string, string> = {\r\n ا: \"a\",\r\n آ: \"a\",\r\n ب: \"b\",\r\n پ: \"p\",\r\n ت: \"t\",\r\n ث: \"s\",\r\n ج: \"j\",\r\n چ: \"ch\",\r\n ح: \"h\",\r\n خ: \"kh\",\r\n د: \"d\",\r\n ذ: \"z\",\r\n ر: \"r\",\r\n ز: \"z\",\r\n ژ: \"zh\",\r\n س: \"s\",\r\n ش: \"sh\",\r\n ص: \"s\",\r\n ض: \"z\",\r\n ط: \"t\",\r\n ظ: \"z\",\r\n ع: \"a\",\r\n غ: \"gh\",\r\n ف: \"f\",\r\n ق: \"gh\",\r\n ک: \"k\",\r\n گ: \"g\",\r\n ل: \"l\",\r\n م: \"m\",\r\n ن: \"n\",\r\n و: \"v\",\r\n ه: \"h\",\r\n ی: \"y\",\r\n ئ: \"y\",\r\n ء: \"a\",\r\n ۀ: \"h\",\r\n ة: \"h\",\r\n ي: \"y\",\r\n ك: \"k\",\r\n};\r\n\r\nconst ARABIC_DIACRITICS = /[\\u064B-\\u065F\\u0670]/g;\r\n\r\nfunction transliterate(text: string) {\r\n return text\r\n .normalize(\"NFKC\")\r\n .replace(ARABIC_DIACRITICS, \"\")\r\n .split(\"\")\r\n .map((char) => PERSIAN_MAP[char] ?? char)\r\n .join(\"\");\r\n}\r\n\r\nfunction generateSlug(\r\n title: string,\r\n transliteratePersian: boolean,\r\n) {\r\n let source = title\r\n .normalize(\"NFKC\")\r\n .replace(ARABIC_DIACRITICS, \"\");\r\n\r\n if (transliteratePersian) {\r\n source = transliterate(source);\r\n }\r\n\r\n return source\r\n .toLowerCase()\r\n .trim()\r\n .replace(/['’`\"]/g, \"\")\r\n .replace(/[^a-z0-9\\s-]/g, \"\")\r\n .replace(/\\s+/g, \"-\")\r\n .replace(/-+/g, \"-\")\r\n .replace(/^-|-$/g, \"\");\r\n}\r\n\r\nexport default function SlugGenerator({\r\n title,\r\n value,\r\n onChange,\r\n label = \"Slug\",\r\n placeholder = \"your-slug\",\r\n buttonText = \"Generate\",\r\n disabled = false,\r\n className = \"\",\r\n icon,\r\n dir = \"ltr\",\r\n generateOnTitleChange = false,\r\n transliteratePersian = true,\r\n}: SlugGeneratorProps) {\r\n const createSlug = () => {\r\n onChange(generateSlug(title, transliteratePersian));\r\n };\r\n\r\n return (\r\n <div className={clsx(\"w-full space-y-1.5\", className)}>\r\n {label && (\r\n <div className=\"flex items-center justify-between gap-3\">\r\n <label className=\"flex items-center gap-2 text-sm font-medium text-(--color-text-primary)\">\r\n {icon}\r\n {label}\r\n </label>\r\n\r\n <button\r\n type=\"button\"\r\n onClick={createSlug}\r\n disabled={disabled || !title.trim()}\r\n className={clsx(\r\n \"inline-flex items-center gap-1.5 rounded-lg px-3 py-2 text-xs font-medium\",\r\n \"text-white transition-all duration-200\",\r\n \"bg-[var(--color-primary)]\",\r\n \"hover:bg-[var(--color-primary-soft)]\",\r\n \"focus:outline-none focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_15%,transparent)]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n >\r\n <WandSparkles className=\"size-4\" />\r\n {buttonText}\r\n </button>\r\n </div>\r\n )}\r\n\r\n <div className=\"relative group\">\r\n <div className=\"pointer-events-none absolute -inset-[1px] rounded-lg opacity-0 transition group-focus-within:opacity-100\" />\r\n\r\n <input\r\n dir={dir}\r\n value={value}\r\n onChange={(event) => onChange(event.target.value)}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n onKeyDown={(event) => {\r\n if (event.key === \"Enter\") {\r\n event.preventDefault();\r\n createSlug();\r\n }\r\n }}\r\n className={clsx(\r\n \"relative w-full rounded-lg border border-(--color-border) bg-(--color-bg-main)\",\r\n \"px-4 py-3 pe-32 text-sm text-(--color-text-primary)\",\r\n \"outline-none transition-all placeholder:text-(--color-text-secondary)/70\",\r\n \"hover:border-[color-mix(in_oklab,var(--color-primary)_22%,var(--color-border))]\",\r\n \"focus:border-[color-mix(in_oklab,var(--color-primary)_45%,var(--color-border))]\",\r\n \"focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_12%,transparent)]\",\r\n \"dark:bg-white/[0.03]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n />\r\n\r\n {!label && (\r\n <button\r\n type=\"button\"\r\n onClick={createSlug}\r\n disabled={disabled || !title.trim()}\r\n className={clsx(\r\n \"absolute inset-y-1.5 end-1.5 inline-flex items-center gap-1.5\",\r\n \"rounded-md px-3 text-xs font-medium text-white\",\r\n \"bg-[var(--color-primary)] transition-all duration-200\",\r\n \"hover:bg-[var(--color-primary-soft)]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n >\r\n <WandSparkles className=\"size-4\" />\r\n {buttonText}\r\n </button>\r\n )}\r\n\r\n {label && (\r\n <button\r\n type=\"button\"\r\n onClick={createSlug}\r\n disabled={disabled || !title.trim()}\r\n className={clsx(\r\n \"absolute inset-y-1.5 end-1.5 inline-flex items-center gap-1.5\",\r\n \"rounded-md px-3 text-xs font-medium text-white\",\r\n \"bg-[var(--color-primary)] transition-all duration-200\",\r\n \"hover:bg-[var(--color-primary-soft)]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n >\r\n <WandSparkles className=\"size-4\" />\r\n {buttonText}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"mappings":"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","names":["clsx"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/SlugGenerator.tsx"],"sourcesContent":["export { default as SlugGenerator } from \"./SlugGenerator\";\r\nexport type { SlugGeneratorProps } from \"./types\";","\"use client\";\r\n\r\nimport { WandSparkles } from \"lucide-react\";\r\nimport clsx from \"clsx\";\r\nimport type { SlugGeneratorProps } from \"./types\";\r\n\r\nconst PERSIAN_MAP: Record<string, string> = {\r\n ا: \"a\",\r\n آ: \"a\",\r\n ب: \"b\",\r\n پ: \"p\",\r\n ت: \"t\",\r\n ث: \"s\",\r\n ج: \"j\",\r\n چ: \"ch\",\r\n ح: \"h\",\r\n خ: \"kh\",\r\n د: \"d\",\r\n ذ: \"z\",\r\n ر: \"r\",\r\n ز: \"z\",\r\n ژ: \"zh\",\r\n س: \"s\",\r\n ش: \"sh\",\r\n ص: \"s\",\r\n ض: \"z\",\r\n ط: \"t\",\r\n ظ: \"z\",\r\n ع: \"a\",\r\n غ: \"gh\",\r\n ف: \"f\",\r\n ق: \"gh\",\r\n ک: \"k\",\r\n گ: \"g\",\r\n ل: \"l\",\r\n م: \"m\",\r\n ن: \"n\",\r\n و: \"v\",\r\n ه: \"h\",\r\n ی: \"y\",\r\n ئ: \"y\",\r\n ء: \"a\",\r\n ۀ: \"h\",\r\n ة: \"h\",\r\n ي: \"y\",\r\n ك: \"k\",\r\n};\r\n\r\nconst ARABIC_DIACRITICS = /[\\u064B-\\u065F\\u0670]/g;\r\n\r\nfunction transliterate(text: string): string {\r\n return text\r\n .normalize(\"NFKC\")\r\n .replace(ARABIC_DIACRITICS, \"\")\r\n .split(\"\")\r\n .map((char) => PERSIAN_MAP[char] ?? char)\r\n .join(\"\");\r\n}\r\n\r\nexport function createSlug(\r\n title: string,\r\n transliteratePersian = true,\r\n): string {\r\n let source = title\r\n .normalize(\"NFKC\")\r\n .replace(ARABIC_DIACRITICS, \"\");\r\n\r\n if (transliteratePersian) {\r\n source = transliterate(source);\r\n }\r\n\r\n return source\r\n .toLowerCase()\r\n .trim()\r\n .replace(/['’`\"]/g, \"\")\r\n .replace(/[^a-z0-9\\s-]/g, \"\")\r\n .replace(/\\s+/g, \"-\")\r\n .replace(/-+/g, \"-\")\r\n .replace(/^-+|-+$/g, \"\");\r\n}\r\n\r\nexport default function SlugGenerator({\r\n title,\r\n value,\r\n onChange,\r\n label = \"Slug\",\r\n placeholder = \"your-slug\",\r\n buttonText = \"Generate\",\r\n disabled = false,\r\n className,\r\n icon,\r\n dir = \"ltr\",\r\n transliteratePersian = true,\r\n}: SlugGeneratorProps) {\r\n const handleGenerate = () => {\r\n if (!title.trim() || disabled) return;\r\n\r\n onChange(createSlug(title, transliteratePersian));\r\n };\r\n\r\n return (\r\n <div className={clsx(\"w-full space-y-1.5\", className)}>\r\n {label && (\r\n <label className=\"flex items-center gap-2 text-sm font-medium text-(--color-text-primary)\">\r\n {icon}\r\n <span>{label}</span>\r\n </label>\r\n )}\r\n\r\n <div className=\"relative\">\r\n <input\r\n dir={dir}\r\n value={value}\r\n onChange={(event) => onChange(event.target.value)}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n onKeyDown={(event) => {\r\n if (event.key === \"Enter\") {\r\n event.preventDefault();\r\n handleGenerate();\r\n }\r\n }}\r\n className={clsx(\r\n \"w-full rounded-lg border border-(--color-border)\",\r\n \"bg-(--color-bg-main)\",\r\n \"py-3 pe-4 ps-32\",\r\n \"text-sm text-(--color-text-primary)\",\r\n \"outline-none transition-all\",\r\n \"placeholder:text-(--color-text-secondary)/70\",\r\n \"hover:border-[color-mix(in_oklab,var(--color-primary)_22%,var(--color-border))]\",\r\n \"focus:border-[color-mix(in_oklab,var(--color-primary)_45%,var(--color-border))]\",\r\n \"focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_12%,transparent)]\",\r\n \"dark:bg-white/[0.03]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n />\r\n\r\n <button\r\n type=\"button\"\r\n onClick={handleGenerate}\r\n disabled={disabled || !title.trim()}\r\n className={clsx(\r\n \"absolute inset-y-1.5 start-1.5\",\r\n \"inline-flex items-center justify-center gap-1.5\",\r\n \"rounded-md px-3\",\r\n \"text-xs font-medium text-white\",\r\n \"bg-(--color-primary)\",\r\n \"transition-all duration-200\",\r\n \"hover:bg-(--color-primary-soft)\",\r\n \"focus:outline-none\",\r\n \"focus:ring-2 focus:ring-(--color-primary)/30\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n >\r\n <WandSparkles className=\"size-4\" />\r\n <span>{buttonText}</span>\r\n </button>\r\n </div>\r\n </div>\r\n );\r\n}"],"mappings":"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","names":["clsx"]}
package/dist/index.mjs CHANGED
@@ -47,12 +47,12 @@ var ARABIC_DIACRITICS = /[\u064B-\u065F\u0670]/g;
47
47
  function transliterate(text) {
48
48
  return text.normalize("NFKC").replace(ARABIC_DIACRITICS, "").split("").map((char) => PERSIAN_MAP[char] ?? char).join("");
49
49
  }
50
- function generateSlug(title, transliteratePersian) {
50
+ function createSlug(title, transliteratePersian = true) {
51
51
  let source = title.normalize("NFKC").replace(ARABIC_DIACRITICS, "");
52
52
  if (transliteratePersian) {
53
53
  source = transliterate(source);
54
54
  }
55
- return source.toLowerCase().trim().replace(/['’`"]/g, "").replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
55
+ return source.toLowerCase().trim().replace(/['’`"]/g, "").replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
56
56
  }
57
57
  function SlugGenerator({
58
58
  title,
@@ -62,44 +62,21 @@ function SlugGenerator({
62
62
  placeholder = "your-slug",
63
63
  buttonText = "Generate",
64
64
  disabled = false,
65
- className = "",
65
+ className,
66
66
  icon,
67
67
  dir = "ltr",
68
- generateOnTitleChange = false,
69
68
  transliteratePersian = true
70
69
  }) {
71
- const createSlug = () => {
72
- onChange(generateSlug(title, transliteratePersian));
70
+ const handleGenerate = () => {
71
+ if (!title.trim() || disabled) return;
72
+ onChange(createSlug(title, transliteratePersian));
73
73
  };
74
74
  return /* @__PURE__ */ jsxs("div", { className: clsx("w-full space-y-1.5", className), children: [
75
- label && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
76
- /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm font-medium text-(--color-text-primary)", children: [
77
- icon,
78
- label
79
- ] }),
80
- /* @__PURE__ */ jsxs(
81
- "button",
82
- {
83
- type: "button",
84
- onClick: createSlug,
85
- disabled: disabled || !title.trim(),
86
- className: clsx(
87
- "inline-flex items-center gap-1.5 rounded-lg px-3 py-2 text-xs font-medium",
88
- "text-white transition-all duration-200",
89
- "bg-[var(--color-primary)]",
90
- "hover:bg-[var(--color-primary-soft)]",
91
- "focus:outline-none focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_15%,transparent)]",
92
- "disabled:cursor-not-allowed disabled:opacity-50"
93
- ),
94
- children: [
95
- /* @__PURE__ */ jsx(WandSparkles, { className: "size-4" }),
96
- buttonText
97
- ]
98
- }
99
- )
75
+ label && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm font-medium text-(--color-text-primary)", children: [
76
+ icon,
77
+ /* @__PURE__ */ jsx("span", { children: label })
100
78
  ] }),
101
- /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
102
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute -inset-[1px] rounded-lg opacity-0 transition group-focus-within:opacity-100" }),
79
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
103
80
  /* @__PURE__ */ jsx(
104
81
  "input",
105
82
  {
@@ -111,13 +88,16 @@ function SlugGenerator({
111
88
  onKeyDown: (event) => {
112
89
  if (event.key === "Enter") {
113
90
  event.preventDefault();
114
- createSlug();
91
+ handleGenerate();
115
92
  }
116
93
  },
117
94
  className: clsx(
118
- "relative w-full rounded-lg border border-(--color-border) bg-(--color-bg-main)",
119
- "px-4 py-3 pe-32 text-sm text-(--color-text-primary)",
120
- "outline-none transition-all placeholder:text-(--color-text-secondary)/70",
95
+ "w-full rounded-lg border border-(--color-border)",
96
+ "bg-(--color-bg-main)",
97
+ "py-3 pe-4 ps-32",
98
+ "text-sm text-(--color-text-primary)",
99
+ "outline-none transition-all",
100
+ "placeholder:text-(--color-text-secondary)/70",
121
101
  "hover:border-[color-mix(in_oklab,var(--color-primary)_22%,var(--color-border))]",
122
102
  "focus:border-[color-mix(in_oklab,var(--color-primary)_45%,var(--color-border))]",
123
103
  "focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_12%,transparent)]",
@@ -126,41 +106,27 @@ function SlugGenerator({
126
106
  )
127
107
  }
128
108
  ),
129
- !label && /* @__PURE__ */ jsxs(
130
- "button",
131
- {
132
- type: "button",
133
- onClick: createSlug,
134
- disabled: disabled || !title.trim(),
135
- className: clsx(
136
- "absolute inset-y-1.5 end-1.5 inline-flex items-center gap-1.5",
137
- "rounded-md px-3 text-xs font-medium text-white",
138
- "bg-[var(--color-primary)] transition-all duration-200",
139
- "hover:bg-[var(--color-primary-soft)]",
140
- "disabled:cursor-not-allowed disabled:opacity-50"
141
- ),
142
- children: [
143
- /* @__PURE__ */ jsx(WandSparkles, { className: "size-4" }),
144
- buttonText
145
- ]
146
- }
147
- ),
148
- label && /* @__PURE__ */ jsxs(
109
+ /* @__PURE__ */ jsxs(
149
110
  "button",
150
111
  {
151
112
  type: "button",
152
- onClick: createSlug,
113
+ onClick: handleGenerate,
153
114
  disabled: disabled || !title.trim(),
154
115
  className: clsx(
155
- "absolute inset-y-1.5 end-1.5 inline-flex items-center gap-1.5",
156
- "rounded-md px-3 text-xs font-medium text-white",
157
- "bg-[var(--color-primary)] transition-all duration-200",
158
- "hover:bg-[var(--color-primary-soft)]",
116
+ "absolute inset-y-1.5 start-1.5",
117
+ "inline-flex items-center justify-center gap-1.5",
118
+ "rounded-md px-3",
119
+ "text-xs font-medium text-white",
120
+ "bg-(--color-primary)",
121
+ "transition-all duration-200",
122
+ "hover:bg-(--color-primary-soft)",
123
+ "focus:outline-none",
124
+ "focus:ring-2 focus:ring-(--color-primary)/30",
159
125
  "disabled:cursor-not-allowed disabled:opacity-50"
160
126
  ),
161
127
  children: [
162
128
  /* @__PURE__ */ jsx(WandSparkles, { className: "size-4" }),
163
- buttonText
129
+ /* @__PURE__ */ jsx("span", { children: buttonText })
164
130
  ]
165
131
  }
166
132
  )
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/SlugGenerator.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport { WandSparkles } from \"lucide-react\";\r\nimport clsx from \"clsx\";\r\nimport type { SlugGeneratorProps } from \"./types\";\r\n\r\nconst PERSIAN_MAP: Record<string, string> = {\r\n ا: \"a\",\r\n آ: \"a\",\r\n ب: \"b\",\r\n پ: \"p\",\r\n ت: \"t\",\r\n ث: \"s\",\r\n ج: \"j\",\r\n چ: \"ch\",\r\n ح: \"h\",\r\n خ: \"kh\",\r\n د: \"d\",\r\n ذ: \"z\",\r\n ر: \"r\",\r\n ز: \"z\",\r\n ژ: \"zh\",\r\n س: \"s\",\r\n ش: \"sh\",\r\n ص: \"s\",\r\n ض: \"z\",\r\n ط: \"t\",\r\n ظ: \"z\",\r\n ع: \"a\",\r\n غ: \"gh\",\r\n ف: \"f\",\r\n ق: \"gh\",\r\n ک: \"k\",\r\n گ: \"g\",\r\n ل: \"l\",\r\n م: \"m\",\r\n ن: \"n\",\r\n و: \"v\",\r\n ه: \"h\",\r\n ی: \"y\",\r\n ئ: \"y\",\r\n ء: \"a\",\r\n ۀ: \"h\",\r\n ة: \"h\",\r\n ي: \"y\",\r\n ك: \"k\",\r\n};\r\n\r\nconst ARABIC_DIACRITICS = /[\\u064B-\\u065F\\u0670]/g;\r\n\r\nfunction transliterate(text: string) {\r\n return text\r\n .normalize(\"NFKC\")\r\n .replace(ARABIC_DIACRITICS, \"\")\r\n .split(\"\")\r\n .map((char) => PERSIAN_MAP[char] ?? char)\r\n .join(\"\");\r\n}\r\n\r\nfunction generateSlug(\r\n title: string,\r\n transliteratePersian: boolean,\r\n) {\r\n let source = title\r\n .normalize(\"NFKC\")\r\n .replace(ARABIC_DIACRITICS, \"\");\r\n\r\n if (transliteratePersian) {\r\n source = transliterate(source);\r\n }\r\n\r\n return source\r\n .toLowerCase()\r\n .trim()\r\n .replace(/['’`\"]/g, \"\")\r\n .replace(/[^a-z0-9\\s-]/g, \"\")\r\n .replace(/\\s+/g, \"-\")\r\n .replace(/-+/g, \"-\")\r\n .replace(/^-|-$/g, \"\");\r\n}\r\n\r\nexport default function SlugGenerator({\r\n title,\r\n value,\r\n onChange,\r\n label = \"Slug\",\r\n placeholder = \"your-slug\",\r\n buttonText = \"Generate\",\r\n disabled = false,\r\n className = \"\",\r\n icon,\r\n dir = \"ltr\",\r\n generateOnTitleChange = false,\r\n transliteratePersian = true,\r\n}: SlugGeneratorProps) {\r\n const createSlug = () => {\r\n onChange(generateSlug(title, transliteratePersian));\r\n };\r\n\r\n return (\r\n <div className={clsx(\"w-full space-y-1.5\", className)}>\r\n {label && (\r\n <div className=\"flex items-center justify-between gap-3\">\r\n <label className=\"flex items-center gap-2 text-sm font-medium text-(--color-text-primary)\">\r\n {icon}\r\n {label}\r\n </label>\r\n\r\n <button\r\n type=\"button\"\r\n onClick={createSlug}\r\n disabled={disabled || !title.trim()}\r\n className={clsx(\r\n \"inline-flex items-center gap-1.5 rounded-lg px-3 py-2 text-xs font-medium\",\r\n \"text-white transition-all duration-200\",\r\n \"bg-[var(--color-primary)]\",\r\n \"hover:bg-[var(--color-primary-soft)]\",\r\n \"focus:outline-none focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_15%,transparent)]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n >\r\n <WandSparkles className=\"size-4\" />\r\n {buttonText}\r\n </button>\r\n </div>\r\n )}\r\n\r\n <div className=\"relative group\">\r\n <div className=\"pointer-events-none absolute -inset-[1px] rounded-lg opacity-0 transition group-focus-within:opacity-100\" />\r\n\r\n <input\r\n dir={dir}\r\n value={value}\r\n onChange={(event) => onChange(event.target.value)}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n onKeyDown={(event) => {\r\n if (event.key === \"Enter\") {\r\n event.preventDefault();\r\n createSlug();\r\n }\r\n }}\r\n className={clsx(\r\n \"relative w-full rounded-lg border border-(--color-border) bg-(--color-bg-main)\",\r\n \"px-4 py-3 pe-32 text-sm text-(--color-text-primary)\",\r\n \"outline-none transition-all placeholder:text-(--color-text-secondary)/70\",\r\n \"hover:border-[color-mix(in_oklab,var(--color-primary)_22%,var(--color-border))]\",\r\n \"focus:border-[color-mix(in_oklab,var(--color-primary)_45%,var(--color-border))]\",\r\n \"focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_12%,transparent)]\",\r\n \"dark:bg-white/[0.03]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n />\r\n\r\n {!label && (\r\n <button\r\n type=\"button\"\r\n onClick={createSlug}\r\n disabled={disabled || !title.trim()}\r\n className={clsx(\r\n \"absolute inset-y-1.5 end-1.5 inline-flex items-center gap-1.5\",\r\n \"rounded-md px-3 text-xs font-medium text-white\",\r\n \"bg-[var(--color-primary)] transition-all duration-200\",\r\n \"hover:bg-[var(--color-primary-soft)]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n >\r\n <WandSparkles className=\"size-4\" />\r\n {buttonText}\r\n </button>\r\n )}\r\n\r\n {label && (\r\n <button\r\n type=\"button\"\r\n onClick={createSlug}\r\n disabled={disabled || !title.trim()}\r\n className={clsx(\r\n \"absolute inset-y-1.5 end-1.5 inline-flex items-center gap-1.5\",\r\n \"rounded-md px-3 text-xs font-medium text-white\",\r\n \"bg-[var(--color-primary)] transition-all duration-200\",\r\n \"hover:bg-[var(--color-primary-soft)]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n >\r\n <WandSparkles className=\"size-4\" />\r\n {buttonText}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/SlugGenerator.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport { WandSparkles } from \"lucide-react\";\r\nimport clsx from \"clsx\";\r\nimport type { SlugGeneratorProps } from \"./types\";\r\n\r\nconst PERSIAN_MAP: Record<string, string> = {\r\n ا: \"a\",\r\n آ: \"a\",\r\n ب: \"b\",\r\n پ: \"p\",\r\n ت: \"t\",\r\n ث: \"s\",\r\n ج: \"j\",\r\n چ: \"ch\",\r\n ح: \"h\",\r\n خ: \"kh\",\r\n د: \"d\",\r\n ذ: \"z\",\r\n ر: \"r\",\r\n ز: \"z\",\r\n ژ: \"zh\",\r\n س: \"s\",\r\n ش: \"sh\",\r\n ص: \"s\",\r\n ض: \"z\",\r\n ط: \"t\",\r\n ظ: \"z\",\r\n ع: \"a\",\r\n غ: \"gh\",\r\n ف: \"f\",\r\n ق: \"gh\",\r\n ک: \"k\",\r\n گ: \"g\",\r\n ل: \"l\",\r\n م: \"m\",\r\n ن: \"n\",\r\n و: \"v\",\r\n ه: \"h\",\r\n ی: \"y\",\r\n ئ: \"y\",\r\n ء: \"a\",\r\n ۀ: \"h\",\r\n ة: \"h\",\r\n ي: \"y\",\r\n ك: \"k\",\r\n};\r\n\r\nconst ARABIC_DIACRITICS = /[\\u064B-\\u065F\\u0670]/g;\r\n\r\nfunction transliterate(text: string): string {\r\n return text\r\n .normalize(\"NFKC\")\r\n .replace(ARABIC_DIACRITICS, \"\")\r\n .split(\"\")\r\n .map((char) => PERSIAN_MAP[char] ?? char)\r\n .join(\"\");\r\n}\r\n\r\nexport function createSlug(\r\n title: string,\r\n transliteratePersian = true,\r\n): string {\r\n let source = title\r\n .normalize(\"NFKC\")\r\n .replace(ARABIC_DIACRITICS, \"\");\r\n\r\n if (transliteratePersian) {\r\n source = transliterate(source);\r\n }\r\n\r\n return source\r\n .toLowerCase()\r\n .trim()\r\n .replace(/['’`\"]/g, \"\")\r\n .replace(/[^a-z0-9\\s-]/g, \"\")\r\n .replace(/\\s+/g, \"-\")\r\n .replace(/-+/g, \"-\")\r\n .replace(/^-+|-+$/g, \"\");\r\n}\r\n\r\nexport default function SlugGenerator({\r\n title,\r\n value,\r\n onChange,\r\n label = \"Slug\",\r\n placeholder = \"your-slug\",\r\n buttonText = \"Generate\",\r\n disabled = false,\r\n className,\r\n icon,\r\n dir = \"ltr\",\r\n transliteratePersian = true,\r\n}: SlugGeneratorProps) {\r\n const handleGenerate = () => {\r\n if (!title.trim() || disabled) return;\r\n\r\n onChange(createSlug(title, transliteratePersian));\r\n };\r\n\r\n return (\r\n <div className={clsx(\"w-full space-y-1.5\", className)}>\r\n {label && (\r\n <label className=\"flex items-center gap-2 text-sm font-medium text-(--color-text-primary)\">\r\n {icon}\r\n <span>{label}</span>\r\n </label>\r\n )}\r\n\r\n <div className=\"relative\">\r\n <input\r\n dir={dir}\r\n value={value}\r\n onChange={(event) => onChange(event.target.value)}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n onKeyDown={(event) => {\r\n if (event.key === \"Enter\") {\r\n event.preventDefault();\r\n handleGenerate();\r\n }\r\n }}\r\n className={clsx(\r\n \"w-full rounded-lg border border-(--color-border)\",\r\n \"bg-(--color-bg-main)\",\r\n \"py-3 pe-4 ps-32\",\r\n \"text-sm text-(--color-text-primary)\",\r\n \"outline-none transition-all\",\r\n \"placeholder:text-(--color-text-secondary)/70\",\r\n \"hover:border-[color-mix(in_oklab,var(--color-primary)_22%,var(--color-border))]\",\r\n \"focus:border-[color-mix(in_oklab,var(--color-primary)_45%,var(--color-border))]\",\r\n \"focus:ring-4 focus:ring-[color-mix(in_oklab,var(--color-primary)_12%,transparent)]\",\r\n \"dark:bg-white/[0.03]\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n />\r\n\r\n <button\r\n type=\"button\"\r\n onClick={handleGenerate}\r\n disabled={disabled || !title.trim()}\r\n className={clsx(\r\n \"absolute inset-y-1.5 start-1.5\",\r\n \"inline-flex items-center justify-center gap-1.5\",\r\n \"rounded-md px-3\",\r\n \"text-xs font-medium text-white\",\r\n \"bg-(--color-primary)\",\r\n \"transition-all duration-200\",\r\n \"hover:bg-(--color-primary-soft)\",\r\n \"focus:outline-none\",\r\n \"focus:ring-2 focus:ring-(--color-primary)/30\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n )}\r\n >\r\n <WandSparkles className=\"size-4\" />\r\n <span>{buttonText}</span>\r\n </button>\r\n </div>\r\n </div>\r\n );\r\n}"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codav/slug-generator",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "A lightweight, reusable TypeScript-first React slug generator component",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",