gov-layout 1.3.0 → 1.3.4

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/README.md CHANGED
@@ -222,7 +222,7 @@ const menuItems: MenuItem[] = [
222
222
  - ✅ Default ตั้งค่าระบบ + ช่วยเหลือ (override ได้)
223
223
  - ✅ โปรไฟล์ + ออกจากระบบ ล่างสุดเสมอ
224
224
  - ✅ `dividerAfter` เส้นคั่นระหว่างกลุ่ม
225
- - ✅ แสดง Avatar Icon อัตโนมัติกรณีผู้ใช้ไม่มีรูปโปรไฟล์ (v1.2.28+) 🆕
225
+ - ✅ ใช้ Standard Avatar Placeholder กรณีที่ไม่มีรูปโปรไฟล์หรือโหลดรูปไม่สำเร็จ (v1.3.2+) 🆕
226
226
 
227
227
  ---
228
228
 
package/dist/index.js CHANGED
@@ -625,15 +625,22 @@ function ProfileIcon() {
625
625
  }
626
626
  );
627
627
  }
628
- function AvatarIcon({ size = 20 }) {
628
+ function AvatarPlaceholder({ size = 40 }) {
629
629
  return /* @__PURE__ */ jsxRuntime.jsx(
630
- "svg",
630
+ "div",
631
631
  {
632
- width: size,
633
- height: size,
634
- viewBox: "0 0 24 24",
635
- fill: "currentColor",
636
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" })
632
+ style: {
633
+ width: `${size}px`,
634
+ height: `${size}px`,
635
+ borderRadius: "50%",
636
+ backgroundColor: "#f1f5f9",
637
+ display: "flex",
638
+ alignItems: "center",
639
+ justifyContent: "center",
640
+ flexShrink: 0,
641
+ boxShadow: "0 2px 8px rgba(0,0,0,0.05)"
642
+ },
643
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size * 0.6, height: size * 0.6, viewBox: "0 0 24 24", fill: "#94a3b8", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" }) })
637
644
  }
638
645
  );
639
646
  }
@@ -645,6 +652,10 @@ function SidebarUserProfile({
645
652
  onProfile
646
653
  }) {
647
654
  const isDark = useDarkMode();
655
+ const [imgError, setImgError] = react.useState(false);
656
+ react.useEffect(() => {
657
+ setImgError(false);
658
+ }, [user?.pictureUrl]);
648
659
  if (!user) return null;
649
660
  const getFullName = () => {
650
661
  if (user.firstName && user.lastName) {
@@ -667,36 +678,21 @@ function SidebarUserProfile({
667
678
  onClick: onProfile,
668
679
  title: onProfile ? "\u0E14\u0E39\u0E42\u0E1B\u0E23\u0E44\u0E1F\u0E25\u0E4C" : getFullName(),
669
680
  style: { cursor: onProfile ? "pointer" : "default" },
670
- children: user.pictureUrl ? /* @__PURE__ */ jsxRuntime.jsx(
681
+ children: user.pictureUrl && !imgError ? /* @__PURE__ */ jsxRuntime.jsx(
671
682
  "img",
672
683
  {
673
684
  src: user.pictureUrl,
674
685
  alt: getFullName(),
686
+ onError: () => setImgError(true),
675
687
  style: {
676
688
  width: "36px",
677
689
  height: "36px",
678
690
  borderRadius: "50%",
679
- objectFit: "cover"
691
+ objectFit: "cover",
692
+ flexShrink: 0
680
693
  }
681
694
  }
682
- ) : /* @__PURE__ */ jsxRuntime.jsx(
683
- "div",
684
- {
685
- style: {
686
- width: "36px",
687
- height: "36px",
688
- borderRadius: "50%",
689
- background: "var(--color-alias-color-brand-primary, #1e7d55)",
690
- display: "flex",
691
- alignItems: "center",
692
- justifyContent: "center",
693
- color: "#fff",
694
- fontWeight: 700,
695
- fontSize: "14px"
696
- },
697
- children: /* @__PURE__ */ jsxRuntime.jsx(AvatarIcon, { size: 20 })
698
- }
699
- )
695
+ ) : /* @__PURE__ */ jsxRuntime.jsx(AvatarPlaceholder, { size: 36 })
700
696
  }
701
697
  ),
702
698
  onProfile && /* @__PURE__ */ jsxRuntime.jsx(
@@ -786,11 +782,12 @@ function SidebarUserProfile({
786
782
  transition: "opacity 0.15s ease"
787
783
  },
788
784
  children: [
789
- user.pictureUrl ? /* @__PURE__ */ jsxRuntime.jsx(
785
+ user.pictureUrl && !imgError ? /* @__PURE__ */ jsxRuntime.jsx(
790
786
  "img",
791
787
  {
792
788
  src: user.pictureUrl,
793
789
  alt: getFullName(),
790
+ onError: () => setImgError(true),
794
791
  style: {
795
792
  width: "40px",
796
793
  height: "40px",
@@ -799,25 +796,7 @@ function SidebarUserProfile({
799
796
  flexShrink: 0
800
797
  }
801
798
  }
802
- ) : /* @__PURE__ */ jsxRuntime.jsx(
803
- "div",
804
- {
805
- style: {
806
- width: "40px",
807
- height: "40px",
808
- borderRadius: "50%",
809
- background: "var(--color-alias-color-brand-primary, #1e7d55)",
810
- display: "flex",
811
- alignItems: "center",
812
- justifyContent: "center",
813
- color: "#fff",
814
- fontWeight: 700,
815
- fontSize: "16px",
816
- flexShrink: 0
817
- },
818
- children: /* @__PURE__ */ jsxRuntime.jsx(AvatarIcon, { size: 24 })
819
- }
820
- ),
799
+ ) : /* @__PURE__ */ jsxRuntime.jsx(AvatarPlaceholder, { size: 40 }),
821
800
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
822
801
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: {
823
802
  fontWeight: 600,
@@ -1109,8 +1088,24 @@ function HamburgerIcon() {
1109
1088
  function MessageOutlinedIcon(props) {
1110
1089
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { focusable: "false", "aria-hidden": "true", viewBox: "0 0 24 24", fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 4h16v12H5.17L4 17.17zm0-2c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm2 10h12v2H6zm0-3h12v2H6zm0-3h12v2H6z" }) });
1111
1090
  }
1112
- function AvatarIcon2({ size = 20 }) {
1113
- return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" }) });
1091
+ function AvatarPlaceholder2({ size = 44 }) {
1092
+ return /* @__PURE__ */ jsxRuntime.jsx(
1093
+ "div",
1094
+ {
1095
+ style: {
1096
+ width: `${size}px`,
1097
+ height: `${size}px`,
1098
+ borderRadius: "50%",
1099
+ backgroundColor: "#f1f5f9",
1100
+ display: "flex",
1101
+ alignItems: "center",
1102
+ justifyContent: "center",
1103
+ flexShrink: 0,
1104
+ boxShadow: "0 2px 8px rgba(0,0,0,0.05)"
1105
+ },
1106
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size * 0.6, height: size * 0.6, viewBox: "0 0 24 24", fill: "#94a3b8", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" }) })
1107
+ }
1108
+ );
1114
1109
  }
1115
1110
  function getNotifBg(type) {
1116
1111
  const map = {
@@ -1143,11 +1138,14 @@ function UserHeader({
1143
1138
  className
1144
1139
  }) {
1145
1140
  const displayName = `${user?.firstName || ""} ${user?.lastName || ""}`.trim() || "\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49";
1146
- user?.firstName?.charAt(0) || "\u0E1C";
1147
1141
  const [isNotifOpen, setIsNotifOpen] = react.useState(false);
1148
1142
  const [activeFilter, setActiveFilter] = react.useState("all");
1149
1143
  const notifRef = react.useRef(null);
1150
1144
  const isDark = useDarkMode();
1145
+ const [imgError, setImgError] = react.useState(false);
1146
+ react.useEffect(() => {
1147
+ setImgError(false);
1148
+ }, [user?.pictureUrl]);
1151
1149
  react.useEffect(() => {
1152
1150
  const handleClickOutside = (e) => {
1153
1151
  if (notifRef.current && !notifRef.current.contains(e.target)) {
@@ -1210,36 +1208,22 @@ function UserHeader({
1210
1208
  overflow: "hidden"
1211
1209
  },
1212
1210
  children: [
1213
- user?.pictureUrl ? /* @__PURE__ */ jsxRuntime.jsx(
1211
+ user?.pictureUrl && !imgError ? /* @__PURE__ */ jsxRuntime.jsx(
1214
1212
  "img",
1215
1213
  {
1216
1214
  src: user.pictureUrl,
1217
1215
  alt: displayName,
1216
+ onError: () => setImgError(true),
1218
1217
  style: {
1219
1218
  width: "44px",
1220
1219
  height: "44px",
1221
1220
  borderRadius: "50%",
1222
1221
  objectFit: "cover",
1223
- border: "2px solid rgba(255,255,255,0.7)"
1224
- }
1225
- }
1226
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1227
- "div",
1228
- {
1229
- style: {
1230
- width: "44px",
1231
- height: "44px",
1232
- borderRadius: "50%",
1233
- background: "rgba(255,255,255,0.2)",
1234
1222
  border: "2px solid rgba(255,255,255,0.7)",
1235
- display: "flex",
1236
- alignItems: "center",
1237
- justifyContent: "center",
1238
- boxShadow: "0 1px 3px rgba(0,0,0,0.1)"
1239
- },
1240
- children: /* @__PURE__ */ jsxRuntime.jsx(AvatarIcon2, { size: 24 })
1223
+ flexShrink: 0
1224
+ }
1241
1225
  }
1242
- ),
1226
+ ) : /* @__PURE__ */ jsxRuntime.jsx(AvatarPlaceholder2, { size: 44 }),
1243
1227
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
1244
1228
  /* @__PURE__ */ jsxRuntime.jsxs(
1245
1229
  "p",
@@ -1631,8 +1615,24 @@ function ProfileIcon2() {
1631
1615
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "7", r: "4" })
1632
1616
  ] });
1633
1617
  }
1634
- function AvatarIcon3({ size = 20 }) {
1635
- return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" }) });
1618
+ function AvatarPlaceholder3({ size = 56 }) {
1619
+ return /* @__PURE__ */ jsxRuntime.jsx(
1620
+ "div",
1621
+ {
1622
+ style: {
1623
+ width: `${size}px`,
1624
+ height: `${size}px`,
1625
+ borderRadius: "50%",
1626
+ backgroundColor: "#f1f5f9",
1627
+ display: "flex",
1628
+ alignItems: "center",
1629
+ justifyContent: "center",
1630
+ flexShrink: 0,
1631
+ boxShadow: "0 2px 8px rgba(0,0,0,0.05)"
1632
+ },
1633
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size * 0.6, height: size * 0.6, viewBox: "0 0 24 24", fill: "#94a3b8", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" }) })
1634
+ }
1635
+ );
1636
1636
  }
1637
1637
  function UserSidebar({
1638
1638
  user,
@@ -1646,6 +1646,10 @@ function UserSidebar({
1646
1646
  onProfile
1647
1647
  }) {
1648
1648
  const isDark = useDarkMode();
1649
+ const [imgError, setImgError] = react.useState(false);
1650
+ react.useEffect(() => {
1651
+ setImgError(false);
1652
+ }, [user?.pictureUrl]);
1649
1653
  react.useEffect(() => {
1650
1654
  if (isOpen) {
1651
1655
  document.body.style.overflow = "hidden";
@@ -1720,38 +1724,22 @@ function UserSidebar({
1720
1724
  padding: "8px"
1721
1725
  },
1722
1726
  children: [
1723
- user.pictureUrl ? /* @__PURE__ */ jsxRuntime.jsx(
1727
+ user.pictureUrl && !imgError ? /* @__PURE__ */ jsxRuntime.jsx(
1724
1728
  "img",
1725
1729
  {
1726
1730
  src: user.pictureUrl,
1727
1731
  alt: getFullName(),
1732
+ onError: () => setImgError(true),
1728
1733
  style: {
1729
1734
  width: "56px",
1730
1735
  height: "56px",
1731
1736
  borderRadius: "50%",
1732
1737
  objectFit: "cover",
1738
+ flexShrink: 0,
1733
1739
  boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
1734
1740
  }
1735
1741
  }
1736
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1737
- "div",
1738
- {
1739
- style: {
1740
- width: "56px",
1741
- height: "56px",
1742
- borderRadius: "50%",
1743
- background: "linear-gradient(135deg, var(--color-alias-color-brand-secondary, #80d897), var(--color-alias-color-brand-primary, #1e7d55))",
1744
- display: "flex",
1745
- alignItems: "center",
1746
- justifyContent: "center",
1747
- color: "#fff",
1748
- fontWeight: 700,
1749
- fontSize: "20px",
1750
- boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
1751
- },
1752
- children: /* @__PURE__ */ jsxRuntime.jsx(AvatarIcon3, { size: 32 })
1753
- }
1754
- ),
1742
+ ) : /* @__PURE__ */ jsxRuntime.jsx(AvatarPlaceholder3, { size: 56 }),
1755
1743
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
1756
1744
  /* @__PURE__ */ jsxRuntime.jsx(
1757
1745
  "p",